Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have to clone here? Can't we compare with value_at_block.as_ref()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is also used as storage for the last value (it is updated in the loop). I could use the same last_values map for this, but it is single clone() call vs multiple HashMap::get() + HashMap::insert() calls. Could change that if you feel this is better.

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