diff --git a/substrate/core/client/db/src/lib.rs b/substrate/core/client/db/src/lib.rs index d17337a665..a7d7935278 100644 --- a/substrate/core/client/db/src/lib.rs +++ b/substrate/core/client/db/src/lib.rs @@ -324,29 +324,26 @@ where Block: BlockT, Ok(()) } - fn reset_storage(&mut self, mut top: StorageOverlay, children: ChildrenStorageOverlay) -> Result { + fn reset_storage(&mut self, top: StorageOverlay, children: ChildrenStorageOverlay) -> Result { if top.iter().any(|(k, _)| well_known_keys::is_child_storage_key(k)) { return Err(client::error::Error::GenesisInvalid.into()); } - let mut transaction: PrefixedMemoryDB = Default::default(); - - for (child_key, child_map) in children { + for child_key in children.keys() { if !well_known_keys::is_child_storage_key(&child_key) { return Err(client::error::Error::GenesisInvalid.into()); } - - let (root, is_default, update) = self.old_state.child_storage_root(&child_key, child_map.into_iter().map(|(k, v)| (k, Some(v)))); - transaction.consolidate(update); - - if !is_default { - top.insert(child_key, root); - } } - let (root, update) = self.old_state.storage_root(top.into_iter().map(|(k, v)| (k, Some(v)))); - transaction.consolidate(update); + let child_delta = children.into_iter() + .map(|(storage_key, child_overlay)| + (storage_key, child_overlay.into_iter().map(|(k, v)| (k, Some(v))))); + + let (root, transaction) = self.old_state.full_storage_root( + top.into_iter().map(|(k, v)| (k, Some(v))), + child_delta + ); self.db_updates = transaction; Ok(root) diff --git a/substrate/core/client/src/client.rs b/substrate/core/client/src/client.rs index 640484f9d5..4091e23b5f 100644 --- a/substrate/core/client/src/client.rs +++ b/substrate/core/client/src/client.rs @@ -52,7 +52,7 @@ use primitives::storage::well_known_keys; use parity_codec::{Encode, Decode}; use state_machine::{ DBValue, Backend as StateBackend, CodeExecutor, ChangesTrieAnchorBlockId, - ExecutionStrategy, ExecutionManager, prove_read, + ExecutionStrategy, ExecutionManager, prove_read, prove_child_read, ChangesTrieRootsStorage, ChangesTrieStorage, key_changes, key_changes_proof, OverlayedChanges, NeverOffchainExt, }; @@ -374,6 +374,20 @@ impl Client where .map_err(Into::into)) } + /// Reads child storage value at a given block + storage_key + key, returning + /// read proof. + pub fn read_child_proof( + &self, + id: &BlockId, + storage_key: &[u8], + key: &[u8] + ) -> error::Result>> { + self.state_at(id) + .and_then(|state| prove_child_read(state, storage_key, key) + .map(|(_, proof)| proof) + .map_err(Into::into)) + } + /// Execute a call to a contract on top of state in a block of given hash /// AND returning execution proof. /// diff --git a/substrate/core/client/src/in_mem.rs b/substrate/core/client/src/in_mem.rs index b02fbe462e..0050cb9110 100644 --- a/substrate/core/client/src/in_mem.rs +++ b/substrate/core/client/src/in_mem.rs @@ -24,7 +24,7 @@ use runtime_primitives::generic::BlockId; use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, NumberFor, As, Digest, DigestItem}; use runtime_primitives::{Justification, StorageOverlay, ChildrenStorageOverlay}; -use state_machine::backend::{Backend as StateBackend, InMemory, Consolidate}; +use state_machine::backend::{Backend as StateBackend, InMemory}; use state_machine::{self, InMemoryChangesTrieStorage, ChangesTrieAnchorBlockId}; use hash_db::Hasher; use trie::MemoryDB; @@ -482,22 +482,17 @@ where Ok(()) } - fn reset_storage(&mut self, mut top: StorageOverlay, children: ChildrenStorageOverlay) -> error::Result { + fn reset_storage(&mut self, top: StorageOverlay, children: ChildrenStorageOverlay) -> error::Result { check_genesis_storage(&top, &children)?; - let mut transaction: Vec<(Option>, Vec, Option>)> = Default::default(); + let child_delta = children.into_iter() + .map(|(storage_key, child_overlay)| + (storage_key, child_overlay.into_iter().map(|(k, v)| (k, Some(v))))); - for (child_key, child_map) in children { - let (root, is_default, update) = self.old_state.child_storage_root(&child_key, child_map.into_iter().map(|(k, v)| (k, Some(v)))); - transaction.consolidate(update); - - if !is_default { - top.insert(child_key, root); - } - } - - let (root, update) = self.old_state.storage_root(top.into_iter().map(|(k, v)| (k, Some(v)))); - transaction.consolidate(update); + let (root, transaction) = self.old_state.full_storage_root( + top.into_iter().map(|(k, v)| (k, Some(v))), + child_delta + ); self.new_state = Some(InMemory::from(transaction)); Ok(root) diff --git a/substrate/core/client/src/light/fetcher.rs b/substrate/core/client/src/light/fetcher.rs index d62930b1ad..1e6f0842fb 100644 --- a/substrate/core/client/src/light/fetcher.rs +++ b/substrate/core/client/src/light/fetcher.rs @@ -25,7 +25,8 @@ use hash_db::{HashDB, Hasher}; use primitives::{ChangesTrieConfiguration, convert_hash}; use runtime_primitives::traits::{As, Block as BlockT, Header as HeaderT, NumberFor}; use state_machine::{CodeExecutor, ChangesTrieRootsStorage, ChangesTrieAnchorBlockId, - TrieBackend, read_proof_check, key_changes_proof_check, create_proof_check_backend_storage}; + TrieBackend, read_proof_check, key_changes_proof_check, + create_proof_check_backend_storage, read_child_proof_check}; use crate::cht; use crate::error::{Error as ClientError, Result as ClientResult}; @@ -71,6 +72,21 @@ pub struct RemoteReadRequest { pub retry_count: Option, } +/// Remote storage read child request. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct RemoteReadChildRequest { + /// Read at state of given block. + pub block: Header::Hash, + /// Header of block at which read is performed. + pub header: Header, + /// Storage key for child. + pub storage_key: Vec, + /// Child storage key to read. + pub key: Vec, + /// Number of times to retry request. None means that default RETRY_COUNT is used. + pub retry_count: Option, +} + /// Remote key changes read request. #[derive(Clone, Debug, PartialEq, Eq)] pub struct RemoteChangesRequest { @@ -123,7 +139,15 @@ pub trait Fetcher: Send + Sync { /// Fetch remote header. fn remote_header(&self, request: RemoteHeaderRequest) -> Self::RemoteHeaderResult; /// Fetch remote storage value. - fn remote_read(&self, request: RemoteReadRequest) -> Self::RemoteReadResult; + fn remote_read( + &self, + request: RemoteReadRequest + ) -> Self::RemoteReadResult; + /// Fetch remote storage child value. + fn remote_read_child( + &self, + request: RemoteReadChildRequest + ) -> Self::RemoteReadResult; /// Fetch remote call result. fn remote_call(&self, request: RemoteCallRequest) -> Self::RemoteCallResult; /// Fetch remote changes ((block number, extrinsic index)) where given key has been changed @@ -149,6 +173,12 @@ pub trait FetchChecker: Send + Sync { request: &RemoteReadRequest, remote_proof: Vec> ) -> ClientResult>>; + /// Check remote storage read proof. + fn check_read_child_proof( + &self, + request: &RemoteReadChildRequest, + remote_proof: Vec> + ) -> ClientResult>>; /// Check remote method execution proof. fn check_execution_proof( &self, @@ -338,6 +368,19 @@ impl FetchChecker for LightDataChecker, + remote_proof: Vec> + ) -> ClientResult>> { + read_child_proof_check::( + convert_hash(request.header.state_root()), + remote_proof, + &request.storage_key, + &request.key) + .map_err(Into::into) + } + fn check_execution_proof( &self, request: &RemoteCallRequest, @@ -425,6 +468,10 @@ pub mod tests { err("Not implemented on test node".into()) } + fn remote_read_child(&self, _request: RemoteReadChildRequest
) -> Self::RemoteReadResult { + err("Not implemented on test node".into()) + } + fn remote_call(&self, _request: RemoteCallRequest
) -> Self::RemoteCallResult { ok((*self.lock()).clone()) } diff --git a/substrate/core/network/src/message.rs b/substrate/core/network/src/message.rs index b2cc5a888a..85a526be53 100644 --- a/substrate/core/network/src/message.rs +++ b/substrate/core/network/src/message.rs @@ -23,7 +23,7 @@ pub use self::generic::{ BlockAnnounce, RemoteCallRequest, RemoteReadRequest, RemoteHeaderRequest, RemoteHeaderResponse, RemoteChangesRequest, RemoteChangesResponse, - FromBlock + FromBlock, RemoteReadChildRequest, }; /// A unique ID of a request. @@ -129,8 +129,8 @@ pub mod generic { use runtime_primitives::Justification; use crate::config::Roles; use super::{ - BlockAttributes, RemoteCallResponse, RemoteReadResponse, - RequestId, Transactions, Direction, ConsensusEngineId, + RemoteReadResponse, Transactions, Direction, + RequestId, BlockAttributes, RemoteCallResponse, ConsensusEngineId, }; /// Consensus is mostly opaque to us #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] @@ -198,6 +198,8 @@ pub mod generic { RemoteChangesRequest(RemoteChangesRequest), /// Remote changes reponse. RemoteChangesResponse(RemoteChangesResponse), + /// Remote child storage read request. + RemoteReadChildRequest(RemoteReadChildRequest), /// Chain-specific message #[codec(index = "255")] ChainSpecific(Vec), @@ -291,6 +293,19 @@ pub mod generic { pub key: Vec, } + #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] + /// Remote storage read child request. + pub struct RemoteReadChildRequest { + /// Unique request id. + pub id: RequestId, + /// Block at which to perform call. + pub block: H, + /// Child Storage key. + pub storage_key: Vec, + /// Storage key. + pub key: Vec, + } + #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] /// Remote header request. pub struct RemoteHeaderRequest { diff --git a/substrate/core/network/src/on_demand.rs b/substrate/core/network/src/on_demand.rs index 6bd303e93e..080eb7f046 100644 --- a/substrate/core/network/src/on_demand.rs +++ b/substrate/core/network/src/on_demand.rs @@ -27,7 +27,8 @@ use linked_hash_map::Entry; use parking_lot::Mutex; use client::error::Error as ClientError; use client::light::fetcher::{Fetcher, FetchChecker, RemoteHeaderRequest, - RemoteCallRequest, RemoteReadRequest, RemoteChangesRequest, ChangesProof}; + RemoteCallRequest, RemoteReadRequest, RemoteChangesRequest, ChangesProof, + RemoteReadChildRequest}; use crate::message; use network_libp2p::PeerId; use crate::config::Roles; @@ -107,6 +108,10 @@ struct Request { enum RequestData { RemoteHeader(RemoteHeaderRequest, OneShotSender>), RemoteRead(RemoteReadRequest, OneShotSender>, ClientError>>), + RemoteReadChild( + RemoteReadChildRequest, + OneShotSender>, ClientError>> + ), RemoteCall(RemoteCallRequest, OneShotSender, ClientError>>), RemoteChanges(RemoteChangesRequest, OneShotSender, u32)>, ClientError>>), } @@ -271,14 +276,30 @@ impl OnDemandService for OnDemand where fn on_remote_read_response(&self, peer: PeerId, response: message::RemoteReadResponse) { self.accept_response("read", peer, response.id, |request| match request.data { - RequestData::RemoteRead(request, sender) => match self.checker.check_read_proof(&request, response.proof) { - Ok(response) => { - // we do not bother if receiver has been dropped already - let _ = sender.send(Ok(response)); - Accept::Ok - }, - Err(error) => Accept::CheckFailed(error, RequestData::RemoteRead(request, sender)), - }, + RequestData::RemoteRead(request, sender) => { + match self.checker.check_read_proof(&request, response.proof) { + Ok(response) => { + // we do not bother if receiver has been dropped already + let _ = sender.send(Ok(response)); + Accept::Ok + }, + Err(error) => Accept::CheckFailed( + error, + RequestData::RemoteRead(request, sender) + ), + }}, + RequestData::RemoteReadChild(request, sender) => { + match self.checker.check_read_child_proof(&request, response.proof) { + Ok(response) => { + // we do not bother if receiver has been dropped already + let _ = sender.send(Ok(response)); + Accept::Ok + }, + Err(error) => Accept::CheckFailed( + error, + RequestData::RemoteReadChild(request, sender) + ), + }}, data => Accept::Unexpected(data), }) } @@ -335,8 +356,23 @@ impl Fetcher for OnDemand where fn remote_read(&self, request: RemoteReadRequest) -> Self::RemoteReadResult { let (sender, receiver) = channel(); - self.schedule_request(request.retry_count.clone(), RequestData::RemoteRead(request, sender), - RemoteResponse { receiver }) + self.schedule_request( + request.retry_count.clone(), + RequestData::RemoteRead(request, sender), + RemoteResponse { receiver } + ) + } + + fn remote_read_child( + &self, + request: RemoteReadChildRequest + ) -> Self::RemoteReadResult { + let (sender, receiver) = channel(); + self.schedule_request( + request.retry_count.clone(), + RequestData::RemoteReadChild(request, sender), + RemoteResponse { receiver } + ) } fn remote_call(&self, request: RemoteCallRequest) -> Self::RemoteCallResult { @@ -477,6 +513,7 @@ impl Request { match self.data { RequestData::RemoteHeader(ref data, _) => data.block, RequestData::RemoteRead(ref data, _) => *data.header.number(), + RequestData::RemoteReadChild(ref data, _) => *data.header.number(), RequestData::RemoteCall(ref data, _) => *data.header.number(), RequestData::RemoteChanges(ref data, _) => data.max_block.0, } @@ -495,6 +532,14 @@ impl Request { block: data.block, key: data.key.clone(), }), + RequestData::RemoteReadChild(ref data, _) => + message::generic::Message::RemoteReadChildRequest( + message::RemoteReadChildRequest { + id: self.id, + block: data.block, + storage_key: data.storage_key.clone(), + key: data.key.clone(), + }), RequestData::RemoteCall(ref data, _) => message::generic::Message::RemoteCallRequest(message::RemoteCallRequest { id: self.id, @@ -522,6 +567,7 @@ impl RequestData { RequestData::RemoteHeader(_, sender) => { let _ = sender.send(Err(error)); }, RequestData::RemoteCall(_, sender) => { let _ = sender.send(Err(error)); }, RequestData::RemoteRead(_, sender) => { let _ = sender.send(Err(error)); }, + RequestData::RemoteReadChild(_, sender) => { let _ = sender.send(Err(error)); }, RequestData::RemoteChanges(_, sender) => { let _ = sender.send(Err(error)); }, } } @@ -535,7 +581,8 @@ pub mod tests { use runtime_primitives::traits::NumberFor; use client::{error::{Error as ClientError, Result as ClientResult}}; use client::light::fetcher::{Fetcher, FetchChecker, RemoteHeaderRequest, - RemoteCallRequest, RemoteReadRequest, RemoteChangesRequest, ChangesProof}; + ChangesProof, RemoteCallRequest, RemoteReadRequest, + RemoteReadChildRequest, RemoteChangesRequest}; use crate::config::Roles; use crate::message; use network_libp2p::PeerId; @@ -566,6 +613,17 @@ pub mod tests { } } + fn check_read_child_proof( + &self, + _: &RemoteReadChildRequest
, + _: Vec> + ) -> ClientResult>> { + match self.ok { + true => Ok(Some(vec![42])), + false => Err(ClientError::Backend("Test error".into())), + } + } + fn check_execution_proof(&self, _: &RemoteCallRequest
, _: Vec>) -> ClientResult> { match self.ok { true => Ok(vec![42]), @@ -847,6 +905,34 @@ pub mod tests { thread.join().unwrap(); } + #[test] + fn receives_remote_read_child_response() { + let (_x, on_demand) = dummy(true); + let (network_sender, _network_port) = network_channel(); + let peer0 = PeerId::random(); + on_demand.set_network_sender(network_sender.clone()); + on_demand.on_connect(peer0.clone(), Roles::FULL, 1000); + + let response = on_demand.remote_read_child(RemoteReadChildRequest { + header: dummy_header(), + block: Default::default(), + storage_key: b":child_storage:sub".to_vec(), + key: b":key".to_vec(), + retry_count: None, + }); + let thread = ::std::thread::spawn(move || { + let result = response.wait().unwrap(); + assert_eq!(result, Some(vec![42])); + }); + + on_demand.on_remote_read_response( + peer0.clone(), message::RemoteReadResponse { + id: 0, + proof: vec![vec![2]], + }); + thread.join().unwrap(); + } + #[test] fn receives_remote_header_response() { let (_x, on_demand) = dummy(true); diff --git a/substrate/core/state-machine/src/backend.rs b/substrate/core/state-machine/src/backend.rs index c93935a347..895a805e43 100644 --- a/substrate/core/state-machine/src/backend.rs +++ b/substrate/core/state-machine/src/backend.rs @@ -70,6 +70,7 @@ pub trait Backend { /// Calculate the storage root, with given delta over what is already stored in /// the backend, and produce a "transaction" that can be used to commit. + /// Does not include child storage updates. fn storage_root(&self, delta: I) -> (H::Out, Self::Transaction) where I: IntoIterator, Option>)>, @@ -91,6 +92,41 @@ pub trait Backend { /// Try convert into trie backend. fn try_into_trie_backend(self) -> Option>; + + /// Calculate the storage root, with given delta over what is already stored + /// in the backend, and produce a "transaction" that can be used to commit. + /// Does include child storage updates. + fn full_storage_root( + &self, + delta: I1, + child_deltas: I2) + -> (H::Out, Self::Transaction) + where + I1: IntoIterator, Option>)>, + I2i: IntoIterator, Option>)>, + I2: IntoIterator, I2i)>, + ::Out: Ord, + { + let mut txs: Self::Transaction = Default::default(); + let mut child_roots: Vec<_> = Default::default(); + // child first + for (storage_key, child_delta) in child_deltas { + let (child_root, empty, child_txs) = + self.child_storage_root(&storage_key[..], child_delta); + txs.consolidate(child_txs); + if empty { + child_roots.push((storage_key, None)); + } else { + child_roots.push((storage_key, Some(child_root))); + } + } + let (root, parent_txs) = self.storage_root( + delta.into_iter().chain(child_roots.into_iter()) + ); + txs.consolidate(parent_txs); + (root, txs) + } + } /// Trait that allows consolidate two transactions together. @@ -213,6 +249,13 @@ impl From>, Vec, Option>)>> for InMemory { impl super::Error for Void {} +impl InMemory { + /// child storage key iterator + pub fn child_storage_keys(&self) -> impl Iterator { + self.inner.iter().filter_map(|item| item.0.as_ref().map(|v|&v[..])) + } +} + impl Backend for InMemory { type Error = Void; type Transaction = Vec<(Option>, Vec, Option>)>; @@ -290,16 +333,28 @@ impl Backend for InMemory { self.inner.get(&None).into_iter().flat_map(|map| map.keys().filter(|k| k.starts_with(prefix)).cloned()).collect() } - fn try_into_trie_backend(self) -> Option> { + fn try_into_trie_backend( + self + )-> Option> { let mut mdb = MemoryDB::default(); let mut root = None; + let mut new_child_roots = Vec::new(); + let mut root_map = None; for (storage_key, map) in self.inner { - if storage_key != None { - let _ = insert_into_memory_db::(&mut mdb, map.into_iter())?; + if let Some(storage_key) = storage_key.as_ref() { + let ch = insert_into_memory_db::(&mut mdb, map.into_iter())?; + new_child_roots.push((storage_key.clone(), ch.as_ref().into())); } else { - root = Some(insert_into_memory_db::(&mut mdb, map.into_iter())?); + root_map = Some(map); } } + // root handling + if let Some(map) = root_map.take() { + root = Some(insert_into_memory_db::( + &mut mdb, + map.into_iter().chain(new_child_roots.into_iter()) + )?); + } let root = match root { Some(root) => root, None => insert_into_memory_db::(&mut mdb, ::std::iter::empty())?, diff --git a/substrate/core/state-machine/src/ext.rs b/substrate/core/state-machine/src/ext.rs index 0ab269784a..0f5a4e2ed5 100644 --- a/substrate/core/state-machine/src/ext.rs +++ b/substrate/core/state-machine/src/ext.rs @@ -18,7 +18,7 @@ use std::{error, fmt, cmp::Ord}; use log::warn; -use crate::backend::{Backend, Consolidate}; +use crate::backend::Backend; use crate::changes_trie::{AnchorBlockId, Storage as ChangesTrieStorage, compute_changes_trie_root}; use crate::{Externalities, OverlayedChanges, OffchainExt, ChildStorageKey}; use hash_db::Hasher; @@ -134,30 +134,6 @@ where self.storage_transaction = None; } - /// Fetch child storage root together with its transaction. - fn child_storage_root_transaction(&mut self, storage_key: &[u8]) -> (Vec, B::Transaction) { - self.mark_dirty(); - - let (root, is_default, transaction) = { - let delta = self.overlay.committed.children.get(storage_key) - .into_iter() - .flat_map(|map| map.1.iter().map(|(k, v)| (k.clone(), v.clone()))) - .chain(self.overlay.prospective.children.get(storage_key) - .into_iter() - .flat_map(|map| map.1.iter().map(|(k, v)| (k.clone(), v.clone())))); - - self.backend.child_storage_root(storage_key, delta) - }; - - let root_val = if is_default { - None - } else { - Some(root.clone()) - }; - self.overlay.sync_child_storage_root(storage_key, root_val); - - (root, transaction) - } } #[cfg(test)] @@ -288,21 +264,24 @@ where return root.clone(); } - let mut transaction = B::Transaction::default(); - let child_storage_keys: std::collections::BTreeSet<_> = self.overlay.prospective.children.keys().cloned() - .chain(self.overlay.committed.children.keys().cloned()).collect(); + let child_storage_keys = + self.overlay.prospective.children.keys() + .chain(self.overlay.committed.children.keys()); + + let child_delta_iter = child_storage_keys.map(|storage_key| + (storage_key.clone(), self.overlay.committed.children.get(storage_key) + .into_iter() + .flat_map(|map| map.1.iter().map(|(k, v)| (k.clone(), v.clone()))) + .chain(self.overlay.prospective.children.get(storage_key) + .into_iter() + .flat_map(|map| map.1.iter().map(|(k, v)| (k.clone(), v.clone())))))); - for key in child_storage_keys { - let (_, t) = self.child_storage_root_transaction(&key); - transaction.consolidate(t); - } // compute and memoize let delta = self.overlay.committed.top.iter().map(|(k, v)| (k.clone(), v.value.clone())) .chain(self.overlay.prospective.top.iter().map(|(k, v)| (k.clone(), v.value.clone()))); - let (root, t) = self.backend.storage_root(delta); - transaction.consolidate(t); + let (root, transaction) = self.backend.full_storage_root(delta, child_delta_iter); self.storage_transaction = Some((transaction, root)); root } @@ -316,7 +295,21 @@ where default_child_trie_root::(storage_key.as_ref()) ) } else { - self.child_storage_root_transaction(storage_key.as_ref()).0 + let storage_key = storage_key.as_ref(); + + let delta = self.overlay.committed.children.get(storage_key) + .into_iter() + .flat_map(|map| map.1.iter().map(|(k, v)| (k.clone(), v.clone()))) + .chain(self.overlay.prospective.children.get(storage_key) + .into_iter() + .flat_map(|map| map.1.iter().map(|(k, v)| (k.clone(), v.clone())))); + + let root = self.backend.child_storage_root(storage_key, delta).0; + + self.overlay.set_storage(storage_key.to_vec(), Some(root.to_vec())); + + root + } } diff --git a/substrate/core/state-machine/src/lib.rs b/substrate/core/state-machine/src/lib.rs index 00fa26c7f2..daf8f915a1 100644 --- a/substrate/core/state-machine/src/lib.rs +++ b/substrate/core/state-machine/src/lib.rs @@ -631,7 +631,7 @@ where Exec: CodeExecutor, H::Out: Ord, { - let trie_backend = proving_backend::create_proof_check_backend::(root.into(), proof)?; + let trie_backend = create_proof_check_backend::(root.into(), proof)?; execution_proof_check_on_trie_backend(&trie_backend, overlay, exec, method, call_data) } @@ -676,10 +676,29 @@ where H::Out: Ord { let trie_backend = backend.try_into_trie_backend() - .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?; + .ok_or_else( + ||Box::new(ExecutionError::UnableToGenerateProof) as Box + )?; prove_read_on_trie_backend(&trie_backend, key) } +/// Generate child storage read proof. +pub fn prove_child_read( + backend: B, + storage_key: &[u8], + key: &[u8], +) -> Result<(Option>, Vec>), Box> +where + B: Backend, + H: Hasher, + H::Out: Ord +{ + let trie_backend = backend.try_into_trie_backend() + .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?; + prove_child_read_on_trie_backend(&trie_backend, storage_key, key) +} + + /// Generate storage read proof on pre-created trie backend. pub fn prove_read_on_trie_backend( trie_backend: &TrieBackend, @@ -695,6 +714,23 @@ where Ok((result, proving_backend.extract_proof())) } +/// Generate storage read proof on pre-created trie backend. +pub fn prove_child_read_on_trie_backend( + trie_backend: &TrieBackend, + storage_key: &[u8], + key: &[u8] +) -> Result<(Option>, Vec>), Box> +where + S: trie_backend_essence::TrieBackendStorage, + H: Hasher, + H::Out: Ord +{ + let proving_backend = proving_backend::ProvingBackend::<_, H>::new(trie_backend); + let result = proving_backend.child_storage(storage_key, key) + .map_err(|e| Box::new(e) as Box)?; + Ok((result, proving_backend.extract_proof())) +} + /// Check storage read proof, generated by `prove_read` call. pub fn read_proof_check( root: H::Out, @@ -705,10 +741,26 @@ where H: Hasher, H::Out: Ord { - let proving_backend = proving_backend::create_proof_check_backend::(root, proof)?; + let proving_backend = create_proof_check_backend::(root, proof)?; read_proof_check_on_proving_backend(&proving_backend, key) } +/// Check child storage read proof, generated by `prove_child_read` call. +pub fn read_child_proof_check( + root: H::Out, + proof: Vec>, + storage_key: &[u8], + key: &[u8], +) -> Result>, Box> +where + H: Hasher, + H::Out: Ord +{ + let proving_backend = create_proof_check_backend::(root, proof)?; + read_child_proof_check_on_proving_backend(&proving_backend, storage_key, key) +} + + /// Check storage read proof on pre-created proving backend. pub fn read_proof_check_on_proving_backend( proving_backend: &TrieBackend, H>, @@ -721,6 +773,19 @@ where proving_backend.storage(key).map_err(|e| Box::new(e) as Box) } +/// Check child storage read proof on pre-created proving backend. +pub fn read_child_proof_check_on_proving_backend( + proving_backend: &TrieBackend, H>, + storage_key: &[u8], + key: &[u8], +) -> Result>, Box> +where + H: Hasher, + H::Out: Ord +{ + proving_backend.child_storage(storage_key, key).map_err(|e| Box::new(e) as Box) +} + /// Sets overlayed changes' changes trie configuration. Returns error if configuration /// differs from previous OR config decode has failed. pub(crate) fn set_changes_trie_config(overlay: &mut OverlayedChanges, config: Option>, final_check: bool) -> Result<(), Box> { @@ -1001,11 +1066,40 @@ mod tests { let remote_root = remote_backend.storage_root(::std::iter::empty()).0; let remote_proof = prove_read(remote_backend, b"value2").unwrap().1; // check proof locally - let local_result1 = read_proof_check::(remote_root, remote_proof.clone(), b"value2").unwrap(); - let local_result2 = read_proof_check::(remote_root, remote_proof.clone(), &[0xff]).is_ok(); + let local_result1 = read_proof_check::( + remote_root, + remote_proof.clone(), + b"value2" + ).unwrap(); + let local_result2 = read_proof_check::( + remote_root, + remote_proof.clone(), + &[0xff] + ).is_ok(); // check that results are correct assert_eq!(local_result1, Some(vec![24])); assert_eq!(local_result2, false); + // on child trie + let remote_backend = trie_backend::tests::test_trie(); + let remote_root = remote_backend.storage_root(::std::iter::empty()).0; + let remote_proof = prove_child_read( + remote_backend, + b":child_storage:default:sub1", + b"value3" + ).unwrap().1; + let local_result1 = read_child_proof_check::( + remote_root, + remote_proof.clone(), + b":child_storage:default:sub1",b"value3" + ).unwrap(); + let local_result2 = read_child_proof_check::( + remote_root, + remote_proof.clone(), + b":child_storage:default:sub1", + b"value2" + ).unwrap(); + assert_eq!(local_result1, Some(vec![142])); + assert_eq!(local_result2, None); } #[test] diff --git a/substrate/core/state-machine/src/overlayed_changes.rs b/substrate/core/state-machine/src/overlayed_changes.rs index c98882726e..e595cff0e4 100644 --- a/substrate/core/state-machine/src/overlayed_changes.rs +++ b/substrate/core/state-machine/src/overlayed_changes.rs @@ -159,19 +159,6 @@ impl OverlayedChanges { } } - /// Sync the child storage root. - pub(crate) fn sync_child_storage_root(&mut self, storage_key: &[u8], root: Option>) { - let entry = self.prospective.top.entry(storage_key.to_vec()).or_default(); - entry.value = root; - - if let Some((Some(extrinsics), _)) = self.prospective.children.get(storage_key) { - for extrinsic in extrinsics { - entry.extrinsics.get_or_insert_with(Default::default) - .insert(*extrinsic); - } - } - } - /// Clear child storage of given storage key. /// /// NOTE that this doesn't take place immediately but written into the prospective diff --git a/substrate/core/state-machine/src/proving_backend.rs b/substrate/core/state-machine/src/proving_backend.rs index 73d41f2a61..fa7e94f78f 100644 --- a/substrate/core/state-machine/src/proving_backend.rs +++ b/substrate/core/state-machine/src/proving_backend.rs @@ -222,6 +222,7 @@ mod tests { use crate::trie_backend::tests::test_trie; use super::*; use primitives::{Blake2Hasher}; + use crate::ChildStorageKey; fn test_proving<'a>(trie_backend: &'a TrieBackend, Blake2Hasher>) -> ProvingBackend<'a, PrefixedMemoryDB, Blake2Hasher> { ProvingBackend::new(trie_backend) @@ -281,4 +282,75 @@ mod tests { let proof_check = create_proof_check_backend::(in_memory_root.into(), proof).unwrap(); assert_eq!(proof_check.storage(&[42]).unwrap().unwrap(), vec![42]); } + + #[test] + fn proof_recorded_and_checked_with_child() { + let subtrie1 = ChildStorageKey::::from_slice( + b":child_storage:default:sub1" + ).unwrap(); + let subtrie2 = ChildStorageKey::::from_slice( + b":child_storage:default:sub2" + ).unwrap(); + let own1 = subtrie1.into_owned(); + let own2 = subtrie2.into_owned(); + let contents = (0..64).map(|i| (None, vec![i], Some(vec![i]))) + .chain((28..65).map(|i| (Some(own1.clone()), vec![i], Some(vec![i])))) + .chain((10..15).map(|i| (Some(own2.clone()), vec![i], Some(vec![i])))) + .collect::>(); + let in_memory = InMemory::::default(); + let in_memory = in_memory.update(contents); + let in_memory_root = in_memory.full_storage_root::<_, Vec<_>, _>( + ::std::iter::empty(), + in_memory.child_storage_keys().map(|k|(k.to_vec(), Vec::new())) + ).0; + (0..64).for_each(|i| assert_eq!( + in_memory.storage(&[i]).unwrap().unwrap(), + vec![i] + )); + (28..65).for_each(|i| assert_eq!( + in_memory.child_storage(&own1[..], &[i]).unwrap().unwrap(), + vec![i] + )); + (10..15).for_each(|i| assert_eq!( + in_memory.child_storage(&own2[..], &[i]).unwrap().unwrap(), + vec![i] + )); + + let trie = in_memory.try_into_trie_backend().unwrap(); + let trie_root = trie.storage_root(::std::iter::empty()).0; + assert_eq!(in_memory_root, trie_root); + (0..64).for_each(|i| assert_eq!( + trie.storage(&[i]).unwrap().unwrap(), + vec![i] + )); + + let proving = ProvingBackend::new(&trie); + assert_eq!(proving.storage(&[42]).unwrap().unwrap(), vec![42]); + + let proof = proving.extract_proof(); + + let proof_check = create_proof_check_backend::( + in_memory_root.into(), + proof + ).unwrap(); + assert!(proof_check.storage(&[0]).is_err()); + assert_eq!(proof_check.storage(&[42]).unwrap().unwrap(), vec![42]); + // note that it is include in root because proof close + assert_eq!(proof_check.storage(&[41]).unwrap().unwrap(), vec![41]); + assert_eq!(proof_check.storage(&[64]).unwrap(), None); + + let proving = ProvingBackend::new(&trie); + assert_eq!(proving.child_storage(&own1[..], &[64]), Ok(Some(vec![64]))); + + let proof = proving.extract_proof(); + let proof_check = create_proof_check_backend::( + in_memory_root.into(), + proof + ).unwrap(); + assert_eq!( + proof_check.child_storage(&own1[..], &[64]).unwrap().unwrap(), + vec![64] + ); + } + } diff --git a/substrate/core/state-machine/src/trie_backend.rs b/substrate/core/state-machine/src/trie_backend.rs index 78f5cf3d52..1ce915d5f5 100644 --- a/substrate/core/state-machine/src/trie_backend.rs +++ b/substrate/core/state-machine/src/trie_backend.rs @@ -188,6 +188,7 @@ impl, H: Hasher> Backend for TrieBackend where pub mod tests { use std::collections::HashSet; use primitives::{Blake2Hasher, H256}; + use parity_codec::Encode; use trie::{TrieMut, TrieDBMut, PrefixedMemoryDB}; use super::*; @@ -196,6 +197,15 @@ pub mod tests { let mut mdb = PrefixedMemoryDB::::default(); { let mut trie = TrieDBMut::new(&mut mdb, &mut root); + trie.insert(b"value3", &[142]).expect("insert failed"); + trie.insert(b"value4", &[124]).expect("insert failed"); + }; + + { + let mut sub_root = Vec::new(); + root.encode_to(&mut sub_root); + let mut trie = TrieDBMut::new(&mut mdb, &mut root); + trie.insert(b":child_storage:default:sub1", &sub_root).expect("insert failed"); trie.insert(b"key", b"value").expect("insert failed"); trie.insert(b"value1", &[42]).expect("insert failed"); trie.insert(b"value2", &[24]).expect("insert failed"); diff --git a/substrate/core/test-client/src/lib.rs b/substrate/core/test-client/src/lib.rs index fc1ccd140f..dfbb1fbcec 100644 --- a/substrate/core/test-client/src/lib.rs +++ b/substrate/core/test-client/src/lib.rs @@ -303,6 +303,13 @@ impl client::light::fetcher::Fetcher for LightFetcher { unimplemented!("not (yet) used in tests") } + fn remote_read_child( + &self, + _request: client::light::fetcher::RemoteReadChildRequest, + ) -> Self::RemoteReadResult { + unimplemented!("not (yet) used in tests") + } + fn remote_call( &self, _request: client::light::fetcher::RemoteCallRequest, diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs index 2adbdd0504..4762ce4e91 100644 --- a/substrate/node/runtime/src/lib.rs +++ b/substrate/node/runtime/src/lib.rs @@ -59,7 +59,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_name: create_runtime_str!("substrate-node"), authoring_version: 10, spec_version: 76, - impl_version: 76, + impl_version: 77, apis: RUNTIME_API_VERSIONS, };