Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Add childstate_getStorageEntries RPC #9459

Merged
19 commits merged into from
Sep 13, 2021
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
d2ad280
Add storage query functions for multiple keys
hirschenberger Jul 29, 2021
993a877
Merge branch 'master' of https://github.com/paritytech/substrate into…
hirschenberger Aug 2, 2021
ab725c8
Query all keys in one request and add more tests
hirschenberger Aug 3, 2021
3a3c1ae
Merge branch 'master' of https://github.com/paritytech/substrate into…
hirschenberger Aug 3, 2021
a23ed69
Make it compatible with stable release channel
hirschenberger Aug 3, 2021
fa146dc
Merge branch 'master' of https://github.com/paritytech/substrate into…
hirschenberger Aug 30, 2021
d73a745
Update to new futures
hirschenberger Aug 31, 2021
0cbe5eb
Update client/rpc/src/state/state_full.rs
hirschenberger Sep 9, 2021
cd4c158
Update client/rpc/src/state/state_full.rs
hirschenberger Sep 9, 2021
3199a10
Update client/rpc/src/state/state_full.rs
hirschenberger Sep 9, 2021
a1b6eed
Update client/rpc/src/state/state_full.rs
hirschenberger Sep 9, 2021
83251ad
Update client/rpc/src/state/state_full.rs
hirschenberger Sep 9, 2021
58fe359
Update client/rpc/src/state/state_light.rs
hirschenberger Sep 9, 2021
f3e3ce3
Update client/rpc/src/state/state_light.rs
hirschenberger Sep 9, 2021
d618704
Satisfy borrowck
hirschenberger Sep 9, 2021
d840015
Remove non-RPC `storage_entries` functions.
hirschenberger Sep 9, 2021
5813b43
Revert "Remove non-RPC `storage_entries` functions."
hirschenberger Sep 9, 2021
a001771
Revert "Revert "Remove non-RPC `storage_entries` functions.""
hirschenberger Sep 9, 2021
19f5d24
Finally some formatting
hirschenberger Sep 9, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions client/rpc-api/src/child_state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@ pub trait ChildStateApi<Hash> {
hash: Option<Hash>,
) -> FutureResult<Option<StorageData>>;

/// Returns child storage entries for multiple keys at a specific block's state.
#[rpc(name = "childstate_getStorageEntries")]
fn storage_entries(
&self,
child_storage_key: PrefixedStorageKey,
keys: Vec<StorageKey>,
hash: Option<Hash>,
) -> FutureResult<Vec<Option<StorageData>>>;

/// Returns the hash of a child storage entry at a block's state.
#[rpc(name = "childstate_getStorageHash")]
fn storage_hash(
Expand Down
24 changes: 24 additions & 0 deletions client/rpc/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ where
key: StorageKey,
) -> FutureResult<Option<StorageData>>;

/// Returns storage entries for multiple keys at a specific block's state.
fn storage_entries(
&self,
block: Option<Block::Hash>,
keys: Vec<StorageKey>,
) -> FutureResult<Vec<Option<StorageData>>>;

hirschenberger marked this conversation as resolved.
Show resolved Hide resolved
/// Returns the hash of a storage entry at a block's state.
fn storage_hash(
&self,
Expand Down Expand Up @@ -465,6 +472,14 @@ where
key: StorageKey,
) -> FutureResult<Option<StorageData>>;

/// Returns child storage entries at a specific block's state.
fn storage_entries(
&self,
block: Option<Block::Hash>,
storage_key: PrefixedStorageKey,
keys: Vec<StorageKey>,
) -> FutureResult<Vec<Option<StorageData>>>;

/// Returns the hash of a child storage entry at a block's state.
fn storage_hash(
&self,
Expand Down Expand Up @@ -516,6 +531,15 @@ where
self.backend.storage(block, storage_key, key)
}

fn storage_entries(
&self,
storage_key: PrefixedStorageKey,
keys: Vec<StorageKey>,
block: Option<Block::Hash>,
) -> FutureResult<Vec<Option<StorageData>>> {
self.backend.storage_entries(block, storage_key, keys)
}

fn storage_keys(
&self,
storage_key: PrefixedStorageKey,
Expand Down
53 changes: 52 additions & 1 deletion client/rpc/src/state/state_full.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@

//! State API backend for full nodes.

use futures::{future, stream, FutureExt, SinkExt, StreamExt};
use futures::{
future,
future::{err, try_join_all},
stream, FutureExt, SinkExt, StreamExt,
};
use jsonrpc_pubsub::{manager::SubscriptionManager, typed::Subscriber, SubscriptionId};
use log::warn;
use rpc::Result as RpcResult;
Expand Down Expand Up @@ -356,6 +360,24 @@ where
async move { r }.boxed()
}

fn storage_entries(
&self,
block: Option<Block::Hash>,
keys: Vec<StorageKey>,
) -> FutureResult<Vec<Option<StorageData>>> {
let block = match self.block_or_best(block) {
Ok(b) => b,
Err(e) => return Box::pin(err(client_err(e))),
hirschenberger marked this conversation as resolved.
Show resolved Hide resolved
};
let client = self.client.clone();
try_join_all(keys.into_iter().map(move |key| {
let client = client.clone();

async move { client.clone().storage(&BlockId::Hash(block), &key).map_err(client_err) }
hirschenberger marked this conversation as resolved.
Show resolved Hide resolved
}))
.boxed()
}

fn storage_size(
&self,
block: Option<Block::Hash>,
Expand Down Expand Up @@ -715,6 +737,35 @@ where
async move { r }.boxed()
}

fn storage_entries(
&self,
block: Option<Block::Hash>,
storage_key: PrefixedStorageKey,
keys: Vec<StorageKey>,
) -> FutureResult<Vec<Option<StorageData>>> {
let child_info = match ChildType::from_prefixed_key(&storage_key) {
Some((ChildType::ParentKeyId, storage_key)) =>
Arc::new(ChildInfo::new_default(storage_key)),
None => return Box::pin(err(client_err(sp_blockchain::Error::InvalidChildStorageKey))),
hirschenberger marked this conversation as resolved.
Show resolved Hide resolved
};
let block = match self.block_or_best(block) {
Ok(b) => b,
Err(e) => return Box::pin(err(client_err(e))),
hirschenberger marked this conversation as resolved.
Show resolved Hide resolved
};
let client = self.client.clone();
try_join_all(keys.into_iter().map(move |key| {
let client = client.clone();
let child_info = child_info.clone();
async move {
client
.clone()
.child_storage(&BlockId::Hash(block), &child_info, &key)
.map_err(client_err)
}
hirschenberger marked this conversation as resolved.
Show resolved Hide resolved
}))
.boxed()
}

fn storage_hash(
&self,
block: Option<Block::Hash>,
Expand Down
61 changes: 61 additions & 0 deletions client/rpc/src/state/state_light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,17 @@ where
.boxed()
}

fn storage_entries(
&self,
block: Option<Block::Hash>,
keys: Vec<StorageKey>,
) -> FutureResult<Vec<Option<StorageData>>> {
let keys = keys.iter().map(|k| k.0.clone()).collect::<Vec<_>>();
storage(&*self.remote_blockchain, self.fetcher.clone(), self.block_or_best(block), keys)
.map_ok(|v| v.into_iter().map(|x| x.1).collect::<Vec<_>>())
.boxed()
}

fn storage_hash(
&self,
block: Option<Block::Hash>,
Expand Down Expand Up @@ -531,6 +542,56 @@ where
child_storage.boxed()
}

fn storage_entries(
&self,
block: Option<Block::Hash>,
storage_key: PrefixedStorageKey,
keys: Vec<StorageKey>,
) -> FutureResult<Vec<Option<StorageData>>> {
let block = self.block_or_best(block);
let fetcher = self.fetcher.clone();
let keys = keys.iter().map(|k| k.0.clone()).collect::<Vec<_>>();
let child_storage =
resolve_header(&*self.remote_blockchain, &*self.fetcher, block).then(move |result| {
match result {
Ok(header) => Either::Left(
fetcher
.remote_read_child(RemoteReadChildRequest {
block,
header,
storage_key,
keys: keys.clone(),
retry_count: Default::default(),
})
.then(move |result| {
ready(
result
.map(|data| {
data.iter()
.filter_map(|(k, d)| {
if keys.contains(k) {
if let Some(d) = d {
return Some(Some(StorageData(
d.to_vec(),
)))
}
};

None
hirschenberger marked this conversation as resolved.
Show resolved Hide resolved
})
.collect::<Vec<_>>()
})
.map_err(client_err),
)
}),
),
Err(error) => Either::Right(ready(Err(error))),
}
});

Box::pin(child_storage.boxed())
hirschenberger marked this conversation as resolved.
Show resolved Hide resolved
}

fn storage_hash(
&self,
block: Option<Block::Hash>,
Expand Down
108 changes: 108 additions & 0 deletions client/rpc/src/state/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,54 @@ fn should_return_storage() {
);
}

#[test]
fn should_return_storage_entries() {
const KEY1: &[u8] = b":mock";
const KEY2: &[u8] = b":turtle";
const VALUE: &[u8] = b"hello world";
const CHILD_VALUE1: &[u8] = b"hello world !";
const CHILD_VALUE2: &[u8] = b"hello world !";

let child_info = ChildInfo::new_default(STORAGE_KEY);
let client = TestClientBuilder::new()
.add_extra_storage(KEY1.to_vec(), VALUE.to_vec())
.add_extra_child_storage(&child_info, KEY1.to_vec(), CHILD_VALUE1.to_vec())
.add_extra_child_storage(&child_info, KEY2.to_vec(), CHILD_VALUE2.to_vec())
.build();
let genesis_hash = client.genesis_hash();
let (_client, child) = new_full(
Arc::new(client),
SubscriptionManager::new(Arc::new(TaskExecutor)),
DenyUnsafe::No,
None,
);

let keys = &[StorageKey(KEY1.to_vec()), StorageKey(KEY2.to_vec())];
assert_eq!(
executor::block_on(child.storage_entries(
prefixed_storage_key(),
keys.to_vec(),
Some(genesis_hash).into()
))
.map(|x| x.into_iter().map(|x| x.map(|x| x.0.len()).unwrap()).sum::<usize>())
.unwrap(),
CHILD_VALUE1.len() + CHILD_VALUE2.len()
);

// should fail if not all keys exist.
let mut failing_keys = vec![StorageKey(b":soup".to_vec())];
failing_keys.extend_from_slice(keys);
assert_matches!(
executor::block_on(child.storage_entries(
prefixed_storage_key(),
failing_keys,
Some(genesis_hash).into()
))
.map(|x| x.iter().all(|x| x.is_some())),
Ok(false)
);
}

#[test]
fn should_return_child_storage() {
let child_info = ChildInfo::new_default(STORAGE_KEY);
Expand All @@ -115,6 +163,19 @@ fn should_return_child_storage() {
)),
Ok(Some(StorageData(ref d))) if d[0] == 42 && d.len() == 1
);

// should fail if key does not exist.
let failing_key = StorageKey(b":soup".to_vec());
assert_matches!(
executor::block_on(child.storage(
prefixed_storage_key(),
failing_key,
Some(genesis_hash).into()
))
.map(|x| x.is_some()),
Ok(false)
);

assert_matches!(
executor::block_on(child.storage_hash(
child_key.clone(),
Expand All @@ -130,6 +191,53 @@ fn should_return_child_storage() {
);
}

#[test]
fn should_return_child_storage_entries() {
let child_info = ChildInfo::new_default(STORAGE_KEY);
let client = Arc::new(
substrate_test_runtime_client::TestClientBuilder::new()
.add_child_storage(&child_info, "key1", vec![42_u8])
.add_child_storage(&child_info, "key2", vec![43_u8, 44])
.build(),
);
let genesis_hash = client.genesis_hash();
let (_client, child) =
new_full(client, SubscriptionManager::new(Arc::new(TaskExecutor)), DenyUnsafe::No, None);
let child_key = prefixed_storage_key();
let keys = vec![StorageKey(b"key1".to_vec()), StorageKey(b"key2".to_vec())];

let res = executor::block_on(child.storage_entries(
child_key.clone(),
keys.clone(),
Some(genesis_hash).into(),
))
.unwrap();

assert_matches!(
res[0],
Some(StorageData(ref d))
if d[0] == 42 && d.len() == 1
);
assert_matches!(
res[1],
Some(StorageData(ref d))
if d[0] == 43 && d[1] == 44 && d.len() == 2
);
assert_matches!(
executor::block_on(child.storage_hash(
child_key.clone(),
keys[0].clone(),
Some(genesis_hash).into()
))
.map(|x| x.is_some()),
Ok(true)
);
assert_matches!(
executor::block_on(child.storage_size(child_key.clone(), keys[0].clone(), None)),
Ok(Some(1))
);
}

#[test]
fn should_call_contract() {
let client = Arc::new(substrate_test_runtime_client::new());
Expand Down