From acab9a333fb61b05fced8fb61b6d39bdab2eb57a Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 12 Feb 2018 18:14:13 +0100 Subject: [PATCH 01/19] Block builder (substrate) --- substrate/client/src/block_builder.rs | 89 +++++++++++++++++++++++++++ substrate/client/src/lib.rs | 17 ++++- substrate/primitives/src/block.rs | 11 ++-- substrate/primitives/src/lib.rs | 4 +- 4 files changed, 111 insertions(+), 10 deletions(-) create mode 100644 substrate/client/src/block_builder.rs diff --git a/substrate/client/src/block_builder.rs b/substrate/client/src/block_builder.rs new file mode 100644 index 0000000000000..ebd8ec6c47fe3 --- /dev/null +++ b/substrate/client/src/block_builder.rs @@ -0,0 +1,89 @@ +// 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 . + +//! Utility struct to build a block. + +use std::vec::Vec; +use codec::{Joiner, Slicable}; +use state_machine::{self, CodeExecutor}; +use primitives::{Header, Block}; +use primitives::block::Transaction; +use {backend, error, BlockId, BlockStatus, Client}; +use triehash::ordered_trie_root; + +/// Utility for building new (valid) blocks from a stream of transactions. +pub struct BlockBuilder where + B: backend::Backend, + E: CodeExecutor + Clone, + error::Error: From<<::State as state_machine::backend::Backend>::Error>, +{ + header: Header, + transactions: Vec, + executor: E, + state: B::State, + changes: state_machine::OverlayedChanges, +} + +impl BlockBuilder where + B: backend::Backend, + E: CodeExecutor + Clone, + error::Error: From<<::State as state_machine::backend::Backend>::Error>, +{ + /// Create a new instance of builder from the given client. + pub fn new(client: &Client) -> error::Result { + let best = (client.info().map(|i| i.chain.best_number)?..1) + .find(|&n| if let Ok(BlockStatus::InChain) = client.block_status(&BlockId::Number(n)) + { true } else { false }) + .unwrap_or(0); + + Ok(BlockBuilder { + header: Header { + number: best + 1, + parent_hash: client.block_hash(best)?.expect("We already ascertained this is InChain before; qed"), + state_root: Default::default(), + transaction_root: Default::default(), + digest: Default::default(), + }, + transactions: Default::default(), + executor: client.clone_executor(), + state: client.state_at(&BlockId::Number(best))?, + changes: Default::default(), + }) + } + + /// Push a transaction onto the block's list of transactions. This will ensure the transaction + /// can be validly executed (by executing it); if it is invalid, it'll be returned along with + /// the error. Otherwise, it will return a mutable reference to self (in order to chain). + pub fn push(&mut self, tx: Transaction) -> error::Result<()> { + let output = state_machine::execute(&self.state, &mut self.changes, &self.executor, "execute_transaction", + &vec![].and(&self.header).and(&tx))?; + self.header = Header::decode(&mut &output[..]).expect("Header came straight out of runtime do must be valid"); + self.transactions.push(tx); + Ok(()) + } + + /// Consume the builder to return a valid `Block` containing all pushed transactions. + pub fn bake(mut self) -> error::Result { + self.header.transaction_root = ordered_trie_root(self.transactions.iter().map(Slicable::encode)).0.into(); + let output = state_machine::execute(&self.state, &mut self.changes, &self.executor, "finalise_block", + &self.header.encode())?; + self.header = Header::decode(&mut &output[..]).expect("Header came straight out of runtime do must be valid"); + Ok(Block { + header: self.header, + transactions: self.transactions, + }) + } +} diff --git a/substrate/client/src/lib.rs b/substrate/client/src/lib.rs index 530288ca29edb..1a7d4ceb622f2 100644 --- a/substrate/client/src/lib.rs +++ b/substrate/client/src/lib.rs @@ -39,9 +39,11 @@ pub mod blockchain; pub mod backend; pub mod in_mem; pub mod genesis; +pub mod block_builder; pub use blockchain::Info as ChainInfo; pub use blockchain::BlockId; +pub use block_builder::BlockBuilder; use primitives::{block, AuthorityId}; use primitives::storage::{StorageKey, StorageData}; @@ -59,6 +61,7 @@ pub struct Client where B: backend::Backend { } /// Client info +// TODO: split queue info from chain info and amalgamate into single struct. #[derive(Debug)] pub struct ClientInfo { /// Best block hash. @@ -167,6 +170,11 @@ impl Client where 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)?; @@ -183,10 +191,8 @@ impl Client where /// 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 state = self.state_at(id)?; - let return_data = state_machine::execute( - &state, + &self.state_at(id)?, &mut changes, &self.executor, method, @@ -195,6 +201,11 @@ impl Client where Ok(CallResult { return_data, changes }) } + /// Create a new block, built on the head of the chain. + pub fn new_block(&self) -> error::Result> where E: Clone { + BlockBuilder::new(self) + } + /// Queue a block for import. pub fn import_block(&self, header: block::Header, body: Option) -> error::Result { // TODO: import lock diff --git a/substrate/primitives/src/block.rs b/substrate/primitives/src/block.rs index ba34dd085f08a..90882f7fc3f71 100644 --- a/substrate/primitives/src/block.rs +++ b/substrate/primitives/src/block.rs @@ -17,19 +17,18 @@ //! Block and header type definitions. #[cfg(feature = "std")] -use bytes; use rstd::vec::Vec; +use {bytes, Hash}; use codec::{Input, Slicable}; -use hash::H256; /// Used to refer to a block number. pub type Number = u64; /// Hash used to refer to a block hash. -pub type HeaderHash = H256; +pub type HeaderHash = Hash; /// Hash used to refer to a transaction hash. -pub type TransactionHash = H256; +pub type TransactionHash = Hash; /// Simple generic transaction type. #[derive(PartialEq, Eq, Clone)] @@ -127,9 +126,9 @@ pub struct Header { /// Block number. pub number: Number, /// State root after this transition. - pub state_root: H256, + pub state_root: Hash, /// The root of the trie that represents this block's transactions, indexed by a 32-byte integer. - pub transaction_root: H256, + pub transaction_root: Hash, /// The digest of activity on the block. pub digest: Digest, } diff --git a/substrate/primitives/src/lib.rs b/substrate/primitives/src/lib.rs index 1c282fe42e92e..211bfce8f5a7f 100644 --- a/substrate/primitives/src/lib.rs +++ b/substrate/primitives/src/lib.rs @@ -90,8 +90,10 @@ mod tests; pub use self::hash::{H160, H256}; pub use self::uint::{U256, U512}; - pub use block::{Block, Header}; +/// General hash type. +pub type Hash = H256; + /// An identifier for an authority in the consensus algorithm. The same as ed25519::Public. pub type AuthorityId = [u8; 32]; From 9acd3f99abd7f592e44a738b645d7fc67ca9cb16 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 12 Feb 2018 23:39:13 +0100 Subject: [PATCH 02/19] Fix wasm build --- .../release/polkadot_runtime.compact.wasm | Bin 75751 -> 75751 bytes .../release/polkadot_runtime.wasm | Bin 75800 -> 75800 bytes substrate/primitives/src/block.rs | 5 +++-- .../substrate_test_runtime.compact.wasm | Bin 31904 -> 31904 bytes .../release/substrate_test_runtime.wasm | Bin 31984 -> 31984 bytes 5 files changed, 3 insertions(+), 2 deletions(-) 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 b4b77e5ec9e4c2a74a57d38ded342bd8de036607..84e52dbc50cfa544c0a8899aa70d307a4b0562d5 100644 GIT binary patch delta 120 zcmaEUp5^&@mJQNOOdMRBWtr}KFa~Y@>m|ZZ1uiXJic6JT+qz z4`c9VkJ9;!jQ*2fl;r`%!pmb>8T~hJt?^+3N-@>XRoPtq(2H;L{KZds7y~!AZcq>a VQp*m^<^W1NowStLeDuAN1pwW1EGqy2 delta 120 zcmaEUp5^&@mJQNOOk5nBWtr}KFa~b^>m|ZZ1uiXJqu>JT+qz z4`a|~kJ9;!jKPy%l;r`%!pmb>8G|=(t?^+3N-@>XRoVP<<5M2SfX(OkDF^_m59elc V0A+fvTS{!6zu1d!^U?Q876A6IEpz|? 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 2b5124161a1f76f50f483c1c8bf74aa9942dd88a..a92614dcc2d6fc281b868d3754d5c972ece62333 100644 GIT binary patch delta 115 zcmbPnfn~-8mJKpYOdMRB<(T+A8G|?LdCM~aDL-E)DaL@!6Vpu?83Q*T%h<%j7_zyt zbUq_vz+}ntJfK)dc`PeX#j_e8CZLpY{alsJYaV*>ZN9MhDGy`N=4~4k1c21_1G71R P(#0n&B{qM1uVeuLwbv@f delta 114 zcmbPnfn~-8mJKpYOk5nB<(T+A8G|6^V%=|nCUB0x; zyu_T$;?(%0oc!c$2EEPtOsiBFjV9kxdjTX5tG@w~8#FS3q?BenqxR$~%^g6}SWAad zdvk(T594N4ul4+se?)(q{6^b}QGc_(&PGN?t;r8{BNz=fTj}Yt0d;1W*aOKGChvjd z3{#M~jb`~k@}rpvkW@3bWHj2GV4lj&s6Y9bQz4_)W(#LKW}sQ+ZtIvgzl(Xp&1ks! fcE)8U#vPOUqYO8*MSWu2ye`{OfYEjGhw2Rg!;Vyy delta 236 zcmZ4RlX1aM#toYoIk~yH*;!ecxY;M~X7tg}5>Bm1O)f1-jV~!m%qvbzF3HT#W6-N|is5sZ49@7w9J0X6Wt+5<^n*Y`lu)(vEwntMKw zoaSxTlkjcA07Ox@<=QMz_fysy6@tBrZ|L 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 ec5638e1c183ce98875d9e6fe4db58fd09e01f70..3329885da9a60eb0dbabb75aadba95405ccefe2a 100644 GIT binary patch delta 241 zcmezHlkvk(#tp|9IoY{^fSr|6^V%=|nC1HQD( zyu_T$;?(%0oc!c$2E)ynOb1jLO($!qzW|cL8gCd)CuwE^nOU0gjCzw#Y3=}$&00E) zdYd(ECn1N;;cU#B2*)H}CH>1gB f?aa$ej5{XZk22gG7WIj7^S^9I0Y=x!4mBG9@V`_w delta 235 zcmezHlkvk(#tp|9Ik~yH*;!ecxY;M4XDrgt6;7>4O)f1-jV~!m%qvbzF3HT#W68P8}k`IP1kAla;? z!)UU3gH{jYIll4X7d3)gDOBb$t&cd)+{$mAU5w z$!G2+KvLSnlF@i`s7ER{qv_<`!G(+ln{`9%n1N>Hgs)@XY@@TDfAg(O6{g9rt-o!S bihje*XtbF<{W8<$f7y-#jBb-1YBm4>k&#gr From ca5900fe5dd576c59be2131e8ca021b632507420 Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 13 Feb 2018 10:18:28 +0100 Subject: [PATCH 03/19] Bulid on any block --- substrate/client/src/block_builder.rs | 13 +++++++++---- substrate/client/src/lib.rs | 26 +++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/substrate/client/src/block_builder.rs b/substrate/client/src/block_builder.rs index ebd8ec6c47fe3..8c2d0c06288ae 100644 --- a/substrate/client/src/block_builder.rs +++ b/substrate/client/src/block_builder.rs @@ -42,24 +42,29 @@ impl BlockBuilder where E: CodeExecutor + Clone, error::Error: From<<::State as state_machine::backend::Backend>::Error>, { - /// Create a new instance of builder from the given client. + /// Create a new instance of builder from the given client, building on the latest block. pub fn new(client: &Client) -> error::Result { let best = (client.info().map(|i| i.chain.best_number)?..1) .find(|&n| if let Ok(BlockStatus::InChain) = client.block_status(&BlockId::Number(n)) { true } else { false }) .unwrap_or(0); + Self::at_block(&BlockId::Number(best), client) + } + /// Create a new instance of builder from the given client using a particular block's ID to + /// build upon. + pub fn at_block(block_id: &BlockId, client: &Client) -> error::Result { Ok(BlockBuilder { header: Header { - number: best + 1, - parent_hash: client.block_hash(best)?.expect("We already ascertained this is InChain before; qed"), + number: client.block_number_from_id(block_id)?.ok_or(error::ErrorKind::UnknownBlock(*block_id))? + 1, + parent_hash: client.block_hash_from_id(block_id)?.ok_or(error::ErrorKind::UnknownBlock(*block_id))?, state_root: Default::default(), transaction_root: Default::default(), digest: Default::default(), }, transactions: Default::default(), executor: client.clone_executor(), - state: client.state_at(&BlockId::Number(best))?, + state: client.state_at(block_id)?, changes: Default::default(), }) } diff --git a/substrate/client/src/lib.rs b/substrate/client/src/lib.rs index 1a7d4ceb622f2..f347dda3cb3d1 100644 --- a/substrate/client/src/lib.rs +++ b/substrate/client/src/lib.rs @@ -43,7 +43,6 @@ pub mod block_builder; pub use blockchain::Info as ChainInfo; pub use blockchain::BlockId; -pub use block_builder::BlockBuilder; use primitives::{block, AuthorityId}; use primitives::storage::{StorageKey, StorageData}; @@ -202,8 +201,13 @@ impl Client where } /// Create a new block, built on the head of the chain. - pub fn new_block(&self) -> error::Result> where E: Clone { - BlockBuilder::new(self) + pub fn new_block(&self) -> error::Result> where E: Clone { + block_builder::BlockBuilder::new(self) + } + + /// Create a new block, built on top of `parent`. + pub fn new_block_at(&self, parent: &BlockId) -> error::Result> where E: Clone { + block_builder::BlockBuilder::at_block(parent, &self) } /// Queue a block for import. @@ -249,6 +253,22 @@ impl Client where 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) From d11cfe1a87797059212cbfe5bf2dfb910cb330c5 Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 13 Feb 2018 11:54:24 +0100 Subject: [PATCH 04/19] Test for block builder. --- substrate/client/src/lib.rs | 24 +++++++++++++++++++++++ substrate/executor/src/native_executor.rs | 8 +++++++- substrate/executor/src/wasm_executor.rs | 2 +- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/substrate/client/src/lib.rs b/substrate/client/src/lib.rs index f347dda3cb3d1..046d84de5f421 100644 --- a/substrate/client/src/lib.rs +++ b/substrate/client/src/lib.rs @@ -306,10 +306,34 @@ mod tests { }; 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() { + let genesis_config = GenesisConfig::new_simple(vec![ + Keyring::Alice.to_raw_public(), + Keyring::Bob.to_raw_public(), + Keyring::Charlie.to_raw_public() + ], 1000); + + let prepare_genesis = || { + let mut storage = genesis_config.genesis_map(); + let block = genesis::construct_genesis_block(&storage); + storage.extend(additional_storage_with_genesis(&block)); + (primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect()) + }; + let client = new_in_mem(Executor::new(), prepare_genesis).unwrap(); + + let builder = client.new_block().unwrap(); + let block = builder.bake().unwrap(); + client.import_block(block.header, Some(block.transactions)).unwrap(); + + assert_eq!(client.info().unwrap().chain.best_number, 1); + } } diff --git a/substrate/executor/src/native_executor.rs b/substrate/executor/src/native_executor.rs index 99773b9f761ba..a759c046c662b 100644 --- a/substrate/executor/src/native_executor.rs +++ b/substrate/executor/src/native_executor.rs @@ -45,12 +45,18 @@ pub trait NativeExecutionDispatch { /// A generic `CodeExecutor` implementation that uses a delegate to determine wasm code equivalence /// and dispatch to native code when possible, falling back on `WasmExecutor` when not. -#[derive(Default)] +#[derive(Debug, Default)] pub struct NativeExecutor { /// Dummy field to avoid the compiler complaining about us not using `D`. pub _dummy: ::std::marker::PhantomData, } +impl Clone for NativeExecutor { + fn clone(&self) -> Self { + NativeExecutor { _dummy: Default::default() } + } +} + impl CodeExecutor for NativeExecutor { type Error = Error; diff --git a/substrate/executor/src/wasm_executor.rs b/substrate/executor/src/wasm_executor.rs index eef0df976d54e..00bdb55865644 100644 --- a/substrate/executor/src/wasm_executor.rs +++ b/substrate/executor/src/wasm_executor.rs @@ -276,7 +276,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, /// Wasm rust executor for contracts. /// /// Executes the provided code in a sandboxed wasm runtime. -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct WasmExecutor; impl CodeExecutor for WasmExecutor { From b973cccdb7dc3f86383943365b64ece1f9d0aeda Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 13 Feb 2018 15:25:42 +0100 Subject: [PATCH 05/19] Block import tests for client. --- Cargo.lock | 2 +- substrate/client/Cargo.toml | 2 +- substrate/client/src/backend.rs | 14 +-- substrate/client/src/in_mem.rs | 35 ++++--- substrate/client/src/lib.rs | 124 ++++++++++++++++++++--- substrate/state-machine/src/backend.rs | 2 +- substrate/state-machine/src/lib.rs | 29 +++--- substrate/test-runtime/src/genesismap.rs | 5 +- 8 files changed, 159 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6e54ac6c5e8e2..cda0cd782e20b 100644 --- a/Cargo.lock +++ b/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/client/Cargo.toml b/substrate/client/Cargo.toml index b9e0135b9e97a..8d3b4199910ea 100644 --- a/substrate/client/Cargo.toml +++ b/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/client/src/backend.rs b/substrate/client/src/backend.rs index fad0f203e8c64..4421c82b3742f 100644 --- a/substrate/client/src/backend.rs +++ b/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/client/src/in_mem.rs b/substrate/client/src/in_mem.rs index ce1aaf3db5d71..69afbb10b6383 100644 --- a/substrate/client/src/in_mem.rs +++ b/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/client/src/lib.rs b/substrate/client/src/lib.rs index 046d84de5f421..62df5390654a1 100644 --- a/substrate/client/src/lib.rs +++ b/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/state-machine/src/backend.rs b/substrate/state-machine/src/backend.rs index a1dec6dc3a575..f77e9a59d811d 100644 --- a/substrate/state-machine/src/backend.rs +++ b/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/state-machine/src/lib.rs b/substrate/state-machine/src/lib.rs index 33dd346c1cd25..a3f2e3dc4b898 100644 --- a/substrate/state-machine/src/lib.rs +++ b/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/test-runtime/src/genesismap.rs b/substrate/test-runtime/src/genesismap.rs index 0dc01c0abce03..1bc24d265763e 100644 --- a/substrate/test-runtime/src/genesismap.rs +++ b/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() ] } From ec618654248bb6567425e53458a78841e10cfaa7 Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 13 Feb 2018 15:31:05 +0100 Subject: [PATCH 06/19] Tidy ups --- substrate/client/src/lib.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/substrate/client/src/lib.rs b/substrate/client/src/lib.rs index 62df5390654a1..947732ee8070e 100644 --- a/substrate/client/src/lib.rs +++ b/substrate/client/src/lib.rs @@ -310,7 +310,6 @@ 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}; @@ -337,8 +336,11 @@ mod tests { #[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.block_hash_from_id(&BlockId::Number(0)).unwrap().unwrap().0, prepare_genesis().0.blake2_256()); + 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] @@ -381,17 +383,13 @@ 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); + assert_eq!(client.using_environment(|| test_runtime::system::latest_block_hash()).unwrap(), client.block_hash(1).unwrap().unwrap()); } trait Signable { From cec06a796e17f2efe81925cbd0908c77da727912 Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 13 Feb 2018 17:45:25 +0100 Subject: [PATCH 07/19] Repotted client --- substrate/client/src/client.rs | 410 +++++++++++++++++++++++++ substrate/client/src/lib.rs | 399 +----------------------- substrate/state-machine/src/backend.rs | 8 +- substrate/state-machine/src/lib.rs | 1 + 4 files changed, 417 insertions(+), 401 deletions(-) create mode 100644 substrate/client/src/client.rs diff --git a/substrate/client/src/client.rs b/substrate/client/src/client.rs new file mode 100644 index 0000000000000..3fff8bf2eecdf --- /dev/null +++ b/substrate/client/src/client.rs @@ -0,0 +1,410 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Substrate Client + +use primitives::{block, AuthorityId}; +use primitives::storage::{StorageKey, StorageData}; +use codec::{KeyedVec, Slicable}; +use state_machine::{self, Ext, OverlayedChanges, Backend as StateBackend, CodeExecutor}; + +use backend::{self, BlockImportOperation}; +use blockchain::{self, Info as ChainInfo, BlockId, Backend as ChainBackend}; +use {error, in_mem, block_builder, runtime_io}; + +/// Polkadot Client +#[derive(Debug)] +pub struct Client where B: backend::Backend { + backend: B, + executor: E, +} + +/// Client info +// TODO: split queue info from chain info and amalgamate into single struct. +#[derive(Debug)] +pub struct ClientInfo { + /// Best block hash. + pub chain: ChainInfo, + /// Best block number in the queue. + pub best_queued_number: Option, + /// Best queued block hash. + pub best_queued_hash: Option, +} + +/// Information regarding the result of a call. +pub struct CallResult { + /// The data that was returned from the call. + pub return_data: Vec, + /// The changes made to the state by the call. + pub changes: OverlayedChanges, +} + +/// Block import result. +#[derive(Debug)] +pub enum ImportResult { + /// Added to the import queue. + Queued, + /// Already in the import queue. + AlreadyQueued, + /// Already in the blockchain. + AlreadyInChain, + /// Block or parent is known to be bad. + KnownBad, + /// Block parent is not in the chain. + UnknownParent, +} + +/// Block status. +#[derive(Debug, PartialEq, Eq)] +pub enum BlockStatus { + /// Added to the import queue. + Queued, + /// Already in the blockchain. + InChain, + /// Block or parent is known to be bad. + KnownBad, + /// Not in the queue or the blockchain. + Unknown, +} + +/// Create an instance of in-memory client. +pub fn new_in_mem( + executor: E, + build_genesis: F +) -> error::Result> + where + E: CodeExecutor, + F: FnOnce() -> (block::Header, Vec<(Vec, Vec)>) +{ + Client::new(in_mem::Backend::new(), executor, build_genesis) +} + +impl Client where + B: backend::Backend, + E: CodeExecutor, + error::Error: From<<::State as StateBackend>::Error>, +{ + /// Creates new Polkadot Client with given blockchain and code executor. + pub fn new( + backend: B, + executor: E, + build_genesis: F + ) -> error::Result + where + F: FnOnce() -> (block::Header, Vec<(Vec, Vec)>) + { + if backend.blockchain().header(BlockId::Number(0))?.is_none() { + trace!("Empty database, writing genesis block"); + let (genesis_header, genesis_store) = build_genesis(); + let mut op = backend.begin_operation(BlockId::Hash(block::HeaderHash::default()))?; + op.reset_storage(genesis_store.into_iter())?; + op.set_block_data(genesis_header, Some(vec![]), true)?; + backend.commit_operation(op)?; + } + Ok(Client { + backend, + executor, + }) + } + + /// Get a reference to the state at a given block. + pub fn state_at(&self, block: &BlockId) -> error::Result { + self.backend.state_at(*block) + } + + /// Expose backend reference. To be used in tests only + pub fn backend(&self) -> &B { + &self.backend + } + + /// Return single storage entry of contract under given address in state in a block of given hash. + pub fn storage(&self, id: &BlockId, key: &StorageKey) -> error::Result { + Ok(self.state_at(id)? + .storage(&key.0) + .map(|x| StorageData(x.to_vec()))?) + } + + /// Get the code at a given block. + pub fn code_at(&self, id: &BlockId) -> error::Result> { + self.storage(id, &StorageKey(b":code".to_vec())).map(|data| data.0) + } + + /// Clone a new instance of Executor. + pub fn clone_executor(&self) -> E where E: Clone { + self.executor.clone() + } + + /// Get the current set of authorities from storage. + pub fn authorities_at(&self, id: &BlockId) -> error::Result> { + let state = self.state_at(id)?; + (0..u32::decode(&mut state.storage(b":auth:len")?).ok_or(error::ErrorKind::AuthLen)?) + .map(|i| state.storage(&i.to_keyed_vec(b":auth:")) + .map_err(|_| error::ErrorKind::Backend) + .and_then(|mut s| AuthorityId::decode(&mut s).ok_or(error::ErrorKind::Auth(i))) + .map_err(Into::into) + ).collect() + } + + /// Execute a call to a contract on top of state in a block of given hash. + /// + /// No changes are made. + pub fn call(&self, id: &BlockId, method: &str, call_data: &[u8]) -> error::Result { + let mut changes = OverlayedChanges::default(); + let return_data = state_machine::execute( + &self.state_at(id)?, + &mut changes, + &self.executor, + method, + call_data, + )?; + Ok(CallResult { return_data, changes }) + } + + /// Set up the native execution environment to call into a native runtime code. + pub fn using_environment T, T>( + &self, f: F + ) -> error::Result { + self.using_environment_at(&BlockId::Number(self.info()?.chain.best_number), &mut Default::default(), f) + } + + /// Set up the native execution environment to call into a native runtime code. + pub fn using_environment_at T, T>( + &self, + id: &BlockId, + overlay: &mut OverlayedChanges, + f: F + ) -> error::Result { + Ok(runtime_io::with_externalities(&mut Ext { backend: &self.state_at(id)?, overlay }, f)) + } + + /// Create a new block, built on the head of the chain. + pub fn new_block(&self) -> error::Result> where E: Clone { + block_builder::BlockBuilder::new(self) + } + + /// Create a new block, built on top of `parent`. + pub fn new_block_at(&self, parent: &BlockId) -> error::Result> where E: Clone { + block_builder::BlockBuilder::at_block(parent, &self) + } + + /// Queue a block for import. + pub fn import_block(&self, header: block::Header, body: Option) -> error::Result { + // TODO: import lock + // TODO: validate block + match self.backend.blockchain().status(BlockId::Hash(header.parent_hash))? { + blockchain::BlockStatus::InChain => (), + blockchain::BlockStatus::Unknown => return Ok(ImportResult::UnknownParent), + } + + let mut transaction = self.backend.begin_operation(BlockId::Hash(header.parent_hash))?; + let mut overlay = OverlayedChanges::default(); + + state_machine::execute( + transaction.state()?, + &mut overlay, + &self.executor, + "execute_block", + &block::Block { header: header.clone(), transactions: body.clone().unwrap_or_default().clone() }.encode() + )?; + + let is_new_best = header.number == self.backend.blockchain().info()?.best_number + 1; + transaction.set_block_data(header, body, is_new_best)?; + transaction.set_storage(overlay.drain())?; + self.backend.commit_operation(transaction)?; + Ok(ImportResult::Queued) + } + + /// Get blockchain info. + pub fn info(&self) -> error::Result { + let info = self.backend.blockchain().info().map_err(|e| error::Error::from_blockchain(Box::new(e)))?; + Ok(ClientInfo { + chain: info, + best_queued_hash: None, + best_queued_number: None, + }) + } + + /// Get block status. + pub fn block_status(&self, id: &BlockId) -> error::Result { + // TODO: more efficient implementation + match self.backend.blockchain().header(*id).map_err(|e| error::Error::from_blockchain(Box::new(e)))?.is_some() { + true => Ok(BlockStatus::InChain), + false => Ok(BlockStatus::Unknown), + } + } + + /// Get block hash by number. + pub fn block_hash(&self, block_number: block::Number) -> error::Result> { + self.backend.blockchain().hash(block_number) + } + + /// Convert an arbitrary block ID into a block hash. + pub fn block_hash_from_id(&self, id: &BlockId) -> error::Result> { + match *id { + BlockId::Hash(h) => Ok(Some(h)), + BlockId::Number(n) => self.block_hash(n), + } + } + + /// Convert an arbitrary block ID into a block hash. + pub fn block_number_from_id(&self, id: &BlockId) -> error::Result> { + match *id { + BlockId::Hash(_) => Ok(self.header(id)?.map(|h| h.number)), + BlockId::Number(n) => Ok(Some(n)), + } + } + + /// Get block header by id. + pub fn header(&self, id: &BlockId) -> error::Result> { + self.backend.blockchain().header(*id) + } + + /// Get block body by id. + pub fn body(&self, id: &BlockId) -> error::Result> { + self.backend.blockchain().body(*id) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use codec::Slicable; + use keyring::Keyring; + use {primitives, genesis}; + use primitives::block::Transaction as PrimitiveTransaction; + use test_runtime::genesismap::{GenesisConfig, additional_storage_with_genesis}; + use test_runtime::{UncheckedTransaction, Transaction}; + use test_runtime; + + native_executor_instance!(Executor, test_runtime::api::dispatch, include_bytes!("../../test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm")); + + fn genesis_config() -> GenesisConfig { + GenesisConfig::new_simple(vec![ + Keyring::Alice.to_raw_public(), + Keyring::Bob.to_raw_public(), + Keyring::Charlie.to_raw_public() + ], 1000) + } + + fn prepare_genesis() -> (primitives::block::Header, Vec<(Vec, Vec)>) { + let mut storage = genesis_config().genesis_map(); + let block = genesis::construct_genesis_block(&storage); + storage.extend(additional_storage_with_genesis(&block)); + (primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect()) + } + + #[test] + fn client_initialises_from_genesis_ok() { + let client = new_in_mem(Executor::new(), prepare_genesis).unwrap(); + let genesis_hash = client.block_hash(0).unwrap().unwrap(); + + assert_eq!(client.using_environment(|| test_runtime::system::latest_block_hash()).unwrap(), genesis_hash); + assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Alice.to_raw_public())).unwrap(), 1000); + assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Ferdie.to_raw_public())).unwrap(), 0); + } + + #[test] + fn authorities_call_works() { + let genesis_config = GenesisConfig::new_simple(vec![ + Keyring::Alice.to_raw_public(), + Keyring::Bob.to_raw_public(), + Keyring::Charlie.to_raw_public() + ], 1000); + + let prepare_genesis = || { + let mut storage = genesis_config.genesis_map(); + let block = genesis::construct_genesis_block(&storage); + storage.extend(additional_storage_with_genesis(&block)); + (primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect()) + }; + let client = new_in_mem(Executor::new(), prepare_genesis).unwrap(); + + assert_eq!(client.info().unwrap().chain.best_number, 0); + assert_eq!(client.authorities_at(&BlockId::Number(0)).unwrap(), vec![ + Keyring::Alice.to_raw_public(), + Keyring::Bob.to_raw_public(), + Keyring::Charlie.to_raw_public() + ]); + } + + #[test] + fn block_builder_works_with_no_transactions() { + let genesis_config = GenesisConfig::new_simple(vec![ + Keyring::Alice.to_raw_public(), + Keyring::Bob.to_raw_public(), + Keyring::Charlie.to_raw_public() + ], 1000); + + let prepare_genesis = || { + let mut storage = genesis_config.genesis_map(); + let block = genesis::construct_genesis_block(&storage); + storage.extend(additional_storage_with_genesis(&block)); + (primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect()) + }; + let client = new_in_mem(Executor::new(), prepare_genesis).unwrap(); + + let builder = client.new_block().unwrap(); + let block = builder.bake().unwrap(); + + client.import_block(block.header, Some(block.transactions)).unwrap(); + + assert_eq!(client.info().unwrap().chain.best_number, 1); + assert_eq!(client.using_environment(|| test_runtime::system::latest_block_hash()).unwrap(), client.block_hash(1).unwrap().unwrap()); + } + + trait Signable { + fn signed(self) -> PrimitiveTransaction; + } + impl Signable for Transaction { + fn signed(self) -> PrimitiveTransaction { + let signature = Keyring::from_raw_public(self.from.clone()).unwrap().sign(&self.encode()); + PrimitiveTransaction::decode(&mut UncheckedTransaction { signature, tx: self }.encode().as_ref()).unwrap() + } + } + + #[test] + fn block_builder_works_with_transactions() { + let genesis_config = GenesisConfig::new_simple(vec![ + Keyring::Alice.to_raw_public(), + Keyring::Bob.to_raw_public(), + Keyring::Charlie.to_raw_public() + ], 1000); + + let prepare_genesis = || { + let mut storage = genesis_config.genesis_map(); + let block = genesis::construct_genesis_block(&storage); + storage.extend(additional_storage_with_genesis(&block)); + (primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect()) + }; + let client = new_in_mem(Executor::new(), prepare_genesis).unwrap(); + + let mut builder = client.new_block().unwrap(); + + builder.push(Transaction { + from: Keyring::Alice.to_raw_public(), + to: Keyring::Ferdie.to_raw_public(), + amount: 42, + nonce: 0 + }.signed()).unwrap(); + let block = builder.bake().unwrap(); + client.import_block(block.header, Some(block.transactions)).unwrap(); + + assert_eq!(client.info().unwrap().chain.best_number, 1); + assert!(client.state_at(&BlockId::Number(1)).unwrap() != client.state_at(&BlockId::Number(0)).unwrap()); + assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Alice.to_raw_public())).unwrap(), 958); + assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Ferdie.to_raw_public())).unwrap(), 42); + } +} diff --git a/substrate/client/src/lib.rs b/substrate/client/src/lib.rs index 947732ee8070e..44b44613c8f82 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)] @@ -40,398 +40,7 @@ pub mod backend; pub mod in_mem; pub mod genesis; pub mod block_builder; +mod client; -pub use blockchain::Info as ChainInfo; -pub use blockchain::BlockId; - -use primitives::{block, AuthorityId}; -use primitives::storage::{StorageKey, StorageData}; -use codec::{KeyedVec, Slicable}; - -use blockchain::Backend as BlockchainBackend; -use backend::BlockImportOperation; -use state_machine::backend::Backend as StateBackend; -use state_machine::{Ext, OverlayedChanges}; - -/// Polkadot Client -#[derive(Debug)] -pub struct Client where B: backend::Backend { - backend: B, - executor: E, -} - -/// Client info -// TODO: split queue info from chain info and amalgamate into single struct. -#[derive(Debug)] -pub struct ClientInfo { - /// Best block hash. - pub chain: ChainInfo, - /// Best block number in the queue. - pub best_queued_number: Option, - /// Best queued block hash. - pub best_queued_hash: Option, -} - -/// Information regarding the result of a call. -pub struct CallResult { - /// The data that was returned from the call. - pub return_data: Vec, - /// The changes made to the state by the call. - pub changes: state_machine::OverlayedChanges, -} - -/// Block import result. -#[derive(Debug)] -pub enum ImportResult { - /// Added to the import queue. - Queued, - /// Already in the import queue. - AlreadyQueued, - /// Already in the blockchain. - AlreadyInChain, - /// Block or parent is known to be bad. - KnownBad, - /// Block parent is not in the chain. - UnknownParent, -} - -/// Block status. -#[derive(Debug, PartialEq, Eq)] -pub enum BlockStatus { - /// Added to the import queue. - Queued, - /// Already in the blockchain. - InChain, - /// Block or parent is known to be bad. - KnownBad, - /// Not in the queue or the blockchain. - Unknown, -} - -/// Create an instance of in-memory client. -pub fn new_in_mem( - executor: E, - build_genesis: F -) -> error::Result> - where - E: state_machine::CodeExecutor, - F: FnOnce() -> (block::Header, Vec<(Vec, Vec)>) -{ - Client::new(in_mem::Backend::new(), executor, build_genesis) -} - -impl Client where - B: backend::Backend, - E: state_machine::CodeExecutor, - error::Error: From<<::State as state_machine::backend::Backend>::Error>, -{ - /// Creates new Polkadot Client with given blockchain and code executor. - pub fn new( - backend: B, - executor: E, - build_genesis: F - ) -> error::Result - where - F: FnOnce() -> (block::Header, Vec<(Vec, Vec)>) - { - if backend.blockchain().header(BlockId::Number(0))?.is_none() { - trace!("Empty database, writing genesis block"); - let (genesis_header, genesis_store) = build_genesis(); - let mut op = backend.begin_operation(BlockId::Hash(block::HeaderHash::default()))?; - op.reset_storage(genesis_store.into_iter())?; - op.set_block_data(genesis_header, Some(vec![]), true)?; - backend.commit_operation(op)?; - } - Ok(Client { - backend, - executor, - }) - } - - /// Get a reference to the state at a given block. - pub fn state_at(&self, block: &BlockId) -> error::Result { - self.backend.state_at(*block) - } - - /// Expose backend reference. To be used in tests only - pub fn backend(&self) -> &B { - &self.backend - } - - /// Return single storage entry of contract under given address in state in a block of given hash. - pub fn storage(&self, id: &BlockId, key: &StorageKey) -> error::Result { - Ok(self.state_at(id)? - .storage(&key.0) - .map(|x| StorageData(x.to_vec()))?) - } - - /// Get the code at a given block. - pub fn code_at(&self, id: &BlockId) -> error::Result> { - self.storage(id, &StorageKey(b":code".to_vec())).map(|data| data.0) - } - - /// Clone a new instance of Executor. - pub fn clone_executor(&self) -> E where E: Clone { - self.executor.clone() - } - - /// Get the current set of authorities from storage. - pub fn authorities_at(&self, id: &BlockId) -> error::Result> { - let state = self.state_at(id)?; - (0..u32::decode(&mut state.storage(b":auth:len")?).ok_or(error::ErrorKind::AuthLen)?) - .map(|i| state.storage(&i.to_keyed_vec(b":auth:")) - .map_err(|_| error::ErrorKind::Backend) - .and_then(|mut s| AuthorityId::decode(&mut s).ok_or(error::ErrorKind::Auth(i))) - .map_err(Into::into) - ).collect() - } - - /// Execute a call to a contract on top of state in a block of given hash. - /// - /// No changes are made. - pub fn call(&self, id: &BlockId, method: &str, call_data: &[u8]) -> error::Result { - let mut changes = state_machine::OverlayedChanges::default(); - let return_data = state_machine::execute( - &self.state_at(id)?, - &mut changes, - &self.executor, - method, - call_data, - )?; - Ok(CallResult { return_data, changes }) - } - - /// Set up the native execution environment to call into a native runtime code. - pub fn using_environment T, T>( - &self, f: F - ) -> error::Result { - self.using_environment_at(&BlockId::Number(self.info()?.chain.best_number), &mut Default::default(), f) - } - - /// Set up the native execution environment to call into a native runtime code. - pub fn using_environment_at T, T>( - &self, - id: &BlockId, - overlay: &mut OverlayedChanges, - f: F - ) -> error::Result { - Ok(runtime_io::with_externalities(&mut Ext { backend: &self.state_at(id)?, overlay }, f)) - } - - /// Create a new block, built on the head of the chain. - pub fn new_block(&self) -> error::Result> where E: Clone { - block_builder::BlockBuilder::new(self) - } - - /// Create a new block, built on top of `parent`. - pub fn new_block_at(&self, parent: &BlockId) -> error::Result> where E: Clone { - block_builder::BlockBuilder::at_block(parent, &self) - } - - /// Queue a block for import. - pub fn import_block(&self, header: block::Header, body: Option) -> error::Result { - // TODO: import lock - // TODO: validate block - match self.backend.blockchain().status(BlockId::Hash(header.parent_hash))? { - blockchain::BlockStatus::InChain => (), - blockchain::BlockStatus::Unknown => return Ok(ImportResult::UnknownParent), - } - - let mut transaction = self.backend.begin_operation(BlockId::Hash(header.parent_hash))?; - let mut overlay = OverlayedChanges::default(); - - state_machine::execute( - transaction.state()?, - &mut overlay, - &self.executor, - "execute_block", - &block::Block { header: header.clone(), transactions: body.clone().unwrap_or_default().clone() }.encode() - )?; - - let is_new_best = header.number == self.backend.blockchain().info()?.best_number + 1; - transaction.set_block_data(header, body, is_new_best)?; - transaction.set_storage(overlay.drain())?; - self.backend.commit_operation(transaction)?; - Ok(ImportResult::Queued) - } - - /// Get blockchain info. - pub fn info(&self) -> error::Result { - let info = self.backend.blockchain().info().map_err(|e| error::Error::from_blockchain(Box::new(e)))?; - Ok(ClientInfo { - chain: info, - best_queued_hash: None, - best_queued_number: None, - }) - } - - /// Get block status. - pub fn block_status(&self, id: &BlockId) -> error::Result { - // TODO: more efficient implementation - match self.backend.blockchain().header(*id).map_err(|e| error::Error::from_blockchain(Box::new(e)))?.is_some() { - true => Ok(BlockStatus::InChain), - false => Ok(BlockStatus::Unknown), - } - } - - /// Get block hash by number. - pub fn block_hash(&self, block_number: block::Number) -> error::Result> { - self.backend.blockchain().hash(block_number) - } - - /// Convert an arbitrary block ID into a block hash. - pub fn block_hash_from_id(&self, id: &BlockId) -> error::Result> { - match *id { - BlockId::Hash(h) => Ok(Some(h)), - BlockId::Number(n) => self.block_hash(n), - } - } - - /// Convert an arbitrary block ID into a block hash. - pub fn block_number_from_id(&self, id: &BlockId) -> error::Result> { - match *id { - BlockId::Hash(_) => Ok(self.header(id)?.map(|h| h.number)), - BlockId::Number(n) => Ok(Some(n)), - } - } - - /// Get block header by id. - pub fn header(&self, id: &BlockId) -> error::Result> { - self.backend.blockchain().header(*id) - } - - /// Get block body by id. - pub fn body(&self, id: &BlockId) -> error::Result> { - self.backend.blockchain().body(*id) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use codec::Slicable; - use keyring::Keyring; - use primitives::block::Transaction as PrimitiveTransaction; - use test_runtime::genesismap::{GenesisConfig, additional_storage_with_genesis}; - use test_runtime::{UncheckedTransaction, Transaction}; - use test_runtime; - - native_executor_instance!(Executor, test_runtime::api::dispatch, include_bytes!("../../test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm")); - - fn genesis_config() -> GenesisConfig { - GenesisConfig::new_simple(vec![ - Keyring::Alice.to_raw_public(), - Keyring::Bob.to_raw_public(), - Keyring::Charlie.to_raw_public() - ], 1000) - } - - fn prepare_genesis() -> (primitives::block::Header, Vec<(Vec, Vec)>) { - let mut storage = genesis_config().genesis_map(); - let block = genesis::construct_genesis_block(&storage); - storage.extend(additional_storage_with_genesis(&block)); - (primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect()) - } - - #[test] - fn client_initialises_from_genesis_ok() { - let client = new_in_mem(Executor::new(), prepare_genesis).unwrap(); - let genesis_hash = client.block_hash(0).unwrap().unwrap(); - - assert_eq!(client.using_environment(|| test_runtime::system::latest_block_hash()).unwrap(), genesis_hash); - assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Alice.to_raw_public())).unwrap(), 1000); - assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Ferdie.to_raw_public())).unwrap(), 0); - } - - #[test] - fn authorities_call_works() { - let genesis_config = GenesisConfig::new_simple(vec![ - Keyring::Alice.to_raw_public(), - Keyring::Bob.to_raw_public(), - Keyring::Charlie.to_raw_public() - ], 1000); - - let prepare_genesis = || { - let mut storage = genesis_config.genesis_map(); - let block = genesis::construct_genesis_block(&storage); - storage.extend(additional_storage_with_genesis(&block)); - (primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect()) - }; - let client = new_in_mem(Executor::new(), prepare_genesis).unwrap(); - - assert_eq!(client.info().unwrap().chain.best_number, 0); - assert_eq!(client.authorities_at(&BlockId::Number(0)).unwrap(), vec![ - Keyring::Alice.to_raw_public(), - Keyring::Bob.to_raw_public(), - Keyring::Charlie.to_raw_public() - ]); - } - - #[test] - fn block_builder_works_with_no_transactions() { - let genesis_config = GenesisConfig::new_simple(vec![ - Keyring::Alice.to_raw_public(), - Keyring::Bob.to_raw_public(), - Keyring::Charlie.to_raw_public() - ], 1000); - - let prepare_genesis = || { - let mut storage = genesis_config.genesis_map(); - let block = genesis::construct_genesis_block(&storage); - storage.extend(additional_storage_with_genesis(&block)); - (primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect()) - }; - let client = new_in_mem(Executor::new(), prepare_genesis).unwrap(); - - let builder = client.new_block().unwrap(); - let block = builder.bake().unwrap(); - - client.import_block(block.header, Some(block.transactions)).unwrap(); - - assert_eq!(client.info().unwrap().chain.best_number, 1); - assert_eq!(client.using_environment(|| test_runtime::system::latest_block_hash()).unwrap(), client.block_hash(1).unwrap().unwrap()); - } - - trait Signable { - fn signed(self) -> PrimitiveTransaction; - } - impl Signable for Transaction { - fn signed(self) -> PrimitiveTransaction { - let signature = Keyring::from_raw_public(self.from.clone()).unwrap().sign(&self.encode()); - PrimitiveTransaction::decode(&mut UncheckedTransaction { signature, tx: self }.encode().as_ref()).unwrap() - } - } - - #[test] - fn block_builder_works_with_transactions() { - let genesis_config = GenesisConfig::new_simple(vec![ - Keyring::Alice.to_raw_public(), - Keyring::Bob.to_raw_public(), - Keyring::Charlie.to_raw_public() - ], 1000); - - let prepare_genesis = || { - let mut storage = genesis_config.genesis_map(); - let block = genesis::construct_genesis_block(&storage); - storage.extend(additional_storage_with_genesis(&block)); - (primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect()) - }; - let client = new_in_mem(Executor::new(), prepare_genesis).unwrap(); - - let mut builder = client.new_block().unwrap(); - - builder.push(Transaction { - from: Keyring::Alice.to_raw_public(), - to: Keyring::Ferdie.to_raw_public(), - amount: 42, - nonce: 0 - }.signed()).unwrap(); - let block = builder.bake().unwrap(); - client.import_block(block.header, Some(block.transactions)).unwrap(); - - assert_eq!(client.info().unwrap().chain.best_number, 1); - assert!(client.state_at(&BlockId::Number(1)).unwrap() != client.state_at(&BlockId::Number(0)).unwrap()); - assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Alice.to_raw_public())).unwrap(), 958); - assert_eq!(client.using_environment(|| test_runtime::system::balance_of(Keyring::Ferdie.to_raw_public())).unwrap(), 42); - } -} +pub use client::{Client, ClientInfo, CallResult, ImportResult, BlockStatus, new_in_mem}; +pub use blockchain::{Info as ChainInfo, BlockId}; diff --git a/substrate/state-machine/src/backend.rs b/substrate/state-machine/src/backend.rs index f77e9a59d811d..6174649c5ced6 100644 --- a/substrate/state-machine/src/backend.rs +++ b/substrate/state-machine/src/backend.rs @@ -20,9 +20,6 @@ use std::{error, fmt}; use super::{Update, MemoryState}; -/// Output of a commit. -pub struct Committed {} - /// A state backend is used to read state data and can have changes committed /// to it. pub trait Backend { @@ -33,7 +30,7 @@ pub trait Backend { fn storage(&self, key: &[u8]) -> Result<&[u8], Self::Error>; /// Commit updates to the backend and get new state. - fn commit(&mut self, changes: I) -> Committed + fn commit(&mut self, changes: I) where I: IntoIterator; /// Get all key/value pairs into a Vec. @@ -80,11 +77,10 @@ impl Backend for InMemory { Ok(self.inner.storage(key).unwrap_or(&[])) } - fn commit(&mut self, changes: I) -> Committed + fn commit(&mut self, changes: I) where I: IntoIterator { self.inner.update(changes); - Committed {} } fn pairs(&self) -> Vec<(&[u8], &[u8])> { diff --git a/substrate/state-machine/src/lib.rs b/substrate/state-machine/src/lib.rs index a3f2e3dc4b898..07d9683b3ded2 100644 --- a/substrate/state-machine/src/lib.rs +++ b/substrate/state-machine/src/lib.rs @@ -40,6 +40,7 @@ mod testing; pub use testing::TestExternalities; pub use ext::Ext; +pub use backend::Backend; /// Updates to be committed to the state. pub type Update = (Vec, Vec); From 38cd36a00bb6f68cb33437c617ce7da45e1eb9d8 Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 13 Feb 2018 18:00:21 +0100 Subject: [PATCH 08/19] Avoid pointless work --- substrate/state-machine/src/lib.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/substrate/state-machine/src/lib.rs b/substrate/state-machine/src/lib.rs index 07d9683b3ded2..e6127beb1e229 100644 --- a/substrate/state-machine/src/lib.rs +++ b/substrate/state-machine/src/lib.rs @@ -99,7 +99,11 @@ impl OverlayedChanges { /// Commit prospective changes to state. pub fn commit_prospective(&mut self) { - self.committed.update(self.prospective.storage.drain()); + if self.committed.storage.is_empty() { + ::std::mem::swap(&mut self.prospective, &mut self.committed); + } else { + self.committed.update(self.prospective.storage.drain()); + } } /// Drain prospective changes to an iterator. From 8b7abca60b7dd487f4921fb7867303051219883b Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 13 Feb 2018 19:21:32 +0100 Subject: [PATCH 09/19] All backend stuff now manages optional storage. Also simplified a lot of the backend. --- substrate/state-machine/src/backend.rs | 40 ++++----- substrate/state-machine/src/ext.rs | 34 ++++--- substrate/state-machine/src/lib.rs | 118 ++++++++++++------------- substrate/state-machine/src/testing.rs | 28 ++---- 4 files changed, 103 insertions(+), 117 deletions(-) diff --git a/substrate/state-machine/src/backend.rs b/substrate/state-machine/src/backend.rs index 6174649c5ced6..2c019510ffcb9 100644 --- a/substrate/state-machine/src/backend.rs +++ b/substrate/state-machine/src/backend.rs @@ -17,8 +17,7 @@ //! State machine backends. These manage the code and storage of contracts. use std::{error, fmt}; - -use super::{Update, MemoryState}; +use std::collections::HashMap; /// A state backend is used to read state data and can have changes committed /// to it. @@ -26,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) - where I: IntoIterator; + where I: IntoIterator, Option>)>; /// Get all key/value pairs into a Vec. fn pairs(&self) -> Vec<(&[u8], &[u8])>; @@ -54,37 +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) - where I: IntoIterator + where I: IntoIterator, Option>)> { - self.inner.update(changes); + 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..fc04d18f5b191 100644 --- a/substrate/state-machine/src/ext.rs +++ b/substrate/state-machine/src/ext.rs @@ -17,6 +17,7 @@ //! Conrete externalities implementation. use std::{error, fmt}; +use std::collections::HashMap; use triehash::trie_root; use backend::Backend; use {Externalities, ExternalitiesError, OverlayedChanges}; @@ -58,17 +59,31 @@ 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> { + fn storage(&self, key: &[u8]) -> Result, ExternalitiesError> { match self.overlay.storage(key) { Some(x) => Ok(x), None => self.backend.storage(key).map_err(|_| ExternalitiesError), } } - 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 +92,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 e6127beb1e229..3bbe7632a8769 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; @@ -42,73 +43,47 @@ pub use testing::TestExternalities; pub use ext::Ext; pub use backend::Backend; -/// 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); - } - } - } -} - /// The overlayed changes to state to be queried on top of the backend. /// /// A transaction shares all prospective changes within an inner overlay /// 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) { - if self.committed.storage.is_empty() { + if self.committed.is_empty() { ::std::mem::swap(&mut self.prospective, &mut self.committed); } else { - self.committed.update(self.prospective.storage.drain()); + 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() } } @@ -133,10 +108,20 @@ impl fmt::Display for ExternalitiesError { /// 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]) -> Result, ExternalitiesError>; - /// Set storage of current contract being called (effective immediately). - fn set_storage(&mut self, key: Vec, value: Vec); + /// 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)); + } + + /// 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; @@ -172,7 +157,8 @@ pub fn execute( exec: &Exec, method: &str, call_data: &[u8], -) -> Result, Box> { +) -> Result, Box> +{ let result = { let mut externalities = ext::Ext { @@ -180,7 +166,7 @@ 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").map_err(|e| Box::new(e) as Box)?.unwrap_or(&[]).to_vec(); exec.call( &mut externalities, @@ -216,21 +202,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 { @@ -244,16 +233,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..28661579ec86a 100644 --- a/substrate/state-machine/src/testing.rs +++ b/substrate/state-machine/src/testing.rs @@ -21,34 +21,24 @@ use super::{Externalities, ExternalitiesError}; 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]) -> Result, ExternalitiesError> { + Ok(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 } } From 9dba812a5d4f8ddc58a69472a9adbf89bfd2bca4 Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 13 Feb 2018 19:42:06 +0100 Subject: [PATCH 10/19] Native runtime-io now supports empty storage items. --- substrate/runtime-io/with_std.rs | 55 +++++++++++--------------- substrate/state-machine/src/ext.rs | 10 ++--- substrate/state-machine/src/lib.rs | 13 ++++-- substrate/state-machine/src/testing.rs | 6 +-- 4 files changed, 40 insertions(+), 44 deletions(-) diff --git a/substrate/runtime-io/with_std.rs b/substrate/runtime-io/with_std.rs index 98864baa63be7..3c75935d64ed2 100644 --- a/substrate/runtime-io/with_std.rs +++ b/substrate/runtime-io/with_std.rs @@ -27,36 +27,29 @@ 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. @@ -164,37 +157,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/state-machine/src/ext.rs b/substrate/state-machine/src/ext.rs index fc04d18f5b191..ac047a31c303c 100644 --- a/substrate/state-machine/src/ext.rs +++ b/substrate/state-machine/src/ext.rs @@ -20,7 +20,7 @@ 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)] @@ -76,11 +76,9 @@ impl<'a, B: 'a + Backend> Ext<'a, B> { impl<'a, B: 'a> Externalities for Ext<'a, B> where B: Backend { - fn storage(&self, key: &[u8]) -> Result, 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 place_storage(&mut self, key: Vec, value: Option>) { diff --git a/substrate/state-machine/src/lib.rs b/substrate/state-machine/src/lib.rs index 3bbe7632a8769..363f29383039a 100644 --- a/substrate/state-machine/src/lib.rs +++ b/substrate/state-machine/src/lib.rs @@ -99,16 +99,19 @@ 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, 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) { @@ -166,7 +169,9 @@ pub fn execute( overlay: &mut *overlay }; // make a copy. - let code = externalities.storage(b":code").map_err(|e| Box::new(e) as Box)?.unwrap_or(&[]).to_vec(); + let code = externalities.storage(b":code") + .ok_or(Box::new(ExecutionError::CodeEntryDoesNotExist) as Box)? + .to_vec(); exec.call( &mut externalities, diff --git a/substrate/state-machine/src/testing.rs b/substrate/state-machine/src/testing.rs index 28661579ec86a..6d991a9acab5b 100644 --- a/substrate/state-machine/src/testing.rs +++ b/substrate/state-machine/src/testing.rs @@ -17,15 +17,15 @@ //! Test implementation for Externalities. use std::collections::HashMap; -use super::{Externalities, ExternalitiesError}; +use super::Externalities; use triehash::trie_root; /// Simple HashMap based Externalities impl. pub type TestExternalities = HashMap, Vec>; impl Externalities for TestExternalities { - fn storage(&self, key: &[u8]) -> Result, ExternalitiesError> { - Ok(self.get(key).map(AsRef::as_ref)) + fn storage(&self, key: &[u8]) -> Option<&[u8]> { + self.get(key).map(AsRef::as_ref) } fn place_storage(&mut self, key: Vec, maybe_value: Option>) { From 0f591c6514f24404b5e4532077bd2a12b82b2959 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 14 Feb 2018 11:40:39 +0100 Subject: [PATCH 11/19] Finish up the API transition. --- polkadot/executor/src/lib.rs | 28 ++++----- polkadot/runtime/src/runtime/governance.rs | 4 +- polkadot/runtime/src/runtime/parachains.rs | 4 +- polkadot/runtime/src/runtime/session.rs | 4 +- polkadot/runtime/src/runtime/staking.rs | 20 +++---- polkadot/runtime/src/runtime/system.rs | 8 +-- polkadot/runtime/src/runtime/timestamp.rs | 4 +- polkadot/runtime/wasm/genesis.wasm | Bin 70449 -> 76656 bytes .../release/polkadot_runtime.compact.wasm | Bin 75751 -> 76656 bytes .../release/polkadot_runtime.wasm | Bin 75800 -> 76705 bytes substrate/client/src/backend.rs | 2 +- substrate/client/src/client.rs | 12 ++-- substrate/client/src/error.rs | 23 +++++++- substrate/client/src/genesis.rs | 15 ++++- substrate/client/src/in_mem.rs | 2 +- substrate/executor/src/wasm_executor.rs | 51 +++++++++++------ substrate/executor/wasm/src/lib.rs | 2 +- .../release/runtime_test.compact.wasm | Bin 14152 -> 14298 bytes .../release/runtime_test.wasm | Bin 14248 -> 14394 bytes substrate/runtime-io/with_std.rs | 9 ++- substrate/runtime-io/without_std.rs | 27 +++++++-- substrate/runtime-support/src/storage.rs | 54 +++++++++++------- substrate/test-runtime/src/system.rs | 4 +- substrate/test-runtime/wasm/genesis.wasm | Bin 31744 -> 32296 bytes .../substrate_test_runtime.compact.wasm | Bin 31904 -> 32296 bytes .../release/substrate_test_runtime.wasm | Bin 31984 -> 32376 bytes 26 files changed, 181 insertions(+), 92 deletions(-) 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 91c7b475743cb7b9cbb48c6dcc162501fe9e43c0..bc0cb89235c570beee9e2749b796933e05e9f774 100644 GIT binary patch literal 76656 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 literal 70449 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 delta 29064 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 delta 29079 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 diff --git a/substrate/client/src/backend.rs b/substrate/client/src/backend.rs index 4421c82b3742f..7c445908f7568 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, 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 index 3fff8bf2eecdf..ec4d8bf40516a 100644 --- a/substrate/client/src/client.rs +++ b/substrate/client/src/client.rs @@ -132,9 +132,10 @@ impl Client where /// 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()))?) + 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. @@ -150,10 +151,11 @@ impl Client where /// 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)?) + (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(|mut s| AuthorityId::decode(&mut s).ok_or(error::ErrorKind::Auth(i))) + .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() } diff --git a/substrate/client/src/error.rs b/substrate/client/src/error.rs index 1c97387e817c9..65380efe1084f 100644 --- a/substrate/client/src/error.rs +++ b/substrate/client/src/error.rs @@ -19,6 +19,7 @@ use std; use state_machine; use blockchain; +use primitives::hexdisplay::HexDisplay; error_chain! { errors { @@ -46,14 +47,32 @@ error_chain! { display("Blockchain: {}", e), } + /// 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), } 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 69afbb10b6383..5d56ef577078b 100644 --- a/substrate/client/src/in_mem.rs +++ b/substrate/client/src/in_mem.rs @@ -172,7 +172,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/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 39b6f737efc2312f442206f6204d317487855cba..cbc81694c0999d1af58aaad064d227df8200e417 100644 GIT binary patch delta 2168 zcmZ`)ZD`X+7{9yx)4W`g{?lq3X}ez1TGN)Mans`1WS;m2Q|ugUFsC-HX^pl?Nur%H z*D}gfCR^NfvoIMIWD{&5Tl~<0xNIvphDbm3gNU#U>OKcnHa`rv-$i2;y1@1Kzvt!m zJoh}$U4Mz+OJvV5$BjLWzf;CWEh@um`-{X+B=J)&i$rqMA^#!LDu|$_Q)Z~NnGRu* zq>B+bNu@eiKnAt?Qyw22JzK>p2?XEp*7TM8GN?svE z*ya02g+nfqxTI;hs4lqPq>Ji-YbXA8xc}5?`aR_q$Zf1_Wf~88tEj7B%2t{8 z$}E#bb-K#$&eur4%+$&+sLc9u*Qy@bje-@rnYu^SXDhncCgDJ|;L$F&3s_V)>tCiz z>S6ss@3>8;?T|(n+W@#?Qv(elcHN%4*l>q>@7o`1t&g?i4t1h2gtG}DJF+|+lT+7Bh zxwo3vQ#R*O=hnu23!W}!3MSDN7h(Vec2GK7+SsK^b#3j=|FIc2OEwc`?#GrM$|mbn zQ}B=GOns@HOLb4i&4Q8RpEc|w(%?q7$0S6WYNWk$G*7s9FxK0bNJcvo!|7Ne9!jPN z=9K$jMB1^oV0p3jQbJY(^_}266-^&Xr4z}>K$PHOace9-d^}BXvAFH2e3EiYec)`6t*2d;yEdr4_URD2$T!JmbcxXcD<(7`G(i$a;eD&6e~CL{p4=`y#`U zzF7LCNSuf!`v(&v1h;{sm5)!~4Xg&!uZ$2!iE*B(j<#CdfbDH}UY|C&ISf@5RIeeT zE*Qa-a9;*eUB*1{`7$m%c=q)H*+-wyiN1}0& z8jSVD;sctvpx)@L3y--u3xx=t7H&@I^WZlIMB@o&%#HWnra$qMSKYiBjcMLV|}7Q$5!dY^{Jcf z#5DnFnWT1^#N;))O66oy=}0o25|K1=nm4(-lq*X7L{v}%>)&R1Z!W)nBc;XMUoY;a zEMKFxZP>vwwd(waFB$$_v~C=tVzQ3=vIwnbtpI#}*0ObIaJaW_#R@LL)x@Z4moV`8 fOP#4-@g>OC^J=_8c&QS7oyo|^p%c+QJW2lnp|0e` delta 2062 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 delta 2105 zcmZ`)TWFhA96#rLU#=%#@}*td(93#~l{Q^x+HPyCFttbPHgR)>J;)HXw{(l`lB~%# zH(`ykH$keW`r=E4B1I==OVO!glsR-r84-r;K}H^m`Y>2vRTSp%_oa87fpdP}`QLBn zKCRIROhq zozSY)ds=-3AzUZVQ0^r10Wr4|enfq*wNX>u*IFD?)S`T-f_eir^F8_*N>>i5N57=r zvH!634rO#hVjZI)xLQk0op4-h{E9JsgGiQ+hHM*U4m&?)i0DsezTyInvas;T9Iyj8 zNAush-e#_OYPJwAZ&9_^bBxZYFFXh7dG(K{278rp7<=5w8M8I-wd`5~2sn*6THhfY0f1Fx(8VdX4h>=rKJ zlHu2*Y(H>P?QM91PN{6etNt0g6`)J%QMMa!-LCF6MA$7yzH#eK>VJT;TU?m1xHC@m zurcDg?J`@D#G*rWiJUp-iLwL2EoXrH<&vTP5Pz}TUUe_nZ9yqirJf3%Vg;}IGSqEB zDYTo-dsXwc?;4lTP~kGeu=a~)wMZ^#2$hv73z~8^^=`QVy8%H3l}^MT2F`m_e^Udy z>roR;y>&}Ql$n?Xw9A75(WDMB0h~7ScbYmWTQrovIUZP+W6N^P8YCf@1hb)KJG8cPB&!S3pZkFK83iNa5YA!Q&E(*}{f?5PxH7fzp z=ndAc+xaA2H`hCY5;@k*U4%L4!0n=g2eSCI^3DM_=l#IJ!zQR!$O705>;pob^aGK_ z2B)}}Z$-YAw_WRwuC6&Mxp>8DebSln_T$Ona)Bn@oDZJK#6&zX9D73$Iqg<&wr}57 zaC4^!TbwkAMK`y%isXpsM>pM^0 Op })).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| 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..ac289a49887f2 100644 --- a/substrate/runtime-support/src/storage.rs +++ b/substrate/runtime-support/src/storage.rs @@ -24,8 +24,9 @@ use codec::{Slicable, KeyedVec}; /// 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 maybe_raw = runtime_io::storage(&twox_128(key)[..]); + maybe_raw.map(|raw| Slicable::decode(&mut &raw[..]) + .expect("storage should contain a decodable value if there is some entry")) } /// Return the value of the item in storage under `key`, or the type's default if there is no @@ -85,17 +86,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 +128,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); } @@ -146,8 +153,9 @@ pub mod unhashed { /// 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[..]) + let maybe_raw = runtime_io::storage(key); + maybe_raw.map(|raw| Slicable::decode(&mut &raw[..]) + .expect("storage should contain a decodable value if there is some entry")) } /// Return the value of the item in storage under `key`, or the type's default if there is no @@ -207,17 +215,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 +256,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 +280,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 +302,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 +320,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 +333,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 +342,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/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 3eca5047352e7efbcf6808672b0454858e410327..7daba8a15c5866dd375c889a8eeb5f9cec8e7dbe 100644 GIT binary patch literal 32296 zcmd75d30UZecyYgJDz)i3vdD_@!V@l0L_!k583bi( zwJ|ScCXwR_&hv|1r*V*0vE)>0m@Fl6UDrm|k{vrQm6a|#_RI5&(JW?Q_n(ASqjR)73yC_SwI^_iz99Z~vw}oC9y_^fBM_Jpb;-f$;3vv;Nrwf&Es- zI}q_(FiX7yJ|I7{zs-~4?p;*7ph>y3XY#SHa$BvH+^XT-0b1${j(=d%z2TDt15nQ zc6!Y-`RQ+ZwG$E?sR_W;w!4jwz%Ujx9&N0$Rm zPtQ3(Q#ek}9zH&||KaI(dKIDRgF&iS70kxz>YbT;aMD#ARAW%>&&je;v<&J9b%V3f zGz6Y|$BB3D-?n`cK3#Wh6jXx9^U5XP_dVaQM1Ccxlq!{a$&cf>QYrSjn?818_R+u_nmuzG&QCvd`1t-a$KP@I_#tnwoH1i{WK3McK&B6#nVV*U zP8>Yqy?RBSpPQXJetPQQ+~E_)y&DD|JbZlW=;70OwHs>#zL$D&b;riF8`Yr}KU~)M|Ti`W>&e*^43hZ)!n&)_+&ZI7fph zeZf1@X^aO^(n!7Wpqe!H`4ge1T$;36NkaqYSG5z(bjt9-TRHA|Njcp>AL-c4sOP!- zi1Ih~@+*}e?d4m_Z|>zA$|t>iRrw9QyvC4@~vWKKd^9;Wqld)dw*8Ug`rFeJ}O_jJ~BlfYJAjK7bMU^#YJlh&aLSv?ZZ#BN~#B zNhcv=40l;hNIS7X@g2Br?hsH#z9 zyoNc?~Jhr+C%>eMYS0-;>Z zDzt~Q!kH6SQ`;zQ%wWC3bZ({-Ha%>YXM-D&TV|uU!o#q4tdlckz)SL+SrkXqS8q%& z%wYd=7=(QWd+dnJU<7+Yi3*dTM@?afjFg%_HXa$lUzC$j>QhdTO$0&`GeUyusqU~7 zk&#X*3E|QR(~86}|M)4*Gj`J?-@qdsP|55w^OW1ORTn&Qm{v-GH}gyqkz}<3<`fe= z3|>8ULh!16Xab6?$f=kU37u0ZoIvPoap45Q)J)yV+>VBlRx?j|doy&6Ub)#h1ST&x zGl!3M`H~~Z^spyBj8Z~Xkx-SB%0!sJt}@2XNs^nEIs%vYNzkgq#bY%t18tRD62?`l z(^?xp75f8cF_q?TnZ7|VrPsG(n>jiCj+f3q@7>+>1jUjKrv6NVg{Y?QnnAEBJK3qF zFM215AJPv8Gh2P{77raw{}EaV{P=Qs47^P^kY4)3uWh{t^)nR5JGC}#a?SMGH3+cg zcOg5jOZvky8St{ctQ%`$jcTv!wK+S}L1}AnXpiSL{J0O7!TV{B)9VxONV}HakaoMz z!9tB5aXr)8G*@m=UALn`vhOl311hr$*J(zY_|4Y>qILWt?U@13ymO6sOfw z&48&JIMObq*!i2hu?|y_hIgz_!&C4&xT6^*-tAb~QX8MvE)iTYs?4t-epqkp=4c?( zH7K>YF&Rw4J>HGz6)XgH;|!SN(V#KpdCjU?M`)lGThxNcFKj|?ETjpTL;juY`&UWBWc-n1>PN{vL0PWXu_xSHPVsIaAI#llOQvuF#gp2uMVwhl@QDj^f z0$0!s8PYNW;%>Jk;D%c`~=aP31f;X6Wd%RKRdw{l9?4{in z5q}!A2LPATMZaB7H?=E$<=sdXLMjAQ@Y`{^q)J}9k`A_;ra25|_>uIYKP&7JHA89! z)aK~qX(IsxM?eJ=B&&WiMhK>iNr=zqI}M;CMwKR z*(OmWN*b!tKP+q8Gh$2kq4_;|2uvOdVpSPoh~*w-sML&KP-4bCVcmzFAUIkZU#j`_ z!el4e3JEnPipkB;%cWYzRb|s;pz4Hd#63hqQ+2{+GPSfW(-nB)G0$vu&b8b zhlKh`eTJs#=Lk_4f19ajma%ZzV~If4`eE#h*7bN3^=~@`_IjJrT6)Ppg@)i3#cC$L zB>Y-gXq;+P&RRrj;U+W^X?f#|Y7C-Hr`k2*Q}7Wp32S+7wjKT;tR;t$Kq{mGXolxt z`bF>5ELH+6v)L4|NN37e@-umoDe`eP-zKE=4O@M&d20zwcZHW}lb=_aCzvA4zJmFW`q;^oS(?P(3pJ2mn}S+96uu$#2#nDD#_j`Aw$v zc2rDO2{C}imzO5WLRnG*!OW(Hj-h7n;h9Oh$QO>NQ_oB*yximA-`h2+TXDTGtu??^ znbs1fHR>zRO=}HfskUZX#p?*uDjTdRDokq?(^|u{R&&#e)u*PJPN`TD&0;$1W?DlCcK_xs@AbZlIP}3Dw~vLUQKgR4d=n?h=KJ| z6Jv4@X3Hs7Zp^fh$oQ$qCfu|?31$3xsAT;5QS}+Wx|@i)jGq>`ir4FdQrZAWy|u}U zA7ckjiw;~F6Qo$E6A-%@%;`$!}J;)CJV53Ni#jLspbVi z7+mG2ELgb~r{7jc#v0d8zu^(8>EY6)@E)m9dT}t_lD^PNF9d0KMf!!Hb$v6C7yti@ zFwRFUi3>a`zpyH8rwgmoU-8q$HR(&L>`P|-bpCJdoZ(k|H!SnhzkKi9O#1KMf8#wo z|N2ure|$ubfAN<*HudzqOP9P;>BleD?ShjNdnSEV;UhvMF#9^9jX;#~pJpN?cPijxLD`Fh7;I}imaE!#v5v+npM$dP; z=cnupN_c$)s&XnN@{ZEz9zfqYYiURXq-|t zsUVbMk5<;^Qlg~lkYp3q=dfUj8B3!^A*I7t%8;?F1Q~v!iZ06+RmBs{j$tngEy%58 z8FJ^)zCZE;aifQrXjaQDJdv={>E*7BrKT!i;F)nT7N;-%A)yFFc!XVnBS{5ALCno6 zkUA7c@$c6BauFt7WG7irHBlT>(++^vSCu5QBI&t6i(Jqi!Q6H+)slQ>hCp8$3-T;z zcpcgVt!HN}P_FQZ9$@*X&}Kt|>42;9k}``aaWQ1c?I&!FtRx|1YB5WA03>xh*ue9A zDP1U8Zp87Feo_8=*j7Ufc_3k^m>Cs22THlbLL2s2A0CXny?2RCp&Y#pHBn`DH&mHl zOt)-ZXjt-rAyQ=*4{2z^ufbJGtOrdg(_bY{Dl2vnhZ$;F8VH+Yv}_`hDx(=?H0(#? z1yb9)a5)0SEC=`Rr3&2NNvj&WB;&#Tq@NB$5VqRXyHjz(IZdl`v56h*7*>gWga0c zWh;p>q+cNmQ^kT)RW5PP(qWFzrobNRAn`my_ zi44MNS{*oL*FGDOCv%#3>)B^8Goc8SC21ah+Jc%G6ScOcOg^m)jf-E=0Z5Uh*BVq3 z6$t~o- zN*j(cvI@E2ZbozD-f5voS-xD)Ej@=Rq=G5Bd%e>vHhd90n4!tM(Q&^gD<+UgNHzyf z1gWIp!p;0LlhQH*9$t?$tgQ0p@6M7yGs(_uj!mj-^_rk~$-> zT~PgE^PX(BR}~;}I-o<|*e9R~qlfLRO95N_iHY53i;cWDBq`gkK?a@SWbr`vK^h>A z&PYTVgrnU3SIvIaIQl zA}Wq1A^NON4B8OX%uGT$PXR$V)b!etl`S11Wf&Pg#Ml!h&-#6F*di8<@HG;z8w3zgx7 zm*z~eAtxZ&aMNZ5I>@A4@*x5wV|{a`MsChb^lZ+o!qWr)OG)`K2TZS&lqUv7%EiF1 zoJ9$nJyWi*AlpOYbV;GAmv+Awo=U5aunWzmIe9IibY8{sbcVHsf0O6qW!&a5?U9$8 z6jl+ZV-6a3YqK)Zauos4CO$1);H*#>=2IE87C-BZI97ab8}}g6LcLCD4cTQC!#?d`WCRNlggK zsfabw;=fAVguR45c7yHePSQ;M(_BO;sR=gLdTDrCkZ?aGR`Dt9*Hi0cv}jmwT}KKB zTGx8$YUCE)eX6KN2lt>46ibxt2!z6(%v1Nsw@?ot$j6Y*thmt*sv#ic^mf5JWp&d zqM%x1)T}T_Lc(H@lPb#wd8JU>|)sVkC#pLA8Vu5z}jr-H4TF`;yf?Xa@3-st$&{J0f5+kUfPhBTPs9pmBV|IR;Sw9-Wj4QP9yYwvdJitp?&^8lIfK|dkAjI zku|(f?)5JBlP>uwC40O#x#Z2*1_%>}TLYw-hymK--QO9K6Ujxa4Bg7A=q?qB`y`XD z25__hCO3FMCo1XV8_5~rBgiL&C!MIZ9Y$$tlA$-D#A?Dg#%B5|kK!b$JjMl{?d@Up zVGVw6B7B(oNFgS~&f{C@^JLI|327_+w`8ysI;n4^Um?@XGk->AK!a9E#-6VM8+0%n zEO5txj2Du&XbV=$hWrXf{*okALr;c{TIS+mESXW0QJB$|QMH_&j1mIzK1zNR0)oBb z-Hq}jx?$w98>a(c} zl_Q;Dnq!T%ibsMuQfzxX%66SoI2b0k$DUq>~E z)4!qii|UqV_~GCg#P3Ar2ve={7?c}^sufM>%)tu?O6+LJvBH9!E{)$q4&e;pV#miD zhxc@b)^{8M*aeScF7b^Ma9J!j`eaB`NRyX>&d@Xa=KXhdR%nO2Sx#0wiE+cjR?PDj zci9G;KBv!Vwau>Ya56L=oUaJ!c+gdF0sqm9;BgvdsEujt>HR;&=<(K_nld|9nNk3e77st(#qn>S;o(xLYxLGnoKK zf$#Dj2I1Vie>CTPP6&8qL>1#WUxM9Xx5c(GUH@9H4|1h7-K<;i;=Z+J;H1R$4q{X+k%(FX z*T_tVtqzATd^*}v^|B3%hXJxXIt%ma*em{;jF#DVC>G<5<=C;#43A;dp!*2qg6+&Ot)506!MFzsto?H)z~W_B7aQteT6)xq*UV zgqK-G?u+!JgguOHs2CD^Hd!${!2K09fRhRjGGJJ+jlHh^if{`NA-c(ctcS8E?y>b( z)a=r?U-V7TmX!})*>@Ep^Xc!+`qd`;mC}e+T=}F7P=G^bdNUD`bw<23(>c#v8B;Wv zmGPMK!Id#r&8&>aW&Fs7nX5r>P7DGjEMm?UsS51}3*KTQob2?eL9<|((+p7bBXt&| zAFFVf8h8$`!a;|FCw3XsJ5Scv$%(K~a5hX@Wiw$*VeTc;m+Xf24Z*M~uK4b+4EV+sAL{b06UL;_ivD44ca zREmZ1Ao2>wiiPo@)`;5N%+`#9SQzrf!gx?In%#GB3*SL{Bws9y36hjAZX?U%vuo@s z=WYqPGvWwvG!p0jkzSWKN;^EuH9>VldjNAhlUO`Nk=a$+C4&so#ye<_ouO>JLuyF{ z05;&>vUtdin1w_})sP!u4>G<>%qC7-nnZY+^-&aM?g9U#Ycm##a+6LeZkHYR9>D z$4+pIog~Iiieyf~mv>h%Rx9L!u}F_$%vjqQ%sOQ(#9=_qyW=r>(hEJA^b(Vp^eRHO z#e$1qiRq-2h&B5ci+wF^S3-Bf8Ln&E8IoYVfd8f*LBe1f{&+JC_QC;=;~`TDbju&^B5|Tk6|2ZCyDV647WLmua|HAeL~H0nwG& zbkGr9J=Uo_voFYr9w>;`bd)-^+nX)3T6yJUk)HoD{*muzxL;*z#YIxcyYOE$aYT9;hslIs~tEfJZl z1LUma)rm+<5jb{`xy?dg9=8M|JrL@R4s*<4pu+d(@l6p#TwK8gUUZ@7yV!@0pIy2--qUbtfbS!Chsi+>h%X<#`0Ola)3TEsyNIelS$I>0Nk zxY3ptcItsA$zz@&T(ZV_lq5!B7W44Qg%BQ=!*6kJli4=UjFt;=hOrV7KQ$Xrj9^d0 z1xyD;t`s(^g0|TZL%EsZbWH)ERH0>FzP2b>S3IpRo;Y&L@N6uet|^{26;B{lRGe3C z7X_W-=~cxOM~WHJYm2Aril^&~Cj?xfb0*-}v1QZCATAdI#alFRVzeyOzI?4$h(#uX zto5!4+grFr1r?|QQ2I+9?FPP#7Br`B$+r0Kul%?6#&H1 zR-=95xa7p~pJd{wW>*(SSCWZiR?XJm3wULVaa-o{pDJS+@!|_*ti0rnV6rDuWrbRW z*x9bGiQr0>HF>qw#RhMmhV|Yf4D6 z-E2(+xolHgbHEOhWB;=4BOl7yRd+i@DPo{bT@G@+EB1|4LmNgSFKIP`y-7)gWSiFw z9u-9)HNg+DY6FYCL7`PP&-qrc3-92&+ZdLw7V1~L7&KHY0tvaCk#&gjPS}hqN}Tzn zB-KPO=%Qz|PnR!PwCc1nZDn5(xK$hP+PJ-tWRT|)+nJwVpT4T4*J++z@AB>RHp?%$ z{C8dQ9alSRwArc_IF}XD$-k)Z;%o1WbTK}nH%#-re1a?6&)0ihEK+$%T!F9RFl>h2 z1#fMG16*j~=DNl&IR7(dl-n|Pi;3lGv?XkD*c+u^0#f`k5nlG|U;AU`NohwIvC4G(;v@I}Ghw#Dp!|#3$Xr!!hIhob`D5qt=<#d%} zUYCqZ6XD~eR>~&9ayGl}X^5|gb_GvW*xQx#>=UD^@lP83HMCT+e*yd z=9V=;9iwzHmO3V-K^X^kfbt_CFK~kKX(rAM*>;A$sF$P2Kf%GHc82~$dF>26C4hE@ z7IY+OGCCey5b)VP3_QA>p>J1$QlGvql#4gjb@tZNSN3P^?JcLTWu1IsHZz6gMq<}! zEvFX|7%NS`YwO^0`V>NM@B+f*8MpvAWQbkfrfb*%7Z8URcmct1JkF7~y!ZPo9rr(w z<$pO#KWyn9?^m+)BU$>`mrqi)hzudS^8^P`tdCN^(wVJ@u`yVVJg7G z!Z`EH4g?yE>oO2nxE_;(gygBxMDRFBvdwIbD+}|)qHG&}UXSWgl{pE~vm+Rib2?v^ zyqD1M%g7T*VSFEX0zn?icR6`4X&2-(n3!U}J$;s3Z?I2mm!iMbOVLE|gj!uGdGJ2t zaxQ7Ej^<=2Denp{lw=tydVH$H1_6>i@9Tzz7>%KHS4@9oh2~z}dD{XhryurnSDqP- zvO*adjS5Tta{4UOYWfab8@_ycLHVaO<>>o&l$X9=5J37aVc-!(84o@s;AeUm7-gpK z&uj`-_38Uk=}2d_I%Qt*vcwL$a){(aoFT985S`l%IGk@19?=b%Vn0N?BGihsgR?qa z!1^=Gm3?*;=JHH&wLj1N*0Q{kh0wLL6F1$Y$fQ0Ge^)1~5#EsABakdPBwW)kS_mYEjk|iM#AC@p|@UK~%cR3J928zQn2Lj1paZ+ZK zEHRuVZrT+~KDLdrP8!7?f~y%=cht?w%LQ^?UtC6*V^NPYJ%+q2A5f3J^#RnHzV!0| z0I}7T9LOe=olaVXy7x!)>#mU_DWRt~YOF*ZI5v#Tum+IPbZ}gXgR>*k52rf^XZtvV z!*_4VF~Ve~uUB6eG78zMsHqJq=Zs06Nrx*bwtrk36B1YAZsLkOn`UfvzrgWIQ-LxQ zIPQuq1A&XZvK(Ymn+x0m$_yX!GgA5n00VkB1z_X_#$42Rmx|g>UgJy^Ezg)5{dszC zW_cxrs)lKTC5WV|_4VAX(rLIN zV-q&d+F(PKaFr>WHEyA1PgT(5Gg-DLv@lQy+2J$o0(H=I46!U73^;Zuo3VpOCmXbt zN)>Wr@nuI_DE%OC=zKBnJ8*q-b=K(^*qUxh%~N(DUvw5|G&R=;Pf}Iz@FZ1rPrSvG zJ-y;lGnZyI`bEKL?-vCCR~3c8?jLElS9GlS2w zGUcHlqb`@>TzRDAqWenaG3hjV^EM_5=xV?6^lTEeIBRXeM{W(Awa$Z=Oam8k3pgr<~QN02zYaAhxePg!`O{`;|t>BHZ+s3%81Zg&Q+hFq&V>b&a8{z_t1hcH5 zV_IG-RY_(A9ru=%fVq~naaK^&2=gMC7sR{;=P3dX^c<-IFbDP)g3&Gjb6~G2WX&Op z=JFI>*PQngvgQKNyt)iP^I8s|cY2-(yWHWjS7=z+Aoxy}8Kd#Yu;wkyAu~qfk)dB! z!WhLDM`m7Pz38aCC;Qm&oZh@vCi})CNBFW5FxRs-a4#F7Fn7VcKp<*X)iy^U-PUPF2zv_DM6`1VLAM}yoE(zj>4Uhw`tdBiOkcnYJWH%I z`})G_*w_he&Hj5#tanNsN7C*h+PEMxDq72 zEl^w#(2F>fva3*(juO%qnawI?m!SY82yKyhf$QD5(c`68a=E>enD=~`r}t&#hcHwk zfR`jRl3Zx?9+MLXZ6H#h3tdtCss5J)TlwCc^8%SK$gd=1n-UAbQC@-N_5r&;5Q)HX z$AkUcqn+#v#roDnx)^cAfTc7GcqfX1@~9lDrhj%nf$dc8C*W)Bb1wdz?%<<@j)6#+ zwkIG~;XJf*^5BjLI0fMU-}|8{AGsRgTyN>8d+dKT*?;f9HG0`04xIk%#$a{dJDFWJ zYSw5=XHB+x?L?Rvgx=**x>A20bt`9iB}=}mXWGH5Fp1j`s_-ffi@s{U=u0?Jbo)VA zDmFFTK2g71_CLETj%&=+VhRYG6)_q~ZY4nAkS}LlEvS;4%W5TS*ecN)I)J!EtTRY@ z3uXsmT(x3f!+}7;8;Si$7FbIx3Q5-?$<~NQi9jG3C|2FB7D#&gEo(%{B1pPIvgDBT z&e|OaB)yx*4g?ae&-YW()eeLW$KuWouQ=t!>7oUk#2WdrKEnY7l2tm7b(}MvXgEHX zYF1>>Ix&PRkBK4rYA!nN_3lCQ8NjT3@Jbdjx(d$SwXfmX-BWKj$@rilz)UtQvvMt5 zT}{3UoIMpBGr=;qvTXj%pRnK9n|^a7*rF?DKY^%xOQUx?yLaW>x=M_uRzcHHL6c&= zevaf5fTQTK9{_(0D;f!qRn13ybv6rD1fsOUNbC>OiFrqNwU8d}VJn$2V zmEq*QDKt#sH;X{0kqC3P`Qq}gR6{E$r#_*9UIuK{s%w462lcmiJ$PR{VgJ^ap4f27 z+JX~U_AGKv7>Evk6#dJW%->J{qAP_9k@=zZudo+?FZD0@A@nb2+K-}tz1ITz^{-LL zdcXc%!7*ppy(|{%3BVGd%LG6mjLlUH@Bw5hA3@LBUN*@GzK;rCNtW3_Bdbph37izl z^;A)StEnR&vqT-ytIVfe)s=-Pif)piD7AhaQ?(^$tqV(GDh2zhSs9PHdA4c0(vcOG&U_naQQ|^D7wHKUd^{xwNe3=>WW93` zr&~6oxiu~v8E>@rBe~p>HH*1*UJE2WF?Ap)xy)Z&?#Naaj>CB^kgO^^Na4s}64bhJaNXDimqBa2hMFmN)s9flLyv6aZQ0eBOK0efcaiSP1JyDZSw2Dly5D8CD~#Zw|U9 zo_#YQfUVH`oT2#N#V_f`rJ<2)1w$+4HbvCtUh|rSFLk`g z_>wpykm8qgEZfw&Sl@}{YZq)W=<}i6y0@22Z8WgY{1fAlbL8Vgz0v6Cf^})FZ%B~X z8xjl|h5v2@+*hPKyCbYo@FF&&S;d>YeEgET?z-op>rDC#5C48#=OcEMDg2ycW`O|5 z(iwg`7$F*9xur|!Sf6J)>ymYP=`dNFG}gD*g0UnDUgSee7QJ(QWvhK&fU7IUY_Ue) z6l)w+JiK;-pudvv6&!shK%XH1v9oBr*2Y)K__`7nfGa9l>zaBH|Ac`<@7fQ%|5njk zOBH)-$p{nq+?du5Mvowy5ngNv+G}M3UCbfjV)>sdi=Ze=1!!>&P*|86P`7o=NKBV8 z@g|lsh6eBxqAQi4gs{v?mLuM43bxGFOF#M$FF3@q^iGuu#40iE zb|uRZcb%Q$gh%(=Y@32rzvifcm~%l%OB%E;+lz2xw@*83`fN)=2l6h%c7&^7-=cOf zb|orBg+vv;W!DB>DQ^|&>rgJk+eL*Lvkz<(>o2{UQiR^NOOmZ`M8dadmYXP#TUzXT zWMlXE{AllZ(bI<#a;Ic}Ou#eU)Y<&>+d+>;`Kd_gAEy51Bgb5oi-ZMy> zE|Q$*MNFSd@%o7JKDJ`Sk&vw?4NokY&n{estm5Mfod?*;iK7=9lH@VvF8Oy!k@e@tP`3YWZi#nHnL*k2#p zIy?0a{?6IKn`ck!Bfs&;JXM7A0{?`w*KFRuDjgU6m%;X&ACD}7LD)S1a%@Xi$JPyA z2HS^&_>2D7*x0tQ?PEK}c8={D8y}k(n;hG{ZEV}NZQHl)*tT=qu5IJnCbms(+r52k z`?l@dx9`}#bNjCCS?NCRkKCxqB=ftjw@rj9v$%);QW0Tt^w@>bv+&Q^xa(r@Pa&mI_Zb;ls_q##8 zn`XNKp)_I4_sKkO7~b3#WlZ7&Y4ONjpf>`6_VCx%W~WY$ZaI*gIG#);2VQ^T z`1FC~k*T9+rg=ppiidJUC#-?tnJ>kqpSk(wn}62hazV_WnL$A5xK;MN)udhWPzZUS zD#b_biA*90KS;Cv^rxz$e~Fd6d>zLBed~C9#FUSS(7%}YOf?tpXNe=0fDQn?ND8Ms z4{D_?J!-#Fj^}-mp`XNlzdR|b_k-&EAaaF6Z1{e7Fs+r|kbZ|DK|iehP6fr}QF5_J zx?C>aSB*y#pqiF3((6bMkkZ^cNy^y!2L5bSpPK`upTvIu?;p76(L8-WdOW@Z+GEaV z3+8m|Jq0*DH(MycLOqTjcyw+$;SUT>zcb-;iuL%qBK_9X@{g@S$nyF4f~|GOFgM$z80+MXmYQ)v~49d9!bxx9#IQ^fm4X`$rd0FMWC-ZtVs4N-zx-gIpYlSLVh}oSiPjW@#WU=Mr;qAnxQ`=I>5E zG@U$nV%BD2TI^GeDqueKgDMx36Tnw>kH zOwEC3X)taYpAdu$ba62LNzV^UrD{2>MAd4oRbL;kZLDq%53C;KOlEj^MQyderZf_) z4cApR`kM%hZYbRt+!WvJkA>U9?ZK1&Q^6;L--`Zu^}hxGz4W)y_rgmxB%u{w2%dT8 zqwjtHKOKAXTMj(_-uKOK*!cer4Bm6^&0BAK+t2R*y}vyF{ttZUmp}PCpZ-^$|H7C4 z_%B}kt7vF=%eI{pdv1Bn-LL!E^BMy=IFu3XXi47a~|J=L&#c%(&)oZTV zbjO{qzwa$?ecRjLb@ul@|CO(PR^~vA$m^I@e>oTxozL=^B?-&r8hiu=8Io? z;ZMK*fB*aMT|yyO#9ttU+~0ZZo6%#ht*$SXhR!`ba7*dPu4`6zzZtHr`cWsWM?0fR z=vT^>R(${9aOI8FAlgu`h1Iaa-+~MIE`KSE%l^Qs(w=arQZBWsZ{p9Vu4>*N{e$qP z(2s`7gN;4W#%r79dy`|)%(dMwmL9tht}8$Gx8YA$R>f;-D;p~tGv#`DUHL858%lT7 zZ;Tp|A8w0pjMkOou=_Mco6>uuo5H#3t>IvJ>qNEs>hfckhDNGehi(d!R(r7fzUZ-! ztc_Rw;#--yor z`E`{>snq?=p>xMt?;L$~`QE7e{;2!eaD6zaw@p_O3YUGKAqPlyQ0M)rmOm5?msSj~ z3|0l}!VRU3(Pn=pJQ92+_|xDkjjz`JEcjaR=l)XZrQqAi#ptj7|LS9eR>!~MH?F_s z$6kNm2S5JtUoTf`s`-(d1?3;-}8ZwFPGBS-*;&Gt#AAN zKREyMzgC-=|IkyF`Yk_pV*j`Q!&@Kt*soqc`o>>d_>F(|#FI}w^Xbn##~+1QwQ9x_otGmBd-W+X? zMyoqxzdL_!qPDtT?S6E3c(7Ur4LL$>^TaXcR`ZYOS(2xjt&t#=||Ob(N@5 zx&Q8o9nBq;^0@<>&YgRGbj{#D7+71WmtWhszIG*eN($!dLkU5F_;ci;^T|D1PM^$i<$pR3$a-x%I`_e3~Qtv~jOcYo}` zGjaF1U;51Xtsnf;$L{=%fBD$%%B!Q`z;*RI>Z7F>2ZytOs7H@M~_|KEzo{^M^p z&u#jNbMJdkbfnY_Yn9gjaiII3>!+*7*WA(l^qXtL-4C3*GyM5|gR6f2{u_SrtKF|{ zxu;Z*g7XWvmi_3BrS&_5a|1Uf!0x>EsxH*EpFv`i`kbPbUvdCsT>5lDNNN%&T?rNO#clHsBog5}nFR zY~^3(n%BnzcTJss*us_%5X_@Qn7ArbX%ehIsT1?oT-< z`a9K*x?@vw2Oq`_<8D|S96Tcm4-qabZit(wXAf>Y{pjhr>0=i7zp#O5pUz+1hyJGv z^e=6Q%g0X~KRCUIh=H0HA>`)1n){2Ik8g~tEH1dS}6ehl;uvHpaK4 zM~^0bf93gfa{A#DXO13num`7S1LeEJY(>za7;Pxbx`HH00W?qe}cSWz7o zSG$|y4fjvYP9L939@f-mBk8NLu!%U6m5Ie99l&5Vrw z*#wP@ArM|7%YqjI42gpc!9frM#u%79Bye3BJdeNJ_Eb#+yBRdscrD{t52LErN{|C;*F@Yu0q{;{2b{jC>oXMw*3 zv(nq?1M<^0JiW!+>3KWLYQvwj>sx~RgWhob&~3frw@>YO(}7+4$2aWQuzAD_1Y98C z_?``$H?QBaaj!*2G8sD>HYGUvBj)}vEr@VryE2_R}Vtm{y z>UD`;Zyul8vFpHr!@I4+>O2WO85Kr!!=R2XHy0zmF7VWw58u9H z{f05va{i0U=D*F0UF8Jl3Tr8Js#i$rW{0XXsqJOJjE*C@J>n|Uvln#y` zJUnqn;Pp=&orKZjH}5^P)4~Yn0Nt4;GAPOPI*`?xR!V$m8(t{ocjrW$#lH zyADn6+C8=R@FDM#-kbIw+I3*>WY%n5b>iru9aH0zQGIn9 zzvI97Bh6soSn!sFilLy8%oX>y7LEjkU|aitu}@7aND9~Vc(I=p_QgT+EBji(NWiNq zJz#nXl2yOZ}wT)R&r<@TCceR z-DZl0u@n!i2~tb~fvFr9xYBD2F6!i+8?N5TJ2zaqdoohtmmIBTH}jj`J5e~u`i#x1620S!vX0ds?roE(97mP;$6`b)WazDwtJ(Y zS1&}aMgwHbI!j$s_&Exj!aG!U;Tmry=X=+vevjWC2u;r>mGRIP4JuGdYZxrkbS)<| zp`6w*Tv}sd_A@cNCDOrcTQ#6;bJ|!TTy2&+UCC5)CNM_ls-Fk|qcd#-0RR}CX}fi| z#SzG2I2+)_16u!)TmQ9m{d)pKPCc$sh{XnhnjRpIiGm&2o&eVjSR$cQ2Y7Tc`aS%mAF6Uei$!EpQ`bB zof4VJn3=Bg4y8`gbzbRUXu8hBb6LtdZ?EZ6ZU@Tk&hxGFYI_CS(r4Di4of3zz^qGc zOJhhxa~MaC2&7PVSa|AjolP<_grUUNmP%V7^U0!?i=dKoK0JrvOqAu|R4+504uI;l zbx|!)y<8Xb*#}zFem%g%0n-W;wP!DAgMrX3@-7WbVVMpl-HR2ZDRr5cv&Cvg#a2jH z4g9qQZuDj~Ms3i&SliaptxRM!&}oxd(%>qzC7R1h_-7r*+TKh6CfN3o0>H7{L#n$L z>ukV6E&oF5&lxjFZqHqsJbJ4@Kj+h}Sz%4+H5bF)a~ck$4c!Em8+#db`981+frGP< z%@rBNT{Qz)kAdzUrnYppDRuRzXFclOJxr@zk4jz>FB}SH2vrmSh!)TlT;)AQ7{#m> zNxIHpNYG*#@d_3;?1)!ndsXACa$dubm9)lLWz~quv%zTSoUij-;-5n}0{|Hny8y_j zlmX}-BBO9do@?557oo1Z0CX>R0q9=I0P0S}6Hfby-yEp>QQ|cRBm7?D0iwpXtCCr5AA%2o}66ptL?T{U16k3|=RtA(Yx$bv%&q%X*|1x!mTU8EKPjn6)ydN~EGUjYWxy zvg*vyR#|BoUbO;AtxcTq49Qq5nrd9sCN#4t^-kj4bb8vv86`}|lxxm2k@jK|vtb2- z<9!(NP7std@T)1vC_%+`GMib4qaQb$br1*~3vjiaAaIvtZin1eHl+Zz|D&i zfCuu^Y3v=yI-S!XiUW;|S+E&k1AJ!#G+DVIfpf}dx<_<0^EdEz8>767184Ev@dx`T zYD`lywanI~Xfl=Qs$fxQVx~J;g_c*0B6nCEJ>peTO*iSJ4i1mf2?d{a0NwTw+n-q; z>e}M>;>Dw31!iI|z1WjFFLF=X%Q!zE_$JBZ$En{FV0fQtrIgA`)~fcIj@lu1_^HSB zlGUpHK}T(j4D#Ajo}vpk0WW!sx6BrMFu(29ykyK#gz3_l-=mJ1U?WY$xT#bnPzWmK ziYR;``u$Q+>qt(VEbnhcz~d;HDKUe_d2uup+*fK6xZ*QQ%{nzF5Vh4O^?k_?wC38G zjO-~6rB6{Djs$Z)WqmaK9%b@9REFrnkXYYlz{}IV0)}SMn8H3XN~TesDER@cpRz?A zQ3?V)g@hK4qTHc{rV*7Q*&61Z7|y{n2yY@mNEzAEE;XfHLYH@_k#>>D{A^$4(k?Zq zYnATbe!_oCt7m)sj(dC9n%~>1CC9GoDaAD+or$T4|+` z;9EXrWh8jahvFIwj3%di)?G<_3XG`AzGj))YOoEHZy*HF8wYD(-FE-w3{p+}ea$L$ zr?EHXe5209X*hCx;$t9q3aP|3 zo(>2|Ro=|uqt3?(i)*BPNMK=nV1kV=##9=ibgyFbTmwi%8yz?c`LjM z<`tSn*qIEYT+FGE6q>dN3-lh7;iEDC{lYTS=0byKT9Fh;{HdlEe8xdmlMj=aRSCYj zN!=4@F3`;XKj-_wv3%`j6tXDx$H5J-1np(>68qOc6GMU?E6)23%UUS()je4xX{$b+ zhaaa?Fn8oba=?-;LY*FsdYZ((Q6I*lp6*c3dT?@2sg97qY>-MIr}~){!IEQPo>rX> z_qUdw!|ZHIDotFiUS&|dc&Y7EELgYASVsocRhjST$e=o?`HqeZ>QaIo%Vz{&)b0h0 z<$N>fKwxTTj2s9w?G)+Kcqv;(wym>5BA*PBcDmGoK+;Z^IuJOeBBiJJ%<_mptTddvcXl7T#{gb2(Kwif2; zB{y-2fF+lwnzVa$Scn%gd=l2IV-Y%=LVNMau4SJ~)WuM2`m=nFL6~Y=v<87)^N2WpJ`#>iDH}OW{kb?7oGV2W08vq!^;qyM zmA6!Zf0oV5*)7jU^1~+sW?2sza%_#Q$T~e*WdSN$vVrXgn#(#@Zdn1JOFwoj+A`wq zf*J$L3i#5t%;#saXge2x)-keZXD0JSn`Qtr73o~Gi(tcRS-Xx6^<*f`7-8vC{p!qC zW((M{^PjzJwXMq*%fDdRW``F4?w4)*Nam+6+iX%lZP~UZ-xm1%a)v#gS@^}dOdSYi z8SGffYK7u1$<1^w<(;hD#i_(9@nHH?jhAFnl!@B;f>$nk36@jG>J^}C1q(3WD&7|N zXoX@9QQv$;63n+3Vu_cI^%rF8@A-R~=g;9qkb~z-7g3PJKf^`TUOR11H@|~x3~7GtMu!?HKmj8OXLlb09O;=UO)Z`M9eYY#=`Lh4`y!BdkN~ z1?bXx0p`>CX}p$pjwKuxC-?vxXJMUsEyOw$gf)t_t5R-Y=@3m?At?;%*~ntHo(h^P zh=Cwrzhj`U%9OV&;^lF;tht;yR@C{CK>J&Zs2lyRL{i#BS@monKEY;oQwqVCkCNBm zSkUOSU>#kn#1%Sd2WzoMG49QGu#5nTsz%eqg{E1Kt4-gwlVTb@Riki4+ex-<`Y z^`7$fw_qMuM$9_Ib(g2)Er}bPE-*d)RcUFhqp04%h^HI?Q}UKnFjiG5O;WPs`rQR$+PoKw#z z#92++SkLp%V^haG$dKj(+eDCM4zQV!HO&Dw737-(Y%Z*n9AJ~Vto?428Oz=~C-dy| zy|RP8(;c{tK9nXO%;|Gi^r2jN3nP$xZwg2$B#8pMF&!uY7SS|h z%xTClZ^Qdgl7bTf??b^@3lUUAx2C1UC#}tduz=Y?ss*Vzs==v6P4H%~sv1_+HhS{B zNv8XyJW~u%P}7hDF(+cS)Tg_s!XC@2hWI6&hPSF2sSw92sU;ggO5H*7LcEu45sOA8 zNiL%xC0=T-?3$xAHbn5DgHoPzO35&aQi|0S)Wqhc$Q#yCS+ftZsA^U5U={!D*J{G)W1U(urw64Ug}EBI7mg1euiL zl8qB*j>$&a)~aHOXuwDe@zSPGH?q%gK>kN^G0#mzotS7ANvoe+c?$eYf!qi&l2K+a|AnY*M!&yog zR--Bi0Zv)gG(<;=JwAaTIzr@EQlv6Kfo?XO#tc%h^pv4q=u~77mo48hh*;4Wj0&<{NmhWkC-XS4mv=Y|RCa9i{;-;MF`+6e!YG3Scq83&Cr#YzSV7i3@L%Hn$6K zD&|B&=Tvehpp0s#6A0zdeg4YZo2A1%W&O?2apNR=H#>*IWbbC?h`QW+(Gg@y<4L*P zlb!ar$_g5Y97n?#c9k%8PLrHiYF^ATi-SfvD)HDTx+07!R?%1%-52@2c^V&?_0c`K zpc$FCl;3W7$%)53VnsnQUwwp-F=nBXyk#H32z9r%s>B1gk}6Lg3HPn@y-Pg>k-x%H z1b(O+9szGE#y|51Pp-R`HD@RewW>|}WXqAGAqcSUSF`5bkc@{pGT>Qb*)Z0{I#plR z?sIHki#TqeohmWjYp2g4=k(b{Gzi z0~Il;`+w?GN$faEL9#G#n^%UYSDO(I%8n2um*U?2P4*M9f3Y{*LKI1O<+3C^0-~JFC~I>;IQ1-4m|GF${#I|8 zVi;|Ge%v{iGo_3(*@*LTg#kU-nlU;VhS5%Zia3l=6e8tht7Q8k+kGO68xXi{xKMW; zCq*EV@|b+2T*wE(AYit{1>?F9xQd>T%fyUb*Kr@JavKNI+JWk+sEWuDHDn7FYz36( zjCZY6KlZkIL&&?AzE}pJm-8YE;(Giajnhb02a6P#EC7( znXxikNFY9I5s8iN#Pm595G*R05Je(Gw<=nHo~Sosi#M?H+xoz50H&1_tHvlpB%dfl zrEdHMC1%lMuWhpL?x z8Z?Hu8;=tI@7#*{7*>{rI6t#EX(p!^Cm-~a6HAh()!5fXdR~8g6({dMj;?`Se)5;^ zn%bBAkN2#-mgkS($MeSq_4pTm$zzY6esboFcO?1P>DpDieCqQ@_a#FD|CjgkRJ-_h0RBosZ%&oRr;#Q6@+`6mJLF9h@_K|xLIs9 zh_1A$V6ruR&VVaAwnoO~BMFX&L+on-{W*IQzNgft;@BDPdfJ zm12Vv-#BnlL9{xHbk%W;udI&sk#&$V#pPunD<{De%0vcBF%zIN_=l4xwPEm*ZR|qb!p5jv}dQZ)d@~|OJx8|r~ zgT;hLDc~;^Jj~}JJ2~m}m(bd|%*fJhal~y33Ci*t%!@c&7bPs}C#oY1u=rWdkC;8C z7?8^(V5f_qbWu#EAiS|k(fV@p=vVg0<{X=J3sR{tda;L@uPq5T^gOjeFrM(w-9vWn zk~PXD3Z3GMIzuHAgSDR5;~mw);sT?tme2t-3#MTDY46AcQ4d%q zQW20GL}YQWAK|G3Yo41yCT}yFy2v6~O3?ThIl90JOyti(&a{6e0Vr@*g-uSf3ZGL} zsI^GTR1axJ`{SBC9$@BW`;r$nA`?Xcz+#!_`6D@OfpVe+Tq9~=<_!gySDS2Sif<>? zz?|ge3yz^?8?@z>wwn;ToISkU>d8dZXl~VQXD_E*m9sD5?28@snX|890Jv%8oV|Em z#MxJ9C=un(URJG!S*T{tUhzQ@XO17x5{ruy*d^!eOPRATQI9dTdYrxNket2MJ19H+ zpXThj*hHS$NewfMX`A2S+bA^)*wdIw4&U4{adhgA{cd+myfdFW&QLvzJ65hl+eS|o zF%Ge|UIX9!zR<56GX=Lax=BbD!zBT55FRh=>O$$O9ra7&V7QZLD6>)3^&+m8X|b3P zOqy8oj^3<)*Bd-q4y^x5TsGOVO60t>dX;Q4B#v_qrLzqPB6U|#qj=KcE2hZUNP>(d zTCr6;0Ufq<2yBGCB(xxxK`_c$Iz}&1p=`%(r^zvP%u@3EYk=s zzUke94_!>_!OFyek+|&1zm6twe-pi` zq|gKuDB=){-^q$}mbL8k_+fT#cS6H&(Oub3zys zoiMV_CIqlDdW^?YohNAFNmMZ+Pa1nCB}wXR3Qw{AAS@wy#2k{l$S*>9N*zGP;`J=* z99)e)P&MjcdrMptzwu&ll=PM#SN4;(y0Yb&X(*etB^!715aqO09mqZha>#Vm5{|uP zBpJ+1pp!~&meS>HyJ-vp+GSl4KD`W$GY6~zNKu@t4XETT?;|IaVt|uuD_QhKTl5gA zH(TxsZO}*}4eMA=dqAApVk_E9**Zmpa@XNga}Vs=A>1-EvUvNT8m=YSm=5 zYMPAosi^=UD?+4(XmogijZ!*Dp<6>A%PCdqVpVIEkQ^d;zFm8rX+E;y+a1LcT)|Nh zJ8VM~3^wd=tk%(~u(@HV7~}Oalr*vBR7;-W(#dteo}#=ud7!W9Cl4OWNEk%3shcH5 z#s}=&*3XF*8?;(YHRdxR3Z>!5gBdjl z5gL-xRa}PG5efh2%M&c0m)vcKPV_0ND>sNa;^2vnA?FKOH#|??Er>dMqVd~_zgW@= z;h}w6 zfn!_s1~z6kULdx?TZ~916LC(Ls4296!`V2QXoI*~w-9vrEh{E^)yWjbHp%G)ebX^h zHFS)Uj?V1Yk|B{{QHB1J&aV~3X_|H=*dZTkWa{{fdUsS4``pxi0N==A*o#=ezgg4* zPTq}vkZc#E{W#(K`A(SxI}1pfg-2u?r-dw6yF4{0h*g?w#afDYaP>v|1vlp*%4r}C zgUDFLKvw5{QJ>HB%AmZ%?;aEH$P#kr2Hc#tH}qY0#nyAEVl^#jI24DhXANS|x}aue zV#+y2Cj|lj+I@MIEghs}8Yw>1*b^lWbbci;i$%kI7R2ki`zWQtR#%+IX;HvsJQal5 z-Clhdq00y%p^|_&Nqi+m28=y>ie(UFqU@OVG8#i3dSG7k4XCCN)f!t==P3s+RF8&(0IV_I(JXUvkMYt6 zF86`5#eB9DQ}7As5QR*cIQ7o6WM)z_1wpqpKY6_lnPIvlGwFt$fONx&%_^i@)Js1^ zfOM=wX6oc*W~yg0vj#uz`1m zcqFN?drruh^RC^G4y*hm&}T&c#h#B1ahYQaS%45$5vOAg>epabYO#`aD1dgH^mKu< zLSdLsW6)astW)7g@x5j3Q9Pw7Yt&ZJU3M{~e6)Y+g6Ml21A)z z*T^l!s2%7k7XN&u5tk5B^F#>gV_r{Y!_1brDbbBd(=a^K+JAY@* zg0cP1F)7*0_PdOV+<1nMEvzn2rSDvZq_ghcK&5>YSb7%nhC9gXyg@_C(7rp&2QUIV zZ_pTpv-$lK9oNvM;|7h9IF^y5%O%ur%HBIk^mIL_Kc}?P#Yhf9`gKJ}$Kf)2niCx= z^K@wWj`wGE!Op^lq2LW!3YPVv>?i(l)wW?4ofyP{m2E`B!8xapktCW0iC2vOfAgTx9ajVd{ z5H^XuasR7Pe|2FV!}~fK53${%@i3QNE^IE)7}nueN5flak4~`o>=?U|JkAU2Mk>=t z{)89Sm=3rby64vF$zJ}HmtIX;H7R?Z1lH$Z+%^F2IFRx}^VM&}I1W6InLjPQY3lI; zqZV1*f(#2_kJ};AE_phfu$~0vxNKC8G6jL$+@^-ZGZ&hkb3F;8E$7oz1@5HvgpCiIH-|bjd7S${MomN>DQ7o!F-=f z@^Na{_mZ;(U8dm}LMKmhJpU>mOuCOhJv_$$2OmtH^~owEe_|CbtbKKKhToylglS}DGIU0SPg2|ra{-I#TgwvnQ1Zqk9%bU@1^BugXD?j3BP)w zF;HuV8WZA!*SjxhEs#R@C$oGuZpPKUt8&IF?+ckPz}bQZ9trertucP09!&>3pTLKl z6xwHKZV2u{%a`Y0yErHggbx4}pt~>4d|vPr?xmklTTeEGSON|UpMi-+fk4p6zYBCA z$R@v}kY26yLEU|nEUuc7btaMhL2m}(0Sr-T-wfqjVA<;eLY3}XkU-UKd>(A{^ zGOXpaewUAsJ0GPlM^5aP4I{#Y(`F|XfUppYkkV~h0bxPM?9>Xv*s^A=vf15@_)H2Q zVXKu@JHhDN)*uCOo|jds@7?+UM3S6C-t_Hle{^FY2&nU8A{k9;1pz(z2XHpuS$?Q% zj*k`cFE!hr?zDf9&sy^1EaD6wKBXvetLYQzzHFF`Gfaxabqejvr{JGE1$bD=S=atf zlr4HEPN@MBq-_di+nqiUwsy9Z1Q{Ja#H=z3u;ghQ0^-irV&V&ucS$YBR%Nc;E! zu_WZ4Q(Q5MK@QEfc@}ldNR370?U*4*OZY&?ZF7jN+?l&T5Q@$@6q?8~ z_1TS-xK9>|?HnkS1)owp8JGC(zoa=}dbEQp#RYAtN)4%_ZjbVxeb$e-2_;sXpAr<`A`2xy6+CDkljJUhHM1r-*#UzWeS+QRV3|R=Prk~T+O}m#4Q*~d zTnj?G?1TAiJz^11SKU6eAH3k%Zj$qJ9b$6opR^*~2$_Y*+!REnLv__SQmrQc*2hLn ztx7&|bS>{FUSOKh;4YA*A5#tRGUQ)wS3T5z{7U=r;r8QK+mBysKYqRa2=YdqNu{fROO#!bM;Xr6 z50pV6nW*CN9Ov4x7O+sc(xfU%B$B!4Y&{T(WWKs%MZ^SkDJ5hi)2CwYDLXZ}O>>*n z#<^bK2y=m&Bg?eYg=ro7D*)pQ zCfWyX!%6m&h{SFPQaNZ=RhVB8R(JR-n)~s*Ak478+YGp?aK@aPM9pUGN7A;o@h`Qj}lj znsG;>XSTFoU+A2>MYlsMVk>RYao@=dL2KAJPl24ygzs}+Jvm9RZI%2=mLjV#Il&G+ z6g-D0=_7CsuqOQM7(aMKeok92}IK`bo+-Sh_UPdS@q;oWU zWhc6fRMuXKE`vC9z%gaa@?@waE^QtRO+C^Kk!f7;!ZePvJ;Na#2oPW#ZVv9|GKtC) zoHU>eAs3P?d&QoagXp_Z2`Bip_}fxY>=50_LYB=sE4xy1*%b~1lwBz)daxvcq^EDH zPS`X`$uyF(+LTpfqhW}_a|pQ<1LpwOi33l^GVH(zJ1ZJtx8H5$i2vU7{WsF`16FSJ zKA4stN=t1RWnlkvT7Eb!Ka!TenU)_-%a5hy$J25qEkBW#zm=B1otF2cE(-@BPmJ!@R=d+UcBNi6Z>?BX1 zu9&!#2n2bkXf}zAydQx%`IgppR+Bu!TYIv^W_gHZbpD8~1iPL2w909uxSVN`(>%$9 z?-8Y`ZEP*n|Y@xc=-b0~Z&2JIR zaMD+s&sq}Gt8>k)C7Jf5PMBX)xK(pGO)!_d#(;Uc(dp|fz#7o7>HX_4yo z5mwQo(OPw=kkq5Vhag1-IW#8spnC!|H4!Kg(#dvCT3?7e1jz@iyFUK~mAMS1RGR45 z3q$3ui90mfN!j@aVynRcC${_JvaY?fjVA6BgW35@2JwFVVoxFFTzNmYTTl20oBS}R zee6e!(}YC#KdzR(4~Oz`-As*1tnkY!y-N4!`)bDQy_YMO^d~o=uax<16@AN4OK-We zF6~xc9l4RV3H8fGtSGaS{0zVQ@dS>E_%2@Gv+1@KxcKb^_|A32xCa1IQpDB6>QrC! zQ+3vtpXV3UBR*P7Yx`8Z#&LLN@RWpoD!Lz0gK4t488qEM6WRuT6G32&ZYd^7x(H6SieaM`&saW>^$k*IPF43 zoBz72=2}$^3l~fs0#FWc34-J=W+0a~FE{`pEjwvzAYvz0h=seB9^no_GV8TFRd`DW zaXO-3Qt)-CtY!pWd?%n^u;VT!r{bnPrdcuuWTW|LQ0XBJ8cKG(@@XMbCVgiYPi`317Im^5rR-!6N&=kF1e;~QBXV}c8M5%A&$f1<;Zb3ZOFbNVM;Gnp z-+&t56Ysp?R{ax4TetELBu(tPb!g4b`0$~4SG@E3!-vLq#<%S{aCF>bMp6HPy>Hrm zcw&6rVe4?o#H4=SBpL*rcM<<><3GL>@NV!uZy}|J(T(c)Ka+IHC6{=Z&FH*6k`cePxUAOMqy?5%4_%Q!E)=dWvznSstn_K&EoTJ4D-I*%#Yzl61JV%MAbH@9}%r0xl#!EGQD(w!;N zcDLDJUP|Vk4cjwdJCR~Lc@DM{;k?*RhR;IVBVqKIKRi6Vet5(1#^FuFn}mXa9Njd!d30oSbaZTV%h>SP z`mqgT8^<<{Z5|sL8yy=P+p+}`w=nz`P;a5z7C;_11Ea<%B(eZjUtVx5_qahA4W0%n zt%_0JZmoKtqt8bAV4aZlsbaJsQ_!0ZPOUq5c#jG5bnyjh`b=qFO`k1A#a}{)f0?05 z(cq~fYl?AaOP;rga+ng~Ldesl=zu-(5(~l~=3sw1OSRE|vDG|#8~E^?ZCC{5sBsc{ z#Kik6nR-7(+WZ7m^#Yxtgj3#0N-ftmwRcsb6TZmMpVc2d;tr?vTM2pkp*}>X-sw_;VAicPivRf}6u0}(dSmx+(KCK$GqC3H)=kfoJi9c15i%Kn1B6l{PvhL|0p^? z-a8o|Iy@Ecig(|#Ya)Kr;iHH4?3%da(s=Kbs`eh5+`DI-wkPUQB_2}uN#17aQE5$l z_@;QLmz=CeKE-VP1un1C^?8@qiJqv}{++(~vX{i;hxWun4A&DC^vYm85&42Q-!gtE zo;WLfbjVe1+sK>?P zX!Y~a1dkiVQDYnZq$zVKJu#=6bltSjMr~QNq`s(ULGPkIer|lhg7d12{Kdt=U`e>Nyux3#C=M?wt_?1ZF7b!M z_2Gu#UjM$}Q^BVTzgzi-;2(?sRQO4FriwN$h)xFg-+srt-t$|-uX*jxcf9M}Czh}H z@4bE3UU$j5%ir+JJHGIjcfIGm@BfWYeeR*JeB;q?{?T7N^;d=d1#8xC8r^#7ORjm@ zFW>cEsvdagE06xs7Ii}{^I++eX9-~UcO?-ufFB?KKl=g7Oz}&hE;f-%O_Jwae@!dZ^ z{WpI%G5Nl!qaXaxH`nbr|B`k0e)daW|HE%T`TY-X`_SF%hTpg9%x8ac=J7Mv-|+fh zDp&d%7p(i?UmrR=`jX4HUvc97KRNS?n~y&B%~Rj`-hcg{KRJW59EctzTHVpQ^ZSK6 zUs_pKEcPG&Oz)+|{hL=Vn*M&cq~dc9-CpuPv%xS)2a#((m3=UtC!KiNf(eJHK2n7N>uw|M+~%J{|lpK3(`5|Nr_Jp~cY${rUx${@nF9yzgTl`*^8b9eK&mz4~uY zeWx%m`0Ceub@Sujo?Gy1?|kpay0!HB8}^L9{taLJ(p_)+Xm#|&`|m5)F8#T~JAU}r zuYc2rKXSp)t3EpOiQm5G-uv!<=;25Bb%}*5wqAb4FMjggZ~gI1dCAh&i@)(`A%4+| z&mY=2w)N_lU3cRvUiE6}+s@tNH|?Li{a26u`aPfi?8(_v>n_LkEs35E-q`vN#SnRn z7yNaXOy9X8T;I36u)4akcv11Ch29INKV4c~SX~&ZY>NEp6URrZi)xkWySIe9E7jpe z#b&sy=x-YoBre*TUSK2QqOr~mD5wS`#U|ySN-DgcfYf+zt|I2%Z>lIbNcUV zla)h@ubh79HPr>v?>&B1__pnR3*UC*MZfml=_l7*TdWm=yJjvc`Gr>%mu(7;_pUAM zslU3`6TYGFk}WHG))uO0W@-AvZ~bm0gj4lbT~seXcWJ1y^5ylyOTy&?gF}Ab;H0=Xv8@ z2j0Bvj>-5<;FW<}I{^{&ZVERY#La&*@L z`~f{;daUl~u_f>E7`1auaj2s<$!q6UL_@D|{~~d$e_h#8cW~F#?ptufxEnUiyN`;( zJp>jfRz^LO6T8<<-Z42fe$ciWGb`D)K<0%V=wFwke{f|~I&}EZ?(wZeF0`Db z?!EcY_#Vtw9Ibw3`(IOru)X6QET#)thP~qIsW@7G#28|lT;r38_=G+n#WB%DB~A2+2ScJhs8O3}qA?`;N@Akt&g>3tVm5p3opaCM z{m!}HmAmx)f9S#j5vadM_MHBB}pQj`e7+0eSRy|AWdAohWbAy*@w?lve>DP!7hLho6Z;^6mo^F-j?wpY8c8yTxKeKCq z(#`VH$W!zo|Cf=qlrnjD_YgJZ=KOs$A&dF37<3H~N6sG^lsEI44ws7Nn8iRVT1@_! zU)yx`0vX06U1BNj3Ff(p*0)-g;-MZi0YI<0X-F5~(rYft*RQ%1s8~PY(u|Yt0a~<@ zXv{pZ#={e5Kx>6XjpMll^yM1NV?zQpJjjhTVlFu{39Bt2k>TGYsK@|Yz9)JsozcOZ z83biutx#dqI2dh}3VLo>n=`SjD|k1oD+a0dX`sTwlE7A|Fb0?F3eb#2>P2)$%NQhA zsL(PAysc8%uEdA6#W=RDqopO#j$+?)R+fus$sh%^Wyqpi$H5MCh*+1hWWfzOqr$DI zUPJy|SiPzZiXLH+07E85dyldR@^}FCGNgl;G>S>0m^Q?hUue!$Wp=j9l;=lRHQQC4 z0|)*$`SJjW|59AbL;7mbBA_`s$7ZPR1w#_f9B%KmywzDdoI3@3J4fip4tuWEI>ZL7 zX!a6<8gx4x!h7fjV~L??3tKhqXoLPXSTZ-6Ib=mxbO|+eXuRCLHHgdnTdg?7R^a3CDVJx@R=UpbHmPo1s1+=mk7ginamWo zA|D(m{)KYanBs}P@fymv$8+5{Ax>nU0;24{AMcAZv4{sUw)aN5TO}bXNg9n~u3ofO z$yF{NaF?|?F!}O+ z7w`Y>{;Md*C*ES{$FV@u|8*imk+lAu?5F*H|AD$-p}c+Y3!0Q~9`bO1_t00! zZ#NI`N0~TMMHakxwXT$?emT>WL#@T?&d{*q1e;%DfzMMXGo^JMkI(C_2T<`H4 zly3EVpZrkw?`*to`)^b>X#TCI?{K=s|K#*HT60d*w5Il|AVLwZ$W6QYcFh1?knO}# z%MOwbzgCA|J>k-;mPg6`;j$hlgW}floD?pg#tk1NS>qb&A?oTg+$pW2v;aD)n!zdA zu0Dmz2cBbOsg_qi(EBw|Js!gzD(2Re6?m$jn2-o_!qoykMuOKcvJP1T$TcLdm||{x zhRE8E-a&bRWDPDbipY8Deuf=_)}>I?3E_OfJ&A+Kjobd`)4w@If|&B%^+s*B^n4lW zXKEK_PQg^I+9*}3P=ZtCsoJU1$(b^|P@1_=hRSJZoGH)ZrVjPm*)o)?joBCf18`(x ANB{r; delta 1601 zcmaJ>U1%It6u#%q?#^r`voo6v=4WGjW*TBPKi&K_o4?xJty|HwC@Mv$8e&*+ry+?Z zLLZ`=C+S0x!Yvd;H26||uqKWVN-5;Q7hmhELeM@47FwY%VnxrL-C0`%ciD6A{m$=q z?%ng*1K9Zkc6Y&qq;AqjPbRs%0jFn&+A9|-%kOMf7B}BsTHRP$-mI>zvf<1d)zzhy z>PBVp^_8{d^XzCk157Z}7=OI02Rpm_TkhTpOZ;rF<1hqd{hTS0aB}i$-zbd9@B4lw z`PCD5NPf~^C7I4I!l10?zai-l48dUd+`uIWUs?FluH6R?HU6HU6cj{^ocH-8Paqd; zk~wObWp4fSSpC#pUwYIs!w%J#!>DTE!U>!VEf@$RK%QrvzE-6AxH{H%3Uw5U$O)=N zS~jVULgmB2Nq7{hpQFc0%+rvUSW#1I3=o7tnV8TQa*o^0=hc^v#xoWr$!q8(O5ka& zU?MA9B*D>4K`Jp4C{~FQL#@PK$eCA%c~;Su_XlU7APYkaa%*S~^zhrEJivhbW%y+n z3FnS>0qF8s(9nI}GUTU)Qyo5Fo8sk0TCtN-4e$;5XQ9YF zCZ8PXsu2t2DGus7DkD0pi=nQ;@PM$ZS5aI;B-H1oq z^usF3okcB1s5E|1OBK06=J!|>%R&~bBZwj-&~8A0*;uK3jUrzs&uu;O%%Bx3BZxe8 zOgsWn`V>M8sSxW$`R1r~s0A}I8uf^LP1GBZAwL?$T1(SlpBhC5EoeRhQE(HD7*fG^ zh}wRN8rRxDZH%L2|8-WD`%x1zP40S=a;ic3jJn)9=WQLWX1$4KnSiN>lW*&)J5IAH z>1Ho91bK0+$I>yrn-pqtZ>+t>9gV7uTVXdtXa5`8pc>+v`u_`Rl%O$elt4QwLDfpT zh}3#fQ%|i`y+KS0bW-$uD-+XZE4tTwThvZlmi&C;6g~NS6MOa;<+fNi}3{6xT=&PK~td$egAo7!7->Kk(2z*UK{+ UW2X6=w?rK`6PLrkS8hB105!54ng9R* 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 3329885da9a60eb0dbabb75aadba95405ccefe2a..f57ee8a7750bc3f229d46de013fc2d79dfd804e6 100644 GIT binary patch delta 1887 zcmZuxU2GIp6ux(6c4u~PXJ^`O5xN!54h?k6U$M|Czr8J6=%1wmQ9>fwwhQcZyNlh0 zU?Of44e)J5nAC&^VvLv=eel6F(HI{{z&A-u^xT=MGO-5!7NllO*F>=aM8^w3_>z4cF|tzD03@6AjB zSvPZ*?iJr=zCij`_jbD1yW4%8w%PKtqS+hS_q)Vz2L|XK(Rpx8%pW{N4eyhK z8A^M^&xc;2yS-h9H&AMbk%3;Si>m|A(3tpOU^oIT8Dfj&scwLBUIAT4}3>T<(%V)bu%EJp)5C>(&^agvZ6gllg*NZ+~bP@sI%m_t)`TRYIK z8AoNqpl#C}DnV%n@UW_r_tJ2_#f}Li~(0u*6@yqtF~) zEb6{f1I#rd^cn@NA(Bon_A84zrgaP7hIvIG(YW>OIx)&NBfv50{tP7igwWQ34MeW+I) zcakHf(!BS9e%scv>LFHO7%w{4qwtVAQ*0jaLk&{USG)~<# z1Jk6Oq%BUI~e~HxB$`?7`6(QW0*=)oZ$>KyJ@pYVUWN~4BR%zr7Z@A z_-@p}_dg!Jjr7B@_YJgTn4s?Uj;AOtt;vZly3M;XQIXs1(TlxEVe)Gl7r#!r$d4Sq zkNYh#HH!4~R0+4>gQ+t#c_YO)+nt2*&r|kRJZ_}}@ro~hs(J^ue`a~V6gm~J{mdgqpY!zdKPt6FMNw+Xo1AdDczMs9qxLBpsGRf> zTORvKGWc2-e6MS%R3d!PiP!f7=PcJ-JRJ?W@nPDrs>Ayn`vpYb(3uV?WUV(^VTGy+n|UCCDagOgiK=+ zlL&%;HiC+WAjKzmv>-GWwT7`!4{8oQcnEmt#gpU^P&C*g9xRCXz1f|$MR1q>-h03A z@9%qWzd!$i+jroL8(>0QH|e9t<6M3Z>tp?Gi?1#&yt27Cx4AyQvN6A~xwN{%@~Ib> zR_2$NHWufeUtV3f$Oe)rV1k)O0Xw@-WFC01>7ARw>wNj7<1hqdJ)9{KbGqbDrv_j| zcAWm1B=0#Qd8=oM>%X+^4!t7C1aP)DJN zoSpsu74K`N2=6{}d8p;l&3Wz3Iyd5fYgCvznjl+|2C?&roq50brEfL=M& z_Y4dL+kG7Xy1ddq)Dg#4aj5y&ruej?UEl{Ark}J7`FsC#y9d~+c%?(FxSUW8@C;eW z7rD#i`}vM4u~44kpr)fT!n0Zq5;a_L4C!@OJX;Y&d&)|Z&hW)C-(%=xc|8gtj_=v^ zc(hI4t)kp5s6_~s#t*8AA~(qVHVb1}$Rc$JQG^)UbqKH}QYv4g$k)hoOAkF$Xhq5p zB2OJ-_dt|Bfe=9|#9C2aAF#TbFcqO;kI2_Vy$%`jXaK8C4TC*u6d5$3c@IRvO*A4% z1;0$x_7c>%<~nL)6s6@~XJxq;HX+sEt~V$r>Xc{H<<>ZF>1Z|TjWx;yOx&G(OIO`- z8cj(ydZ8i69|k)u9izKRpeA9ct;!vZs*PG(+GxH_N9HulslQ|{zZpH1(8V4pT9Ye>qC37_^qPTU})PsHOSe-kS-FD{g4q5aE@bDF6* zG<9g&sG)NyBTmTuN499V%uK!vy}`GW+d!euPwxT@27f>LxfWC}9GNg2EHB;U!R+iw Wp3)dI&0pPlYPcD@6g=_Lq4N&{iyg55 From a802b7eb43e4ad593dfec42234f79caaab6992bc Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 15 Feb 2018 18:02:35 +0100 Subject: [PATCH 12/19] Build fix. --- substrate/client/src/client.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/substrate/client/src/client.rs b/substrate/client/src/client.rs index 1a15d488e418c..57a607706a252 100644 --- a/substrate/client/src/client.rs +++ b/substrate/client/src/client.rs @@ -18,6 +18,7 @@ use primitives::{block, AuthorityId}; use primitives::storage::{StorageKey, StorageData}; +use runtime_support::Hashable; use codec::{KeyedVec, Slicable}; use state_machine::{self, Ext, OverlayedChanges, Backend as StateBackend, CodeExecutor}; From 3928c1c5900597b858fe547f423a116c9bd91ece Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 15 Feb 2018 18:40:04 +0100 Subject: [PATCH 13/19] Fix tests. --- substrate/client/src/block_builder.rs | 8 ++------ substrate/rpc/src/state/tests.rs | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/substrate/client/src/block_builder.rs b/substrate/client/src/block_builder.rs index 8c2d0c06288ae..097dd670dc9bc 100644 --- a/substrate/client/src/block_builder.rs +++ b/substrate/client/src/block_builder.rs @@ -21,7 +21,7 @@ use codec::{Joiner, Slicable}; use state_machine::{self, CodeExecutor}; use primitives::{Header, Block}; use primitives::block::Transaction; -use {backend, error, BlockId, BlockStatus, Client}; +use {backend, error, BlockId, Client}; use triehash::ordered_trie_root; /// Utility for building new (valid) blocks from a stream of transactions. @@ -44,11 +44,7 @@ impl BlockBuilder where { /// Create a new instance of builder from the given client, building on the latest block. pub fn new(client: &Client) -> error::Result { - let best = (client.info().map(|i| i.chain.best_number)?..1) - .find(|&n| if let Ok(BlockStatus::InChain) = client.block_status(&BlockId::Number(n)) - { true } else { false }) - .unwrap_or(0); - Self::at_block(&BlockId::Number(best), client) + client.info().and_then(|i| Self::at_block(&BlockId::Hash(i.chain.best_hash), client)) } /// Create a new instance of builder from the given client using a particular block's ID to 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] ) } From 61db63f69bc84732459424d4a0443729e6ac32f6 Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 16 Feb 2018 20:16:02 +0100 Subject: [PATCH 14/19] Remerge in changes to client. --- substrate/client/src/client.rs | 120 ++++++++++++++++++++++++++++----- 1 file changed, 105 insertions(+), 15 deletions(-) diff --git a/substrate/client/src/client.rs b/substrate/client/src/client.rs index 57a607706a252..8efc326310dd2 100644 --- a/substrate/client/src/client.rs +++ b/substrate/client/src/client.rs @@ -17,6 +17,7 @@ //! Substrate Client use primitives::{block, AuthorityId}; +use primitives::block::Id as BlockId; use primitives::storage::{StorageKey, StorageData}; use runtime_support::Hashable; use codec::{KeyedVec, Slicable}; @@ -81,6 +82,20 @@ pub enum BlockStatus { 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, @@ -112,7 +127,7 @@ impl Client where let (genesis_header, genesis_store) = build_genesis(); let mut op = backend.begin_operation(BlockId::Hash(block::HeaderHash::default()))?; op.reset_storage(genesis_store.into_iter())?; - op.set_block_data(genesis_header, Some(vec![]), true)?; + op.set_block_data(genesis_header, Some(vec![]), None, true)?; backend.commit_operation(op)?; } Ok(Client { @@ -203,15 +218,36 @@ impl Client where 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, + }) + } + /// Author a new block, filling it with valid transactions from our transaction pool. pub fn propose_block_at(&self, parent: &BlockId) -> block::Block { unimplemented!() } /// Queue a block for import. - pub fn import_block(&self, header: block::Header, body: Option) -> error::Result { + 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), @@ -230,7 +266,7 @@ impl Client where 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, 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) @@ -285,6 +321,38 @@ impl Client where 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)] @@ -315,6 +383,31 @@ mod tests { (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(); @@ -327,11 +420,7 @@ mod tests { #[test] fn authorities_call_works() { - let genesis_config = GenesisConfig::new_simple(vec![ - Keyring::Alice.to_raw_public(), - Keyring::Bob.to_raw_public(), - Keyring::Charlie.to_raw_public() - ], 1000); + let genesis_config = genesis_config(); let prepare_genesis = || { let mut storage = genesis_config.genesis_map(); @@ -351,11 +440,7 @@ mod tests { #[test] fn block_builder_works_with_no_transactions() { - let genesis_config = GenesisConfig::new_simple(vec![ - Keyring::Alice.to_raw_public(), - Keyring::Bob.to_raw_public(), - Keyring::Charlie.to_raw_public() - ], 1000); + let genesis_config = genesis_config(); let prepare_genesis = || { let mut storage = genesis_config.genesis_map(); @@ -368,7 +453,9 @@ mod tests { let builder = client.new_block().unwrap(); let block = builder.bake().unwrap(); - client.import_block(block.header, Some(block.transactions)).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()); @@ -409,7 +496,10 @@ mod tests { nonce: 0 }.signed()).unwrap(); let block = builder.bake().unwrap(); - client.import_block(block.header, Some(block.transactions)).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()); From 5d1a3a7112b51ba46767a24a19da018423043ffe Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 16 Feb 2018 20:17:26 +0100 Subject: [PATCH 15/19] Final fixes. --- substrate/client/src/client.rs | 6 +++--- substrate/client/src/error.rs | 1 - substrate/client/src/lib.rs | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/substrate/client/src/client.rs b/substrate/client/src/client.rs index 8efc326310dd2..36353309cea2e 100644 --- a/substrate/client/src/client.rs +++ b/substrate/client/src/client.rs @@ -16,7 +16,7 @@ //! Substrate Client -use primitives::{block, AuthorityId}; +use primitives::{self, block, AuthorityId}; use primitives::block::Id as BlockId; use primitives::storage::{StorageKey, StorageData}; use runtime_support::Hashable; @@ -24,8 +24,8 @@ use codec::{KeyedVec, Slicable}; use state_machine::{self, Ext, OverlayedChanges, Backend as StateBackend, CodeExecutor}; use backend::{self, BlockImportOperation}; -use blockchain::{self, Info as ChainInfo, BlockId, Backend as ChainBackend}; -use {error, in_mem, block_builder, runtime_io}; +use blockchain::{self, Info as ChainInfo, Backend as ChainBackend}; +use {error, in_mem, block_builder, runtime_io, bft}; /// Polkadot Client #[derive(Debug)] diff --git a/substrate/client/src/error.rs b/substrate/client/src/error.rs index 46599af18a5dc..179fa7d910a91 100644 --- a/substrate/client/src/error.rs +++ b/substrate/client/src/error.rs @@ -18,7 +18,6 @@ use std; use state_machine; -use blockchain; use primitives::hexdisplay::HexDisplay; error_chain! { diff --git a/substrate/client/src/lib.rs b/substrate/client/src/lib.rs index a81740ef71940..6054808f9b433 100644 --- a/substrate/client/src/lib.rs +++ b/substrate/client/src/lib.rs @@ -44,4 +44,4 @@ pub mod block_builder; mod client; pub use client::{Client, ClientInfo, CallResult, ImportResult, BlockStatus, new_in_mem}; -pub use blockchain::{Info as ChainInfo, BlockId}; +pub use blockchain::Info as ChainInfo; From 212d37016c76f2827cf3995304f2c686f8aa9513 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 17 Feb 2018 15:47:41 +0100 Subject: [PATCH 16/19] Unrevert typos --- substrate/client/src/block_builder.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substrate/client/src/block_builder.rs b/substrate/client/src/block_builder.rs index feb69861de3f4..54fcd8df7ebf0 100644 --- a/substrate/client/src/block_builder.rs +++ b/substrate/client/src/block_builder.rs @@ -71,7 +71,7 @@ impl BlockBuilder where pub fn push(&mut self, tx: Transaction) -> error::Result<()> { let output = state_machine::execute(&self.state, &mut self.changes, &self.executor, "execute_transaction", &vec![].and(&self.header).and(&tx))?; - self.header = Header::decode(&mut &output[..]).expect("Header came straight out of runtime do must be valid"); + self.header = Header::decode(&mut &output[..]).expect("Header came straight out of runtime so must be valid"); self.transactions.push(tx); Ok(()) } @@ -81,7 +81,7 @@ impl BlockBuilder where self.header.transaction_root = ordered_trie_root(self.transactions.iter().map(Slicable::encode)).0.into(); let output = state_machine::execute(&self.state, &mut self.changes, &self.executor, "finalise_block", &self.header.encode())?; - self.header = Header::decode(&mut &output[..]).expect("Header came straight out of runtime do must be valid"); + self.header = Header::decode(&mut &output[..]).expect("Header came straight out of runtime so must be valid"); Ok(Block { header: self.header, transactions: self.transactions, From 780e54961f8368d21c1995d29fbd91fc85cc673d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 18 Feb 2018 22:53:53 +0100 Subject: [PATCH 17/19] Remove accidentally committed change --- substrate/client/src/client.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/substrate/client/src/client.rs b/substrate/client/src/client.rs index 36353309cea2e..3ccc9debff5b4 100644 --- a/substrate/client/src/client.rs +++ b/substrate/client/src/client.rs @@ -233,11 +233,6 @@ impl Client where }) } - /// Author a new block, filling it with valid transactions from our transaction pool. - pub fn propose_block_at(&self, parent: &BlockId) -> block::Block { - unimplemented!() - } - /// Queue a block for import. pub fn import_block( &self, From e5528757aa0bc75f0d0245f32c90a89fb7ee1229 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sun, 18 Feb 2018 23:08:48 +0100 Subject: [PATCH 18/19] Bring back zero copy --- substrate/runtime-support/src/storage.rs | 39 +++++++++++++++++++----- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/substrate/runtime-support/src/storage.rs b/substrate/runtime-support/src/storage.rs index ac289a49887f2..95625bfa189fe 100644 --- a/substrate/runtime-support/src/storage.rs +++ b/substrate/runtime-support/src/storage.rs @@ -22,11 +22,30 @@ use codec::{Slicable, KeyedVec}; // 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 maybe_raw = runtime_io::storage(&twox_128(key)[..]); - maybe_raw.map(|raw| Slicable::decode(&mut &raw[..]) - .expect("storage should contain a decodable value if there is some entry")) + 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) + } } /// Return the value of the item in storage under `key`, or the type's default if there is no @@ -153,11 +172,15 @@ pub mod unhashed { /// 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 maybe_raw = runtime_io::storage(key); - maybe_raw.map(|raw| Slicable::decode(&mut &raw[..]) - .expect("storage should contain a decodable value if there is some entry")) + runtime_io::read_storage(key, &mut [0; 0][..], 0).map(|_| { + let mut input = IncrementalInput { + key, + pos: 0, + }; + Slicable::decode(&mut input) + } } - + /// Return the value of the item in storage under `key`, or the type's default if there is no /// explicit entry. pub fn get_or_default(key: &[u8]) -> T { From 0a08196c2bd23ddffc19c559532902d7ac85f390 Mon Sep 17 00:00:00 2001 From: Gav Date: Sun, 18 Feb 2018 23:44:29 +0100 Subject: [PATCH 19/19] Fix merge. --- substrate/runtime-support/src/storage.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/substrate/runtime-support/src/storage.rs b/substrate/runtime-support/src/storage.rs index 95625bfa189fe..7dbfd8013b35e 100644 --- a/substrate/runtime-support/src/storage.rs +++ b/substrate/runtime-support/src/storage.rs @@ -18,7 +18,7 @@ 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. @@ -44,8 +44,8 @@ pub fn get(key: &[u8]) -> Option { key: &key[..], pos: 0, }; - Slicable::decode(&mut input) - } + 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 @@ -168,7 +168,7 @@ 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 { @@ -177,10 +177,10 @@ pub mod unhashed { key, pos: 0, }; - Slicable::decode(&mut input) - } + 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 /// explicit entry. pub fn get_or_default(key: &[u8]) -> T {