Fetching multiple remote values using single message (#3612)

* multiple remote values in single message

* keys_str: String ->keys_str: closure
This commit is contained in:
Svyatoslav Nikolsky
2019-09-16 13:30:21 +03:00
committed by GitHub
parent c45a15e559
commit 849e824652
11 changed files with 273 additions and 190 deletions
+29 -17
View File
@@ -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<Header, Hasher, BlocksI, HashesI>(
let mut storage = InMemoryState::<Hasher>::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<Header, Hasher>(
Hasher: hash_db::Hasher,
Hasher::Out: Ord,
{
do_check_proof::<Header, Hasher, _>(local_root, local_number, remote_hash, move |local_root, local_cht_key|
read_proof_check::<Hasher>(local_root, remote_proof,
local_cht_key).map_err(|e| ClientError::from(e)))
do_check_proof::<Header, Hasher, _>(
local_root,
local_number,
remote_hash,
move |local_root, local_cht_key|
read_proof_check::<Hasher, _>(
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<Header, Hasher>(
Hasher: hash_db::Hasher,
Hasher::Out: Ord,
{
do_check_proof::<Header, Hasher, _>(local_root, local_number, remote_hash, |_, local_cht_key|
read_proof_check_on_proving_backend::<Hasher>(
proving_backend, local_cht_key).map_err(|e| ClientError::from(e)))
do_check_proof::<Header, Hasher, _>(
local_root,
local_number,
remote_hash,
|_, local_cht_key|
read_proof_check_on_proving_backend::<Hasher>(
proving_backend,
local_cht_key,
).map_err(|e| ClientError::from(e)),
)
}
/// Check CHT-based header proof using passed checker function.
+12 -8
View File
@@ -436,24 +436,28 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
}
/// Reads storage value at a given block + key, returning read proof.
pub fn read_proof(&self, id: &BlockId<Block>, key: &[u8]) -> error::Result<Vec<Vec<u8>>> {
pub fn read_proof<I>(&self, id: &BlockId<Block>, keys: I) -> error::Result<Vec<Vec<u8>>> 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<I>(
&self,
id: &BlockId<Block>,
storage_key: &[u8],
key: &[u8]
) -> error::Result<Vec<Vec<u8>>> {
keys: I,
) -> error::Result<Vec<Vec<u8>>> 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))
}
+27 -21
View File
@@ -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: HeaderT> {
/// Header of block at which read is performed.
pub header: Header,
/// Storage key to read.
pub key: Vec<u8>,
pub keys: Vec<Vec<u8>>,
/// Number of times to retry request. None means that default RETRY_COUNT is used.
pub retry_count: Option<usize>,
}
@@ -88,7 +88,7 @@ pub struct RemoteReadChildRequest<Header: HeaderT> {
/// Storage key for child.
pub storage_key: Vec<u8>,
/// Child storage key to read.
pub key: Vec<u8>,
pub keys: Vec<Vec<u8>>,
/// Number of times to retry request. None means that default RETRY_COUNT is used.
pub retry_count: Option<usize>,
}
@@ -147,7 +147,7 @@ pub trait Fetcher<Block: BlockT>: Send + Sync {
/// Remote header future.
type RemoteHeaderResult: Future<Output = Result<Block::Header, ClientError>> + Send + 'static;
/// Remote storage read future.
type RemoteReadResult: Future<Output = Result<Option<Vec<u8>>, ClientError>> + Send + 'static;
type RemoteReadResult: Future<Output = Result<HashMap<Vec<u8>, Option<Vec<u8>>>, ClientError>> + Send + 'static;
/// Remote call result future.
type RemoteCallResult: Future<Output = Result<Vec<u8>, ClientError>> + Send + 'static;
/// Remote changes result future.
@@ -193,13 +193,13 @@ pub trait FetchChecker<Block: BlockT>: Send + Sync {
&self,
request: &RemoteReadRequest<Block::Header>,
remote_proof: Vec<Vec<u8>>
) -> ClientResult<Option<Vec<u8>>>;
) -> ClientResult<HashMap<Vec<u8>, Option<Vec<u8>>>>;
/// Check remote storage read proof.
fn check_read_child_proof(
&self,
request: &RemoteReadChildRequest<Block::Header>,
remote_proof: Vec<Vec<u8>>
) -> ClientResult<Option<Vec<u8>>>;
) -> ClientResult<HashMap<Vec<u8>, Option<Vec<u8>>>>;
/// Check remote method execution proof.
fn check_execution_proof(
&self,
@@ -395,23 +395,26 @@ impl<E, Block, H, S> FetchChecker<Block> for LightDataChecker<E, H, Block, S>
fn check_read_proof(
&self,
request: &RemoteReadRequest<Block::Header>,
remote_proof: Vec<Vec<u8>>
) -> ClientResult<Option<Vec<u8>>> {
read_proof_check::<H>(convert_hash(request.header.state_root()), remote_proof, &request.key)
.map_err(Into::into)
remote_proof: Vec<Vec<u8>>,
) -> ClientResult<HashMap<Vec<u8>, Option<Vec<u8>>>> {
read_proof_check::<H, _>(
convert_hash(request.header.state_root()),
remote_proof,
request.keys.iter(),
).map_err(Into::into)
}
fn check_read_child_proof(
&self,
request: &RemoteReadChildRequest<Block::Header>,
remote_proof: Vec<Vec<u8>>
) -> ClientResult<Option<Vec<u8>>> {
read_child_proof_check::<H>(
) -> ClientResult<HashMap<Vec<u8>, Option<Vec<u8>>>> {
read_child_proof_check::<H, _>(
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<Block> for OkCallFetcher {
type RemoteHeaderResult = Ready<Result<Header, ClientError>>;
type RemoteReadResult = Ready<Result<Option<Vec<u8>>, ClientError>>;
type RemoteReadResult = Ready<Result<HashMap<Vec<u8>, Option<Vec<u8>>>, ClientError>>;
type RemoteCallResult = Ready<Result<Vec<u8>, ClientError>>;
type RemoteChangesResult = Ready<Result<Vec<(NumberFor<Block>, u32)>, ClientError>>;
type RemoteBodyResult = Ready<Result<Vec<Extrinsic>, 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::<Block>::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<Block>).check_read_proof(&RemoteReadRequest::<Header> {
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]
+11 -6
View File
@@ -49,10 +49,15 @@ pub trait Client<Block: BlockT>: Send + Sync {
fn header_proof(&self, block_number: <Block::Header as HeaderT>::Number) -> Result<(Block::Header, Vec<Vec<u8>>), Error>;
/// Get storage read execution proof.
fn read_proof(&self, block: &Block::Hash, key: &[u8]) -> Result<Vec<Vec<u8>>, Error>;
fn read_proof(&self, block: &Block::Hash, keys: &[Vec<u8>]) -> Result<Vec<Vec<u8>>, Error>;
/// Get child storage read execution proof.
fn read_child_proof(&self, block: &Block::Hash, storage_key: &[u8], key: &[u8]) -> Result<Vec<Vec<u8>>, Error>;
fn read_child_proof(
&self,
block: &Block::Hash,
storage_key: &[u8],
keys: &[Vec<u8>],
) -> Result<Vec<Vec<u8>>, Error>;
/// Get method execution proof.
fn execution_proof(&self, block: &Block::Hash, method: &str, data: &[u8]) -> Result<(Vec<u8>, Vec<Vec<u8>>), Error>;
@@ -113,18 +118,18 @@ impl<B, E, Block, RA> Client<Block> for SubstrateClient<B, E, Block, RA> where
(self as &SubstrateClient<B, E, Block, RA>).header_proof(&BlockId::Number(block_number))
}
fn read_proof(&self, block: &Block::Hash, key: &[u8]) -> Result<Vec<Vec<u8>>, Error> {
(self as &SubstrateClient<B, E, Block, RA>).read_proof(&BlockId::Hash(block.clone()), key)
fn read_proof(&self, block: &Block::Hash, keys: &[Vec<u8>]) -> Result<Vec<Vec<u8>>, Error> {
(self as &SubstrateClient<B, E, Block, RA>).read_proof(&BlockId::Hash(block.clone()), keys)
}
fn read_child_proof(
&self,
block: &Block::Hash,
storage_key: &[u8],
key: &[u8]
keys: &[Vec<u8>],
) -> Result<Vec<Vec<u8>>, Error> {
(self as &SubstrateClient<B, E, Block, RA>)
.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<u8>, Vec<Vec<u8>>), Error> {
@@ -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<B> Fetcher<B> for OnDemand<B> where
B::Header: HeaderT,
{
type RemoteHeaderResult = Compat01As03<RemoteResponse<B::Header>>;
type RemoteReadResult = Compat01As03<RemoteResponse<Option<Vec<u8>>>>;
type RemoteReadResult = Compat01As03<RemoteResponse<HashMap<Vec<u8>, Option<Vec<u8>>>>>;
type RemoteCallResult = Compat01As03<RemoteResponse<Vec<u8>>>;
type RemoteChangesResult = Compat01As03<RemoteResponse<Vec<(NumberFor<B>, u32)>>>;
type RemoteBodyResult = Compat01As03<RemoteResponse<Vec<B::Extrinsic>>>;
+38 -10
View File
@@ -172,11 +172,17 @@ impl<'a, B: BlockT> LightDispatchNetwork<B> for LightDispatchIn<'a, B> {
self.behaviour.send_packet(who, message)
}
fn send_read_request(&mut self, who: &PeerId, id: RequestId, block: <B as BlockT>::Hash, key: Vec<u8>) {
fn send_read_request(
&mut self,
who: &PeerId,
id: RequestId,
block: <B as BlockT>::Hash,
keys: Vec<Vec<u8>>,
) {
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<B> for LightDispatchIn<'a, B> {
id: RequestId,
block: <B as BlockT>::Hash,
storage_key: Vec<u8>,
key: Vec<u8>
keys: Vec<Vec<u8>>,
) {
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<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
who: PeerId,
request: message::RemoteReadRequest<B::Hash>,
) {
let keys_str = || match request.keys.len() {
1 => request.keys[0].to_hex::<String>(),
_ => format!(
"{}..{}",
request.keys[0].to_hex::<String>(),
request.keys[request.keys.len() - 1].to_hex::<String>(),
),
};
trace!(target: "sync", "Remote read request {} from {} ({} at {})",
request.id, who, request.key.to_hex::<String>(), 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::<String>(),
keys_str(),
request.block,
error
);
@@ -1301,16 +1316,29 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
who: PeerId,
request: message::RemoteReadChildRequest<B::Hash>,
) {
let keys_str = || match request.keys.len() {
1 => request.keys[0].to_hex::<String>(),
_ => format!(
"{}..{}",
request.keys[0].to_hex::<String>(),
request.keys[request.keys.len() - 1].to_hex::<String>(),
),
};
trace!(target: "sync", "Remote read child request {} from {} ({} {} at {})",
request.id, who, request.storage_key.to_hex::<String>(), request.key.to_hex::<String>(), 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::<String>(), 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::<String>(),
request.key.to_hex::<String>(),
keys_str(),
request.block,
error
);
@@ -53,7 +53,13 @@ pub trait LightDispatchNetwork<B: BlockT> {
fn send_header_request(&mut self, who: &PeerId, id: RequestId, block: <<B as BlockT>::Header as HeaderT>::Number);
/// Send to `who` a read request.
fn send_read_request(&mut self, who: &PeerId, id: RequestId, block: <B as BlockT>::Hash, key: Vec<u8>);
fn send_read_request(
&mut self,
who: &PeerId,
id: RequestId,
block: <B as BlockT>::Hash,
keys: Vec<Vec<u8>>,
);
/// Send to `who` a child read request.
fn send_read_child_request(
@@ -62,7 +68,7 @@ pub trait LightDispatchNetwork<B: BlockT> {
id: RequestId,
block: <B as BlockT>::Hash,
storage_key: Vec<u8>,
key: Vec<u8>
keys: Vec<Vec<u8>>,
);
/// Send to `who` a call request.
@@ -135,10 +141,13 @@ struct Request<Block: BlockT> {
pub(crate) enum RequestData<Block: BlockT> {
RemoteBody(RemoteBodyRequest<Block::Header>, OneShotSender<Result<Vec<Block::Extrinsic>, ClientError>>),
RemoteHeader(RemoteHeaderRequest<Block::Header>, OneShotSender<Result<Block::Header, ClientError>>),
RemoteRead(RemoteReadRequest<Block::Header>, OneShotSender<Result<Option<Vec<u8>>, ClientError>>),
RemoteRead(
RemoteReadRequest<Block::Header>,
OneShotSender<Result<HashMap<Vec<u8>, Option<Vec<u8>>>, ClientError>>,
),
RemoteReadChild(
RemoteReadChildRequest<Block::Header>,
OneShotSender<Result<Option<Vec<u8>>, ClientError>>
OneShotSender<Result<HashMap<Vec<u8>, Option<Vec<u8>>>, ClientError>>
),
RemoteCall(RemoteCallRequest<Block::Header>, OneShotSender<Result<Vec<u8>, ClientError>>),
RemoteChanges(
@@ -174,7 +183,7 @@ impl<Block: BlockT> FetchChecker<Block> for AlwaysBadChecker {
&self,
_request: &RemoteReadRequest<Block::Header>,
_remote_proof: Vec<Vec<u8>>
) -> Result<Option<Vec<u8>>, ClientError> {
) -> Result<HashMap<Vec<u8>,Option<Vec<u8>>>, ClientError> {
Err(ClientError::Msg("AlwaysBadChecker".into()))
}
@@ -182,7 +191,7 @@ impl<Block: BlockT> FetchChecker<Block> for AlwaysBadChecker {
&self,
_request: &RemoteReadChildRequest<Block::Header>,
_remote_proof: Vec<Vec<u8>>
) -> Result<Option<Vec<u8>>, ClientError> {
) -> Result<HashMap<Vec<u8>, Option<Vec<u8>>>, ClientError> {
Err(ClientError::Msg("AlwaysBadChecker".into()))
}
@@ -604,7 +613,7 @@ impl<Block: BlockT> Request<Block> {
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<Block: BlockT> Request<Block> {
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<Block: BlockT> RequestData<Block> {
#[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<Header>, _: Vec<Vec<u8>>) -> ClientResult<Option<Vec<u8>>> {
fn check_read_proof(
&self,
request: &RemoteReadRequest<Header>,
_: Vec<Vec<u8>>,
) -> ClientResult<HashMap<Vec<u8>, Option<Vec<u8>>>> {
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<Header>,
request: &RemoteReadChildRequest<Header>,
_: Vec<Vec<u8>>
) -> ClientResult<Option<Vec<u8>>> {
) -> ClientResult<HashMap<Vec<u8>, Option<Vec<u8>>>> {
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, _: <<B as BlockT>::Header as HeaderT>::Number) {}
fn send_read_request(&mut self, _: &PeerId, _: RequestId, _: <B as BlockT>::Hash, _: Vec<u8>) {}
fn send_read_request(&mut self, _: &PeerId, _: RequestId, _: <B as BlockT>::Hash, _: Vec<Vec<u8>>) {}
fn send_read_child_request(&mut self, _: &PeerId, _: RequestId, _: <B as BlockT>::Hash, _: Vec<u8>,
_: Vec<u8>) {}
_: Vec<Vec<u8>>) {}
fn send_call_request(&mut self, _: &PeerId, _: RequestId, _: <B as BlockT>::Hash, _: String, _: Vec<u8>) {}
fn send_changes_request(&mut self, _: &PeerId, _: RequestId, _: <B as BlockT>::Hash, _: <B as BlockT>::Hash,
_: <B as BlockT>::Hash, _: <B as BlockT>::Hash, _: Option<Vec<u8>>, _: Vec<u8>) {}
@@ -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]
@@ -284,7 +284,7 @@ pub mod generic {
/// Block at which to perform call.
pub block: H,
/// Storage key.
pub key: Vec<u8>,
pub keys: Vec<Vec<u8>>,
}
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
@@ -297,7 +297,7 @@ pub mod generic {
/// Child Storage key.
pub storage_key: Vec<u8>,
/// Storage key.
pub key: Vec<u8>,
pub keys: Vec<Vec<u8>>,
}
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
+18 -4
View File
@@ -153,9 +153,16 @@ impl<Block, F, B, E, RA> StateBackend<B, E, Block, RA> for LightState<Block, F,
Ok(header) => 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<Block, F, B, E, RA> StateBackend<B, E, Block, RA> for LightState<Block, F,
block: header.hash(),
header,
storage_key: child_storage_key.0,
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))),
});
+90 -44
View File
@@ -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<B, H>(
pub fn prove_read<B, H, I>(
mut backend: B,
key: &[u8]
) -> Result<(Option<Vec<u8>>, Vec<Vec<u8>>), Box<dyn Error>>
keys: I,
) -> Result<Vec<Vec<u8>>, Box<dyn Error>>
where
B: Backend<H>,
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<dyn Error>
)?;
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<B, H>(
pub fn prove_child_read<B, H, I>(
mut backend: B,
storage_key: &[u8],
key: &[u8],
) -> Result<(Option<Vec<u8>>, Vec<Vec<u8>>), Box<dyn Error>>
keys: I,
) -> Result<Vec<Vec<u8>>, Box<dyn Error>>
where
B: Backend<H>,
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<dyn Error>)?;
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<S, H>(
pub fn prove_read_on_trie_backend<S, H, I>(
trie_backend: &TrieBackend<S, H>,
key: &[u8]
) -> Result<(Option<Vec<u8>>, Vec<Vec<u8>>), Box<dyn Error>>
keys: I,
) -> Result<Vec<Vec<u8>>, Box<dyn Error>>
where
S: trie_backend_essence::TrieBackendStorage<H>,
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<dyn Error>)?;
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<dyn Error>)?;
}
Ok(proving_backend.extract_proof())
}
/// Generate storage read proof on pre-created trie backend.
pub fn prove_child_read_on_trie_backend<S, H>(
pub fn prove_child_read_on_trie_backend<S, H, I>(
trie_backend: &TrieBackend<S, H>,
storage_key: &[u8],
key: &[u8]
) -> Result<(Option<Vec<u8>>, Vec<Vec<u8>>), Box<dyn Error>>
keys: I,
) -> Result<Vec<Vec<u8>>, Box<dyn Error>>
where
S: trie_backend_essence::TrieBackendStorage<H>,
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<dyn Error>)?;
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<dyn Error>)?;
}
Ok(proving_backend.extract_proof())
}
/// Check storage read proof, generated by `prove_read` call.
pub fn read_proof_check<H>(
pub fn read_proof_check<H, I>(
root: H::Out,
proof: Vec<Vec<u8>>,
key: &[u8],
) -> Result<Option<Vec<u8>>, Box<dyn Error>>
keys: I,
) -> Result<HashMap<Vec<u8>, Option<Vec<u8>>>, Box<dyn Error>>
where
H: Hasher,
H::Out: Ord,
I: IntoIterator,
I::Item: AsRef<[u8]>,
{
let proving_backend = create_proof_check_backend::<H>(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<H>(
pub fn read_child_proof_check<H, I>(
root: H::Out,
proof: Vec<Vec<u8>>,
storage_key: &[u8],
key: &[u8],
) -> Result<Option<Vec<u8>>, Box<dyn Error>>
keys: I,
) -> Result<HashMap<Vec<u8>, Option<Vec<u8>>>, Box<dyn Error>>
where
H: Hasher,
H::Out: Ord,
I: IntoIterator,
I::Item: AsRef<[u8]>,
{
let proving_backend = create_proof_check_backend::<H>(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::<Blake2Hasher>(
let local_result1 = read_proof_check::<Blake2Hasher, _>(
remote_root,
remote_proof.clone(),
b"value2"
&[b"value2"],
).unwrap();
let local_result2 = read_proof_check::<Blake2Hasher>(
let local_result2 = read_proof_check::<Blake2Hasher, _>(
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<_>>(),
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::<Blake2Hasher>(
remote_root,
remote_proof.clone(),
b":child_storage:default:sub1",b"value3"
&[b"value3"],
).unwrap();
let local_result2 = read_child_proof_check::<Blake2Hasher>(
let local_result1 = read_child_proof_check::<Blake2Hasher, _>(
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::<Blake2Hasher, _>(
remote_root,
remote_proof.clone(),
b":child_storage:default:sub1",
&[b"value2"],
).unwrap();
assert_eq!(
local_result1.into_iter().collect::<Vec<_>>(),
vec![(b"value3".to_vec(), Some(vec![142]))],
);
assert_eq!(
local_result2.into_iter().collect::<Vec<_>>(),
vec![(b"value2".to_vec(), None)],
);
}
#[test]
+1 -57
View File
@@ -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<Block> = 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<E, Backend, G: GenesisInit> TestClientBuilder<
self.build_with_executor(executor)
}
}
impl<Block: BlockT> client::light::fetcher::Fetcher<Block> for LightFetcher {
type RemoteHeaderResult = Ready<Result<Block::Header, client::error::Error>>;
type RemoteReadResult = Ready<Result<Option<Vec<u8>>, client::error::Error>>;
type RemoteCallResult = Ready<Result<Vec<u8>, client::error::Error>>;
type RemoteChangesResult = Ready<Result<Vec<(NumberFor<Block>, u32)>, client::error::Error>>;
type RemoteBodyResult = Ready<Result<Vec<Block::Extrinsic>, client::error::Error>>;
fn remote_header(
&self,
_request: client::light::fetcher::RemoteHeaderRequest<Block::Header>,
) -> Self::RemoteHeaderResult {
unimplemented!("not (yet) used in tests")
}
fn remote_read(
&self,
_request: client::light::fetcher::RemoteReadRequest<Block::Header>,
) -> Self::RemoteReadResult {
unimplemented!("not (yet) used in tests")
}
fn remote_read_child(
&self,
_request: client::light::fetcher::RemoteReadChildRequest<Block::Header>,
) -> Self::RemoteReadResult {
unimplemented!("not (yet) used in tests")
}
fn remote_call(
&self,
_request: client::light::fetcher::RemoteCallRequest<Block::Header>,
) -> Self::RemoteCallResult {
unimplemented!("not (yet) used in tests")
}
fn remote_changes(
&self,
_request: client::light::fetcher::RemoteChangesRequest<Block::Header>,
) -> Self::RemoteChangesResult {
unimplemented!("not (yet) used in tests")
}
fn remote_body(
&self,
_request: client::light::fetcher::RemoteBodyRequest<Block::Header>,
) -> Self::RemoteBodyResult {
unimplemented!("not (yet) used in tests")
}
}