diff --git a/substrate/core/client/src/cht.rs b/substrate/core/client/src/cht.rs index 37462851a9..63a5b39c26 100644 --- a/substrate/core/client/src/cht.rs +++ b/substrate/core/client/src/cht.rs @@ -23,8 +23,6 @@ //! root has. A correct proof implies that the claimed block is identical to the one //! we discarded. -use std::collections::HashSet; - use hash_db; use codec::Encode; use trie; @@ -105,15 +103,10 @@ pub fn build_proof( let mut storage = InMemoryState::::default().update(transaction); let trie_storage = storage.as_trie_backend() .expect("InMemoryState::as_trie_backend always returns Some; qed"); - let mut total_proof = HashSet::new(); - for block in blocks.into_iter() { - debug_assert_eq!(block_to_cht_number(cht_size, block), Some(cht_num)); - - let (value, proof) = prove_read_on_trie_backend(trie_storage, &encode_cht_key(block))?; - assert!(value.is_some(), "we have just built trie that includes the value for block"); - total_proof.extend(proof); - } - Ok(total_proof.into_iter().collect()) + prove_read_on_trie_backend( + trie_storage, + blocks.into_iter().map(|number| encode_cht_key(number)), + ).map_err(ClientError::Execution) } /// Check CHT-based header proof. @@ -128,9 +121,21 @@ pub fn check_proof( Hasher: hash_db::Hasher, Hasher::Out: Ord, { - do_check_proof::(local_root, local_number, remote_hash, move |local_root, local_cht_key| - read_proof_check::(local_root, remote_proof, - local_cht_key).map_err(|e| ClientError::from(e))) + do_check_proof::( + local_root, + local_number, + remote_hash, + move |local_root, local_cht_key| + read_proof_check::( + local_root, + remote_proof, + ::std::iter::once(local_cht_key), + ) + .map(|mut map| map + .remove(local_cht_key) + .expect("checked proof of local_cht_key; qed")) + .map_err(|e| ClientError::from(e)), + ) } /// Check CHT-based header proof on pre-created proving backend. @@ -145,9 +150,16 @@ pub fn check_proof_on_proving_backend( Hasher: hash_db::Hasher, Hasher::Out: Ord, { - do_check_proof::(local_root, local_number, remote_hash, |_, local_cht_key| - read_proof_check_on_proving_backend::( - proving_backend, local_cht_key).map_err(|e| ClientError::from(e))) + do_check_proof::( + local_root, + local_number, + remote_hash, + |_, local_cht_key| + read_proof_check_on_proving_backend::( + proving_backend, + local_cht_key, + ).map_err(|e| ClientError::from(e)), + ) } /// Check CHT-based header proof using passed checker function. diff --git a/substrate/core/client/src/client.rs b/substrate/core/client/src/client.rs index 63d30b45e3..d0a53802fa 100644 --- a/substrate/core/client/src/client.rs +++ b/substrate/core/client/src/client.rs @@ -436,24 +436,28 @@ impl Client where } /// Reads storage value at a given block + key, returning read proof. - pub fn read_proof(&self, id: &BlockId, key: &[u8]) -> error::Result>> { + pub fn read_proof(&self, id: &BlockId, keys: I) -> error::Result>> where + I: IntoIterator, + I::Item: AsRef<[u8]>, + { self.state_at(id) - .and_then(|state| prove_read(state, key) - .map(|(_, proof)| proof) + .and_then(|state| prove_read(state, keys) .map_err(Into::into)) } /// Reads child storage value at a given block + storage_key + key, returning /// read proof. - pub fn read_child_proof( + pub fn read_child_proof( &self, id: &BlockId, storage_key: &[u8], - key: &[u8] - ) -> error::Result>> { + keys: I, + ) -> error::Result>> where + I: IntoIterator, + I::Item: AsRef<[u8]>, + { self.state_at(id) - .and_then(|state| prove_child_read(state, storage_key, key) - .map(|(_, proof)| proof) + .and_then(|state| prove_child_read(state, storage_key, keys) .map_err(Into::into)) } diff --git a/substrate/core/client/src/light/fetcher.rs b/substrate/core/client/src/light/fetcher.rs index 068534c5f6..3c4387209a 100644 --- a/substrate/core/client/src/light/fetcher.rs +++ b/substrate/core/client/src/light/fetcher.rs @@ -17,7 +17,7 @@ //! Light client data fetcher. Fetches requested data from remote full nodes. use std::sync::Arc; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; use std::marker::PhantomData; use std::future::Future; @@ -73,7 +73,7 @@ pub struct RemoteReadRequest { /// Header of block at which read is performed. pub header: Header, /// Storage key to read. - pub key: Vec, + pub keys: Vec>, /// Number of times to retry request. None means that default RETRY_COUNT is used. pub retry_count: Option, } @@ -88,7 +88,7 @@ pub struct RemoteReadChildRequest { /// Storage key for child. pub storage_key: Vec, /// Child storage key to read. - pub key: Vec, + pub keys: Vec>, /// Number of times to retry request. None means that default RETRY_COUNT is used. pub retry_count: Option, } @@ -147,7 +147,7 @@ pub trait Fetcher: Send + Sync { /// Remote header future. type RemoteHeaderResult: Future> + Send + 'static; /// Remote storage read future. - type RemoteReadResult: Future>, ClientError>> + Send + 'static; + type RemoteReadResult: Future, Option>>, ClientError>> + Send + 'static; /// Remote call result future. type RemoteCallResult: Future, ClientError>> + Send + 'static; /// Remote changes result future. @@ -193,13 +193,13 @@ pub trait FetchChecker: Send + Sync { &self, request: &RemoteReadRequest, remote_proof: Vec> - ) -> ClientResult>>; + ) -> ClientResult, Option>>>; /// Check remote storage read proof. fn check_read_child_proof( &self, request: &RemoteReadChildRequest, remote_proof: Vec> - ) -> ClientResult>>; + ) -> ClientResult, Option>>>; /// Check remote method execution proof. fn check_execution_proof( &self, @@ -395,23 +395,26 @@ impl FetchChecker for LightDataChecker fn check_read_proof( &self, request: &RemoteReadRequest, - remote_proof: Vec> - ) -> ClientResult>> { - read_proof_check::(convert_hash(request.header.state_root()), remote_proof, &request.key) - .map_err(Into::into) + remote_proof: Vec>, + ) -> ClientResult, Option>>> { + read_proof_check::( + convert_hash(request.header.state_root()), + remote_proof, + request.keys.iter(), + ).map_err(Into::into) } fn check_read_child_proof( &self, request: &RemoteReadChildRequest, remote_proof: Vec> - ) -> ClientResult>> { - read_child_proof_check::( + ) -> ClientResult, Option>>> { + read_child_proof_check::( convert_hash(request.header.state_root()), remote_proof, &request.storage_key, - &request.key) - .map_err(Into::into) + request.keys.iter(), + ).map_err(Into::into) } fn check_execution_proof( @@ -529,7 +532,7 @@ pub mod tests { impl Fetcher for OkCallFetcher { type RemoteHeaderResult = Ready>; - type RemoteReadResult = Ready>, ClientError>>; + type RemoteReadResult = Ready, Option>>, ClientError>>; type RemoteCallResult = Ready, ClientError>>; type RemoteChangesResult = Ready, u32)>, ClientError>>; type RemoteBodyResult = Ready, ClientError>>; @@ -579,7 +582,10 @@ pub mod tests { let heap_pages = remote_client.storage(&remote_block_id, &StorageKey(well_known_keys::HEAP_PAGES.to_vec())) .unwrap() .and_then(|v| Decode::decode(&mut &v.0[..]).ok()).unwrap(); - let remote_read_proof = remote_client.read_proof(&remote_block_id, well_known_keys::HEAP_PAGES).unwrap(); + let remote_read_proof = remote_client.read_proof( + &remote_block_id, + &[well_known_keys::HEAP_PAGES], + ).unwrap(); // check remote read proof locally let local_storage = InMemoryBlockchain::::new(); @@ -618,7 +624,7 @@ pub mod tests { let remote_read_proof = remote_client.read_child_proof( &remote_block_id, b":child_storage:default:child1", - b"key1", + &[b"key1"], ).unwrap(); // check locally @@ -676,9 +682,9 @@ pub mod tests { assert_eq!((&local_checker as &dyn FetchChecker).check_read_proof(&RemoteReadRequest::
{ block: remote_block_header.hash(), header: remote_block_header, - key: well_known_keys::HEAP_PAGES.to_vec(), + keys: vec![well_known_keys::HEAP_PAGES.to_vec()], retry_count: None, - }, remote_read_proof).unwrap().unwrap()[0], heap_pages as u8); + }, remote_read_proof).unwrap().remove(well_known_keys::HEAP_PAGES).unwrap().unwrap()[0], heap_pages as u8); } #[test] @@ -694,11 +700,11 @@ pub mod tests { block: remote_block_header.hash(), header: remote_block_header, storage_key: b":child_storage:default:child1".to_vec(), - key: b"key1".to_vec(), + keys: vec![b"key1".to_vec()], retry_count: None, }, remote_read_proof - ).unwrap().unwrap(), result); + ).unwrap().remove(b"key1".as_ref()).unwrap().unwrap(), result); } #[test] diff --git a/substrate/core/network/src/chain.rs b/substrate/core/network/src/chain.rs index a4767caf13..b7465685ae 100644 --- a/substrate/core/network/src/chain.rs +++ b/substrate/core/network/src/chain.rs @@ -49,10 +49,15 @@ pub trait Client: Send + Sync { fn header_proof(&self, block_number: ::Number) -> Result<(Block::Header, Vec>), Error>; /// Get storage read execution proof. - fn read_proof(&self, block: &Block::Hash, key: &[u8]) -> Result>, Error>; + fn read_proof(&self, block: &Block::Hash, keys: &[Vec]) -> Result>, Error>; /// Get child storage read execution proof. - fn read_child_proof(&self, block: &Block::Hash, storage_key: &[u8], key: &[u8]) -> Result>, Error>; + fn read_child_proof( + &self, + block: &Block::Hash, + storage_key: &[u8], + keys: &[Vec], + ) -> Result>, Error>; /// Get method execution proof. fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec, Vec>), Error>; @@ -113,18 +118,18 @@ impl Client for SubstrateClient where (self as &SubstrateClient).header_proof(&BlockId::Number(block_number)) } - fn read_proof(&self, block: &Block::Hash, key: &[u8]) -> Result>, Error> { - (self as &SubstrateClient).read_proof(&BlockId::Hash(block.clone()), key) + fn read_proof(&self, block: &Block::Hash, keys: &[Vec]) -> Result>, Error> { + (self as &SubstrateClient).read_proof(&BlockId::Hash(block.clone()), keys) } fn read_child_proof( &self, block: &Block::Hash, storage_key: &[u8], - key: &[u8] + keys: &[Vec], ) -> Result>, Error> { (self as &SubstrateClient) - .read_child_proof(&BlockId::Hash(block.clone()), storage_key, key) + .read_child_proof(&BlockId::Hash(block.clone()), storage_key, keys) } fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec, Vec>), Error> { diff --git a/substrate/core/network/src/on_demand_layer.rs b/substrate/core/network/src/on_demand_layer.rs index 818230eea5..cdcb99a25f 100644 --- a/substrate/core/network/src/on_demand_layer.rs +++ b/substrate/core/network/src/on_demand_layer.rs @@ -17,6 +17,7 @@ //! On-demand requests service. use crate::protocol::light_dispatch::RequestData; +use std::collections::HashMap; use std::sync::Arc; use futures::{prelude::*, sync::mpsc, sync::oneshot}; use futures03::compat::{Compat01As03, Future01CompatExt as _}; @@ -84,7 +85,7 @@ impl Fetcher for OnDemand where B::Header: HeaderT, { type RemoteHeaderResult = Compat01As03>; - type RemoteReadResult = Compat01As03>>>; + type RemoteReadResult = Compat01As03, Option>>>>; type RemoteCallResult = Compat01As03>>; type RemoteChangesResult = Compat01As03, u32)>>>; type RemoteBodyResult = Compat01As03>>; diff --git a/substrate/core/network/src/protocol.rs b/substrate/core/network/src/protocol.rs index ee2849e1dd..6dab5a2a54 100644 --- a/substrate/core/network/src/protocol.rs +++ b/substrate/core/network/src/protocol.rs @@ -172,11 +172,17 @@ impl<'a, B: BlockT> LightDispatchNetwork for LightDispatchIn<'a, B> { self.behaviour.send_packet(who, message) } - fn send_read_request(&mut self, who: &PeerId, id: RequestId, block: ::Hash, key: Vec) { + fn send_read_request( + &mut self, + who: &PeerId, + id: RequestId, + block: ::Hash, + keys: Vec>, + ) { let message = message::generic::Message::RemoteReadRequest(message::RemoteReadRequest { id, block, - key, + keys, }); self.behaviour.send_packet(who, message) @@ -188,13 +194,13 @@ impl<'a, B: BlockT> LightDispatchNetwork for LightDispatchIn<'a, B> { id: RequestId, block: ::Hash, storage_key: Vec, - key: Vec + keys: Vec>, ) { let message = message::generic::Message::RemoteReadChildRequest(message::RemoteReadChildRequest { id, block, storage_key, - key, + keys, }); self.behaviour.send_packet(who, message) @@ -1272,15 +1278,24 @@ impl, H: ExHashT> Protocol { who: PeerId, request: message::RemoteReadRequest, ) { + let keys_str = || match request.keys.len() { + 1 => request.keys[0].to_hex::(), + _ => format!( + "{}..{}", + request.keys[0].to_hex::(), + request.keys[request.keys.len() - 1].to_hex::(), + ), + }; + trace!(target: "sync", "Remote read request {} from {} ({} at {})", - request.id, who, request.key.to_hex::(), request.block); - let proof = match self.context_data.chain.read_proof(&request.block, &request.key) { + request.id, who, keys_str(), request.block); + let proof = match self.context_data.chain.read_proof(&request.block, &request.keys) { Ok(proof) => proof, Err(error) => { trace!(target: "sync", "Remote read request {} from {} ({} at {}) failed with: {}", request.id, who, - request.key.to_hex::(), + keys_str(), request.block, error ); @@ -1301,16 +1316,29 @@ impl, H: ExHashT> Protocol { who: PeerId, request: message::RemoteReadChildRequest, ) { + let keys_str = || match request.keys.len() { + 1 => request.keys[0].to_hex::(), + _ => format!( + "{}..{}", + request.keys[0].to_hex::(), + request.keys[request.keys.len() - 1].to_hex::(), + ), + }; + trace!(target: "sync", "Remote read child request {} from {} ({} {} at {})", - request.id, who, request.storage_key.to_hex::(), request.key.to_hex::(), request.block); - let proof = match self.context_data.chain.read_child_proof(&request.block, &request.storage_key, &request.key) { + request.id, who, request.storage_key.to_hex::(), keys_str(), request.block); + let proof = match self.context_data.chain.read_child_proof( + &request.block, + &request.storage_key, + &request.keys, + ) { Ok(proof) => proof, Err(error) => { trace!(target: "sync", "Remote read child request {} from {} ({} {} at {}) failed with: {}", request.id, who, request.storage_key.to_hex::(), - request.key.to_hex::(), + keys_str(), request.block, error ); diff --git a/substrate/core/network/src/protocol/light_dispatch.rs b/substrate/core/network/src/protocol/light_dispatch.rs index db7a55d742..33ff23c909 100644 --- a/substrate/core/network/src/protocol/light_dispatch.rs +++ b/substrate/core/network/src/protocol/light_dispatch.rs @@ -53,7 +53,13 @@ pub trait LightDispatchNetwork { fn send_header_request(&mut self, who: &PeerId, id: RequestId, block: <::Header as HeaderT>::Number); /// Send to `who` a read request. - fn send_read_request(&mut self, who: &PeerId, id: RequestId, block: ::Hash, key: Vec); + fn send_read_request( + &mut self, + who: &PeerId, + id: RequestId, + block: ::Hash, + keys: Vec>, + ); /// Send to `who` a child read request. fn send_read_child_request( @@ -62,7 +68,7 @@ pub trait LightDispatchNetwork { id: RequestId, block: ::Hash, storage_key: Vec, - key: Vec + keys: Vec>, ); /// Send to `who` a call request. @@ -135,10 +141,13 @@ struct Request { pub(crate) enum RequestData { RemoteBody(RemoteBodyRequest, OneShotSender, ClientError>>), RemoteHeader(RemoteHeaderRequest, OneShotSender>), - RemoteRead(RemoteReadRequest, OneShotSender>, ClientError>>), + RemoteRead( + RemoteReadRequest, + OneShotSender, Option>>, ClientError>>, + ), RemoteReadChild( RemoteReadChildRequest, - OneShotSender>, ClientError>> + OneShotSender, Option>>, ClientError>> ), RemoteCall(RemoteCallRequest, OneShotSender, ClientError>>), RemoteChanges( @@ -174,7 +183,7 @@ impl FetchChecker for AlwaysBadChecker { &self, _request: &RemoteReadRequest, _remote_proof: Vec> - ) -> Result>, ClientError> { + ) -> Result,Option>>, ClientError> { Err(ClientError::Msg("AlwaysBadChecker".into())) } @@ -182,7 +191,7 @@ impl FetchChecker for AlwaysBadChecker { &self, _request: &RemoteReadChildRequest, _remote_proof: Vec> - ) -> Result>, ClientError> { + ) -> Result, Option>>, ClientError> { Err(ClientError::Msg("AlwaysBadChecker".into())) } @@ -604,7 +613,7 @@ impl Request { peer, self.id, data.block, - data.key.clone(), + data.keys.clone(), ), RequestData::RemoteReadChild(ref data, _) => out.send_read_child_request( @@ -612,7 +621,7 @@ impl Request { self.id, data.block, data.storage_key.clone(), - data.key.clone(), + data.keys.clone(), ), RequestData::RemoteCall(ref data, _) => out.send_call_request( @@ -663,7 +672,7 @@ impl RequestData { #[cfg(test)] pub mod tests { - use std::collections::HashSet; + use std::collections::{HashMap, HashSet}; use std::sync::Arc; use std::time::Instant; use futures::{Future, sync::oneshot}; @@ -693,20 +702,34 @@ pub mod tests { } } - fn check_read_proof(&self, _: &RemoteReadRequest
, _: Vec>) -> ClientResult>> { + fn check_read_proof( + &self, + request: &RemoteReadRequest
, + _: Vec>, + ) -> ClientResult, Option>>> { match self.ok { - true => Ok(Some(vec![42])), + true => Ok(request.keys + .iter() + .cloned() + .map(|k| (k, Some(vec![42]))) + .collect() + ), false => Err(ClientError::Backend("Test error".into())), } } fn check_read_child_proof( &self, - _: &RemoteReadChildRequest
, + request: &RemoteReadChildRequest
, _: Vec> - ) -> ClientResult>> { + ) -> ClientResult, Option>>> { match self.ok { - true => Ok(Some(vec![42])), + true => Ok(request.keys + .iter() + .cloned() + .map(|k| (k, Some(vec![42]))) + .collect() + ), false => Err(ClientError::Backend("Test error".into())), } } @@ -782,9 +805,9 @@ pub mod tests { self.disconnected_peers.insert(who.clone()); } fn send_header_request(&mut self, _: &PeerId, _: RequestId, _: <::Header as HeaderT>::Number) {} - fn send_read_request(&mut self, _: &PeerId, _: RequestId, _: ::Hash, _: Vec) {} + fn send_read_request(&mut self, _: &PeerId, _: RequestId, _: ::Hash, _: Vec>) {} fn send_read_child_request(&mut self, _: &PeerId, _: RequestId, _: ::Hash, _: Vec, - _: Vec) {} + _: Vec>) {} fn send_call_request(&mut self, _: &PeerId, _: RequestId, _: ::Hash, _: String, _: Vec) {} fn send_changes_request(&mut self, _: &PeerId, _: RequestId, _: ::Hash, _: ::Hash, _: ::Hash, _: ::Hash, _: Option>, _: Vec) {} @@ -984,7 +1007,7 @@ pub mod tests { light_dispatch.add_request(&mut network_interface, RequestData::RemoteRead(RemoteReadRequest { header: dummy_header(), block: Default::default(), - key: b":key".to_vec(), + keys: vec![b":key".to_vec()], retry_count: None, }, tx)); @@ -992,7 +1015,7 @@ pub mod tests { id: 0, proof: vec![vec![2]], }); - assert_eq!(response.wait().unwrap().unwrap(), Some(vec![42])); + assert_eq!(response.wait().unwrap().unwrap().remove(b":key".as_ref()).unwrap(), Some(vec![42])); } #[test] @@ -1007,7 +1030,7 @@ pub mod tests { header: dummy_header(), block: Default::default(), storage_key: b":child_storage:sub".to_vec(), - key: b":key".to_vec(), + keys: vec![b":key".to_vec()], retry_count: None, }, tx)); @@ -1016,7 +1039,7 @@ pub mod tests { id: 0, proof: vec![vec![2]], }); - assert_eq!(response.wait().unwrap().unwrap(), Some(vec![42])); + assert_eq!(response.wait().unwrap().unwrap().remove(b":key".as_ref()).unwrap(), Some(vec![42])); } #[test] diff --git a/substrate/core/network/src/protocol/message.rs b/substrate/core/network/src/protocol/message.rs index 39bb94eb33..cb02845735 100644 --- a/substrate/core/network/src/protocol/message.rs +++ b/substrate/core/network/src/protocol/message.rs @@ -284,7 +284,7 @@ pub mod generic { /// Block at which to perform call. pub block: H, /// Storage key. - pub key: Vec, + pub keys: Vec>, } #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] @@ -297,7 +297,7 @@ pub mod generic { /// Child Storage key. pub storage_key: Vec, /// Storage key. - pub key: Vec, + pub keys: Vec>, } #[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)] diff --git a/substrate/core/rpc/src/state/state_light.rs b/substrate/core/rpc/src/state/state_light.rs index eb14b3fe7b..456992db66 100644 --- a/substrate/core/rpc/src/state/state_light.rs +++ b/substrate/core/rpc/src/state/state_light.rs @@ -153,9 +153,16 @@ impl StateBackend for LightState Either::Left(fetcher.remote_read(RemoteReadRequest { block: header.hash(), header, - key: key.0, + keys: vec![key.0.clone()], retry_count: Default::default(), - }).then(|result| ready(result.map(|data| data.map(StorageData)).map_err(client_err)))), + }).then(move |result| ready(result + .map(|mut data| data + .remove(&key.0) + .expect("successful result has entry for all keys; qed") + .map(StorageData) + ) + .map_err(client_err) + ))), Err(error) => Either::Right(ready(Err(error))), }); @@ -197,9 +204,16 @@ impl StateBackend for LightState Either::Right(ready(Err(error))), }); diff --git a/substrate/core/state-machine/src/lib.rs b/substrate/core/state-machine/src/lib.rs index 4008ec7c23..7b9dc6ac84 100644 --- a/substrate/core/state-machine/src/lib.rs +++ b/substrate/core/state-machine/src/lib.rs @@ -18,7 +18,10 @@ #![warn(missing_docs)] -use std::{fmt, panic::UnwindSafe, result, marker::PhantomData}; +use std::{ + fmt, result, collections::HashMap, + marker::PhantomData, panic::UnwindSafe, +}; use log::warn; use hash_db::Hasher; use codec::{Decode, Encode}; @@ -524,97 +527,130 @@ where } /// Generate storage read proof. -pub fn prove_read( +pub fn prove_read( mut backend: B, - key: &[u8] -) -> Result<(Option>, Vec>), Box> + keys: I, +) -> Result>, Box> where B: Backend, H: Hasher, H::Out: Ord, + I: IntoIterator, + I::Item: AsRef<[u8]>, { let trie_backend = backend.as_trie_backend() .ok_or_else( || Box::new(ExecutionError::UnableToGenerateProof) as Box )?; - prove_read_on_trie_backend(trie_backend, key) + prove_read_on_trie_backend(trie_backend, keys) } /// Generate child storage read proof. -pub fn prove_child_read( +pub fn prove_child_read( mut backend: B, storage_key: &[u8], - key: &[u8], -) -> Result<(Option>, Vec>), Box> + keys: I, +) -> Result>, Box> where B: Backend, H: Hasher, H::Out: Ord, + I: IntoIterator, + I::Item: AsRef<[u8]>, { let trie_backend = backend.as_trie_backend() .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?; - prove_child_read_on_trie_backend(trie_backend, storage_key, key) + prove_child_read_on_trie_backend(trie_backend, storage_key, keys) } /// Generate storage read proof on pre-created trie backend. -pub fn prove_read_on_trie_backend( +pub fn prove_read_on_trie_backend( trie_backend: &TrieBackend, - key: &[u8] -) -> Result<(Option>, Vec>), Box> + keys: I, +) -> Result>, Box> where S: trie_backend_essence::TrieBackendStorage, H: Hasher, H::Out: Ord, + I: IntoIterator, + I::Item: AsRef<[u8]>, { let proving_backend = proving_backend::ProvingBackend::<_, H>::new(trie_backend); - let result = proving_backend.storage(key).map_err(|e| Box::new(e) as Box)?; - Ok((result, proving_backend.extract_proof())) + for key in keys.into_iter() { + proving_backend + .storage(key.as_ref()) + .map_err(|e| Box::new(e) as Box)?; + } + Ok(proving_backend.extract_proof()) } /// Generate storage read proof on pre-created trie backend. -pub fn prove_child_read_on_trie_backend( +pub fn prove_child_read_on_trie_backend( trie_backend: &TrieBackend, storage_key: &[u8], - key: &[u8] -) -> Result<(Option>, Vec>), Box> + keys: I, +) -> Result>, Box> where S: trie_backend_essence::TrieBackendStorage, H: Hasher, H::Out: Ord, + I: IntoIterator, + I::Item: AsRef<[u8]>, { 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())) + for key in keys.into_iter() { + proving_backend + .child_storage(storage_key, key.as_ref()) + .map_err(|e| Box::new(e) as Box)?; + } + Ok(proving_backend.extract_proof()) } /// Check storage read proof, generated by `prove_read` call. -pub fn read_proof_check( +pub fn read_proof_check( root: H::Out, proof: Vec>, - key: &[u8], -) -> Result>, Box> + keys: I, +) -> Result, Option>>, Box> where H: Hasher, H::Out: Ord, + I: IntoIterator, + I::Item: AsRef<[u8]>, { let proving_backend = create_proof_check_backend::(root, proof)?; - read_proof_check_on_proving_backend(&proving_backend, key) + let mut result = HashMap::new(); + for key in keys.into_iter() { + let value = read_proof_check_on_proving_backend(&proving_backend, key.as_ref())?; + result.insert(key.as_ref().to_vec(), value); + } + Ok(result) } /// Check child storage read proof, generated by `prove_child_read` call. -pub fn read_child_proof_check( +pub fn read_child_proof_check( root: H::Out, proof: Vec>, storage_key: &[u8], - key: &[u8], -) -> Result>, Box> + keys: I, +) -> Result, Option>>, Box> where H: Hasher, H::Out: Ord, + I: IntoIterator, + I::Item: AsRef<[u8]>, { let proving_backend = create_proof_check_backend::(root, proof)?; - read_child_proof_check_on_proving_backend(&proving_backend, storage_key, key) + let mut result = HashMap::new(); + for key in keys.into_iter() { + let value = read_child_proof_check_on_proving_backend( + &proving_backend, + storage_key, + key.as_ref(), + )?; + result.insert(key.as_ref().to_vec(), value); + } + Ok(result) } /// Check storage read proof on pre-created proving backend. @@ -963,20 +999,23 @@ mod tests { // fetch read proof from 'remote' full node let remote_backend = trie_backend::tests::test_trie(); let remote_root = remote_backend.storage_root(::std::iter::empty()).0; - let remote_proof = prove_read(remote_backend, b"value2").unwrap().1; + let remote_proof = prove_read(remote_backend, &[b"value2"]).unwrap(); // check proof locally - let local_result1 = read_proof_check::( + let local_result1 = read_proof_check::( remote_root, remote_proof.clone(), - b"value2" + &[b"value2"], ).unwrap(); - let local_result2 = read_proof_check::( + let local_result2 = read_proof_check::( remote_root, remote_proof.clone(), - &[0xff] + &[&[0xff]], ).is_ok(); // check that results are correct - assert_eq!(local_result1, Some(vec![24])); + assert_eq!( + local_result1.into_iter().collect::>(), + vec![(b"value2".to_vec(), Some(vec![24]))], + ); assert_eq!(local_result2, false); // on child trie let remote_backend = trie_backend::tests::test_trie(); @@ -984,21 +1023,28 @@ mod tests { 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" + &[b"value3"], ).unwrap(); - let local_result2 = read_child_proof_check::( + let local_result1 = read_child_proof_check::( remote_root, remote_proof.clone(), b":child_storage:default:sub1", - b"value2" + &[b"value3"], ).unwrap(); - assert_eq!(local_result1, Some(vec![142])); - assert_eq!(local_result2, None); + let local_result2 = read_child_proof_check::( + remote_root, + remote_proof.clone(), + b":child_storage:default:sub1", + &[b"value2"], + ).unwrap(); + assert_eq!( + local_result1.into_iter().collect::>(), + vec![(b"value3".to_vec(), Some(vec![142]))], + ); + assert_eq!( + local_result2.into_iter().collect::>(), + vec![(b"value2".to_vec(), None)], + ); } #[test] diff --git a/substrate/core/test-client/src/lib.rs b/substrate/core/test-client/src/lib.rs index 3ae999f1f1..3ce5bec4fb 100644 --- a/substrate/core/test-client/src/lib.rs +++ b/substrate/core/test-client/src/lib.rs @@ -36,12 +36,9 @@ pub use state_machine::ExecutionStrategy; use std::sync::Arc; use std::collections::HashMap; -use futures::future::Ready; use hash_db::Hasher; use primitives::storage::well_known_keys; -use sr_primitives::traits::{ - Block as BlockT, NumberFor -}; +use sr_primitives::traits::Block as BlockT; use client::LocalCallExecutor; /// Test client light database backend. @@ -50,9 +47,6 @@ pub type LightBackend = client::light::backend::Backend< Blake2Hasher, >; -/// Test client light fetcher. -pub struct LightFetcher; - /// A genesis storage initialisation trait. pub trait GenesisInit: Default { /// Construct genesis storage. @@ -231,53 +225,3 @@ impl TestClientBuilder< self.build_with_executor(executor) } } - -impl client::light::fetcher::Fetcher for LightFetcher { - type RemoteHeaderResult = Ready>; - type RemoteReadResult = Ready>, client::error::Error>>; - type RemoteCallResult = Ready, client::error::Error>>; - type RemoteChangesResult = Ready, u32)>, client::error::Error>>; - type RemoteBodyResult = Ready, client::error::Error>>; - - fn remote_header( - &self, - _request: client::light::fetcher::RemoteHeaderRequest, - ) -> Self::RemoteHeaderResult { - unimplemented!("not (yet) used in tests") - } - - fn remote_read( - &self, - _request: client::light::fetcher::RemoteReadRequest, - ) -> Self::RemoteReadResult { - 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, - ) -> Self::RemoteCallResult { - unimplemented!("not (yet) used in tests") - } - - fn remote_changes( - &self, - _request: client::light::fetcher::RemoteChangesRequest, - ) -> Self::RemoteChangesResult { - unimplemented!("not (yet) used in tests") - } - - fn remote_body( - &self, - _request: client::light::fetcher::RemoteBodyRequest, - ) -> Self::RemoteBodyResult { - unimplemented!("not (yet) used in tests") - } -}