Skip to content

Commit

Permalink
do not return empty entries from state_queryStorage
Browse files Browse the repository at this point in the history
  • Loading branch information
svyatonik committed Jun 19, 2019
1 parent 1595a5e commit 0475ed9
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 31 deletions.
2 changes: 2 additions & 0 deletions core/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,8 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where

/// Get pairs of (block, extrinsic) where key has been changed at given blocks range.
/// Works only for runtimes that are supporting changes tries.
///
/// Changes are returned in descending order (i.e. last block comes first).
pub fn key_changes(
&self,
first: NumberFor<Block>,
Expand Down
31 changes: 22 additions & 9 deletions core/rpc/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,25 +258,29 @@ impl<B, E, Block: BlockT, RA> State<B, E, Block, RA> where
&self,
range: &QueryStorageRange<Block>,
keys: &[StorageKey],
last_values: &mut HashMap<StorageKey, Option<StorageData>>,
changes: &mut Vec<StorageChangeSet<Block::Hash>>,
) -> Result<()> {
let mut last_state: HashMap<_, Option<_>> = Default::default();
for block in range.unfiltered_range.start..range.unfiltered_range.end {
let block_hash = range.hashes[block].clone();
let mut block_changes = StorageChangeSet { block: block_hash.clone(), changes: Vec::new() };
let id = BlockId::hash(block_hash);
for key in keys {
let (has_changed, data) = {
let curr_data = self.client.storage(&id, key)?;
let prev_data = last_state.get(key).and_then(|x| x.as_ref());
(curr_data.as_ref() != prev_data, curr_data)
match last_values.get(key) {
Some(prev_data) => (curr_data != *prev_data, curr_data),
None => (true, curr_data),
}
};
if has_changed {
block_changes.changes.push((key.clone(), data.clone()));
}
last_state.insert(key.clone(), data);
last_values.insert(key.clone(), data);
}
if !block_changes.changes.is_empty() {
changes.push(block_changes);
}
changes.push(block_changes);
}
Ok(())
}
Expand All @@ -286,6 +290,7 @@ impl<B, E, Block: BlockT, RA> State<B, E, Block, RA> where
&self,
range: &QueryStorageRange<Block>,
keys: &[StorageKey],
last_values: &HashMap<StorageKey, Option<StorageData>>,
changes: &mut Vec<StorageChangeSet<Block::Hash>>,
) -> Result<()> {
let (begin, end) = match range.filtered_range {
Expand All @@ -298,17 +303,24 @@ impl<B, E, Block: BlockT, RA> State<B, E, Block, RA> where
let mut changes_map: BTreeMap<NumberFor<Block>, StorageChangeSet<Block::Hash>> = BTreeMap::new();
for key in keys {
let mut last_block = None;
for (block, _) in self.client.key_changes(begin, end, key)? {
let mut last_value = last_values.get(key).cloned().unwrap_or_default();
for (block, _) in self.client.key_changes(begin, end, key)?.into_iter().rev() {
if last_block == Some(block) {
continue;
}

let block_hash = range.hashes[(block - range.first_number).saturated_into::<usize>()].clone();
let id = BlockId::Hash(block_hash);
let value_at_block = self.client.storage(&id, key)?;
if last_value == value_at_block {
continue;
}

changes_map.entry(block)
.or_insert_with(|| StorageChangeSet { block: block_hash, changes: Vec::new() })
.changes.push((key.clone(), value_at_block));
.changes.push((key.clone(), value_at_block.clone()));
last_block = Some(block);
last_value = value_at_block;
}
}
if let Some(additional_capacity) = changes_map.len().checked_sub(changes.len()) {
Expand Down Expand Up @@ -432,8 +444,9 @@ impl<B, E, Block, RA> StateApi<Block::Hash> for State<B, E, Block, RA> where
) -> Result<Vec<StorageChangeSet<Block::Hash>>> {
let range = self.split_query_storage_range(from, to)?;
let mut changes = Vec::new();
self.query_storage_unfiltered(&range, &keys, &mut changes)?;
self.query_storage_filtered(&range, &keys, &mut changes)?;
let mut last_values = HashMap::new();
self.query_storage_unfiltered(&range, &keys, &mut last_values, &mut changes)?;
self.query_storage_filtered(&range, &keys, &last_values, &mut changes)?;
Ok(changes)
}

Expand Down
46 changes: 24 additions & 22 deletions core/rpc/src/state/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,16 @@ fn should_query_storage() {

let add_block = |nonce| {
let mut builder = client.new_block(Default::default()).unwrap();
builder.push_transfer(runtime::Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 42,
nonce,
}).unwrap();
// fake change: None -> None -> None
builder.push_storage_change(vec![1], None).unwrap();
// fake change: None -> Some(value) -> Some(value)
builder.push_storage_change(vec![2], Some(vec![2])).unwrap();
// actual change: None -> Some(value) -> None
builder.push_storage_change(vec![3], if nonce == 0 { Some(vec![3]) } else { None }).unwrap();
// actual change: None -> Some(value)
builder.push_storage_change(vec![4], if nonce == 0 { None } else { Some(vec![4]) }).unwrap();
// actual change: Some(value1) -> Some(value2)
builder.push_storage_change(vec![5], Some(vec![nonce as u8])).unwrap();
let block = builder.bake().unwrap();
let hash = block.header.hash();
client.import(BlockOrigin::Own, block).unwrap();
Expand All @@ -182,32 +186,31 @@ fn should_query_storage() {
let block2_hash = add_block(1);
let genesis_hash = client.genesis_hash();

let alice_balance_key = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Alice.into()));

let mut expected = vec![
StorageChangeSet {
block: genesis_hash,
changes: vec![
(
StorageKey(alice_balance_key.to_vec()),
Some(StorageData(vec![232, 3, 0, 0, 0, 0, 0, 0]))
),
(StorageKey(vec![1]), None),
(StorageKey(vec![2]), None),
(StorageKey(vec![3]), None),
(StorageKey(vec![4]), None),
(StorageKey(vec![5]), None),
],
},
StorageChangeSet {
block: block1_hash,
changes: vec![
(
StorageKey(alice_balance_key.to_vec()),
Some(StorageData(vec![190, 3, 0, 0, 0, 0, 0, 0]))
),
(StorageKey(vec![2]), Some(StorageData(vec![2]))),
(StorageKey(vec![3]), Some(StorageData(vec![3]))),
(StorageKey(vec![5]), Some(StorageData(vec![0]))),
],
},
];

// Query changes only up to block1
let keys = (1..6).map(|k| StorageKey(vec![k])).collect::<Vec<_>>();
let result = api.query_storage(
vec![StorageKey(alice_balance_key.to_vec())],
keys.clone(),
genesis_hash,
Some(block1_hash).into(),
);
Expand All @@ -216,18 +219,17 @@ fn should_query_storage() {

// Query all changes
let result = api.query_storage(
vec![StorageKey(alice_balance_key.to_vec())],
keys.clone(),
genesis_hash,
None.into(),
);

expected.push(StorageChangeSet {
block: block2_hash,
changes: vec![
(
StorageKey(alice_balance_key.to_vec()),
Some(StorageData(vec![148, 3, 0, 0, 0, 0, 0, 0]))
),
(StorageKey(vec![3]), None),
(StorageKey(vec![4]), Some(StorageData(vec![4]))),
(StorageKey(vec![5]), Some(StorageData(vec![1]))),
],
});
assert_eq!(result.unwrap(), expected);
Expand Down
6 changes: 6 additions & 0 deletions core/test-runtime/client/src/block_builder_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ use generic_test_client::client::block_builder::api::BlockBuilder;
pub trait BlockBuilderExt {
/// Add transfer extrinsic to the block.
fn push_transfer(&mut self, transfer: runtime::Transfer) -> Result<(), client::error::Error>;
/// Add storage change extrinsic to the block.
fn push_storage_change(&mut self, key: Vec<u8>, value: Option<Vec<u8>>) -> Result<(), client::error::Error>;
}

impl<'a, A> BlockBuilderExt for client::block_builder::BlockBuilder<'a, runtime::Block, A> where
Expand All @@ -34,4 +36,8 @@ impl<'a, A> BlockBuilderExt for client::block_builder::BlockBuilder<'a, runtime:
fn push_transfer(&mut self, transfer: runtime::Transfer) -> Result<(), client::error::Error> {
self.push(transfer.into_signed_tx())
}

fn push_storage_change(&mut self, key: Vec<u8>, value: Option<Vec<u8>>) -> Result<(), client::error::Error> {
self.push(runtime::Extrinsic::StorageChange(key, value))
}
}
2 changes: 2 additions & 0 deletions core/test-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ pub enum Extrinsic {
AuthoritiesChange(Vec<AuthorityId>),
Transfer(Transfer, AccountSignature),
IncludeData(Vec<u8>),
StorageChange(Vec<u8>, Option<Vec<u8>>),
}

#[cfg(feature = "std")]
Expand All @@ -129,6 +130,7 @@ impl BlindCheckable for Extrinsic {
}
},
Extrinsic::IncludeData(_) => Err(runtime_primitives::BAD_SIGNATURE),
Extrinsic::StorageChange(key, value) => Ok(Extrinsic::StorageChange(key, value)),
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions core/test-runtime/src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ fn execute_transaction_backend(utx: &Extrinsic) -> ApplyResult {
Extrinsic::Transfer(ref transfer, _) => execute_transfer_backend(transfer),
Extrinsic::AuthoritiesChange(ref new_auth) => execute_new_authorities_backend(new_auth),
Extrinsic::IncludeData(_) => Ok(ApplyOutcome::Success),
Extrinsic::StorageChange(key, value) => execute_storage_change(key, value.as_ref().map(|v| &**v)),
}
}

Expand Down Expand Up @@ -273,6 +274,14 @@ fn execute_new_authorities_backend(new_authorities: &[AuthorityId]) -> ApplyResu
Ok(ApplyOutcome::Success)
}

fn execute_storage_change(key: &[u8], value: Option<&[u8]>) -> ApplyResult {
match value {
Some(value) => storage::unhashed::put_raw(key, value),
None => storage::unhashed::kill(key),
}
Ok(ApplyOutcome::Success)
}

#[cfg(feature = "std")]
fn info_expect_equal_hash(given: &Hash, expected: &Hash) {
use primitives::hexdisplay::HexDisplay;
Expand Down

0 comments on commit 0475ed9

Please sign in to comment.