Refactor OverlayedChanges (#5989)

* Hide internal structure of OverlayChanges

* Fix tests for OverlayChanges refactor

* Do not clone pending changes

Discarding prospective changes should be equivalent as a state machine
is not to be called with peding changes.

This will be replaced by a storage transaction that is rolled back before
executing the call the second time removing this constraint.

* Doc fixes

* Remove overlong line

* Revert "Do not clone pending changes"

This reverts commit 4799491f4ac16f8517287a0fcf4a3f84ad56f46e.

* Deduplicate chield tries returned from child_infos()

* Remove redundant type annotation

* Avoid changing the storage root in tests

* Preserve extrinsic indices in trie build test

* Swap order of comitted and prospective in fn child_infos

This is only for consistency and does not impact the result.

* Rename set_pending to replace_pending for clearity
This commit is contained in:
Alexander Theißen
2020-05-20 11:39:45 +02:00
committed by GitHub
parent f275c6ab0b
commit 7a5bdb896b
5 changed files with 149 additions and 164 deletions
@@ -30,7 +30,7 @@ use crate::{
use std::iter::FromIterator;
use std::collections::{HashMap, BTreeMap, BTreeSet};
use codec::{Decode, Encode};
use sp_core::storage::{well_known_keys::EXTRINSIC_INDEX, ChildInfo};
use sp_core::storage::{well_known_keys::EXTRINSIC_INDEX, ChildInfo, ChildType};
use sp_core::offchain::storage::OffchainOverlayedChanges;
use std::{mem, ops};
@@ -55,13 +55,13 @@ pub type ChildStorageCollection = Vec<(StorageKey, StorageCollection)>;
#[derive(Debug, Default, Clone)]
pub struct OverlayedChanges {
/// Changes that are not yet committed.
pub(crate) prospective: OverlayedChangeSet,
prospective: OverlayedChangeSet,
/// Committed changes.
pub(crate) committed: OverlayedChangeSet,
committed: OverlayedChangeSet,
/// True if extrinsics stats must be collected.
pub(crate) collect_extrinsics: bool,
collect_extrinsics: bool,
/// Collect statistic on this execution.
pub(crate) stats: StateMachineStats,
stats: StateMachineStats,
}
/// The storage value, used inside OverlayedChanges.
@@ -69,10 +69,10 @@ pub struct OverlayedChanges {
#[cfg_attr(test, derive(PartialEq))]
pub struct OverlayedValue {
/// Current value. None if value has been deleted.
pub value: Option<StorageValue>,
value: Option<StorageValue>,
/// The set of extrinsic indices where the values has been changed.
/// Is filled only if runtime has announced changes trie support.
pub extrinsics: Option<BTreeSet<u32>>,
extrinsics: Option<BTreeSet<u32>>,
}
/// Prospective or committed overlayed change set.
@@ -80,9 +80,9 @@ pub struct OverlayedValue {
#[cfg_attr(test, derive(PartialEq))]
pub struct OverlayedChangeSet {
/// Top level storage changes.
pub top: BTreeMap<StorageKey, OverlayedValue>,
top: BTreeMap<StorageKey, OverlayedValue>,
/// Child storage changes. The map key is the child storage key without the common prefix.
pub children_default: HashMap<StorageKey, (BTreeMap<StorageKey, OverlayedValue>, ChildInfo)>,
children_default: HashMap<StorageKey, (BTreeMap<StorageKey, OverlayedValue>, ChildInfo)>,
}
/// A storage changes structure that can be generated by the data collected in [`OverlayedChanges`].
@@ -187,6 +187,18 @@ impl FromIterator<(StorageKey, OverlayedValue)> for OverlayedChangeSet {
}
}
impl OverlayedValue {
/// The most recent value contained in this overlay.
pub fn value(&self) -> Option<&StorageValue> {
self.value.as_ref()
}
/// List of indices of extrinsics which modified the value using this overlay.
pub fn extrinsics(&self) -> Option<impl Iterator<Item=&u32>> {
self.extrinsics.as_ref().map(|v| v.iter())
}
}
impl OverlayedChangeSet {
/// Whether the change set is empty.
pub fn is_empty(&self) -> bool {
@@ -499,6 +511,44 @@ impl OverlayedChanges {
)
}
/// Get an iterator over all pending and committed child tries in the overlay.
pub fn child_infos(&self) -> impl IntoIterator<Item=&ChildInfo> {
self.committed.children_default.iter()
.chain(self.prospective.children_default.iter())
.map(|(_, v)| &v.1).collect::<BTreeSet<_>>()
}
/// Get an iterator over all pending and committed changes.
///
/// Supplying `None` for `child_info` will only return changes that are in the top
/// trie. Specifying some `child_info` will return only the changes in that
/// child trie.
pub fn changes(&self, child_info: Option<&ChildInfo>)
-> impl Iterator<Item=(&StorageKey, &OverlayedValue)>
{
let (committed, prospective) = if let Some(child_info) = child_info {
match child_info.child_type() {
ChildType::ParentKeyId => (
self.committed.children_default.get(child_info.storage_key()).map(|c| &c.0),
self.prospective.children_default.get(child_info.storage_key()).map(|c| &c.0),
),
}
} else {
(Some(&self.committed.top), Some(&self.prospective.top))
};
committed.into_iter().flatten().chain(prospective.into_iter().flatten())
}
/// Return a clone of the currently pending changes.
pub fn clone_pending(&self) -> OverlayedChangeSet {
self.prospective.clone()
}
/// Replace the currently pending changes.
pub fn replace_pending(&mut self, pending: OverlayedChangeSet) {
self.prospective = pending;
}
/// Convert this instance with all changes into a [`StorageChanges`] instance.
pub fn into_storage_changes<
B: Backend<H>, H: Hasher, N: BlockNumber