use crate::{
backend::Backend, ChangesTrieTransaction,
changes_trie::{
NO_EXTRINSIC_INDEX, BlockNumber, build_changes_trie,
State as ChangesTrieState,
},
stats::StateMachineStats,
};
use std::{mem, ops, collections::{HashMap, BTreeMap, BTreeSet}};
use codec::{Decode, Encode};
use sp_core::storage::{well_known_keys::EXTRINSIC_INDEX, ChildInfo, ChildType};
use sp_core::offchain::storage::OffchainOverlayedChanges;
use hash_db::Hasher;
pub type StorageKey = Vec<u8>;
pub type StorageValue = Vec<u8>;
pub type StorageCollection = Vec<(StorageKey, Option<StorageValue>)>;
pub type ChildStorageCollection = Vec<(StorageKey, StorageCollection)>;
#[derive(Debug, Default, Clone)]
pub struct OverlayedChanges {
prospective: OverlayedChangeSet,
committed: OverlayedChangeSet,
collect_extrinsics: bool,
stats: StateMachineStats,
}
#[derive(Debug, Default, Clone)]
#[cfg_attr(test, derive(PartialEq))]
pub struct OverlayedValue {
value: Option<StorageValue>,
extrinsics: Option<BTreeSet<u32>>,
}
#[derive(Debug, Default, Clone)]
#[cfg_attr(test, derive(PartialEq))]
pub struct OverlayedChangeSet {
top: BTreeMap<StorageKey, OverlayedValue>,
children_default: HashMap<StorageKey, (BTreeMap<StorageKey, OverlayedValue>, ChildInfo)>,
}
pub struct StorageChanges<Transaction, H: Hasher, N: BlockNumber> {
pub main_storage_changes: StorageCollection,
pub child_storage_changes: ChildStorageCollection,
pub offchain_storage_changes: OffchainOverlayedChanges,
pub transaction: Transaction,
pub transaction_storage_root: H::Out,
pub changes_trie_transaction: Option<ChangesTrieTransaction<H, N>>,
}
impl<Transaction, H: Hasher, N: BlockNumber> StorageChanges<Transaction, H, N> {
pub fn into_inner(self) -> (
StorageCollection,
ChildStorageCollection,
OffchainOverlayedChanges,
Transaction,
H::Out,
Option<ChangesTrieTransaction<H, N>>,
) {
(
self.main_storage_changes,
self.child_storage_changes,
self.offchain_storage_changes,
self.transaction,
self.transaction_storage_root,
self.changes_trie_transaction,
)
}
}
pub struct StorageTransactionCache<Transaction, H: Hasher, N: BlockNumber> {
pub(crate) transaction: Option<Transaction>,
pub(crate) transaction_storage_root: Option<H::Out>,
pub(crate) changes_trie_transaction: Option<Option<ChangesTrieTransaction<H, N>>>,
pub(crate) changes_trie_transaction_storage_root: Option<Option<H::Out>>,
}
impl<Transaction, H: Hasher, N: BlockNumber> StorageTransactionCache<Transaction, H, N> {
pub fn reset(&mut self) {
*self = Self::default();
}
}
impl<Transaction, H: Hasher, N: BlockNumber> Default for StorageTransactionCache<Transaction, H, N> {
fn default() -> Self {
Self {
transaction: None,
transaction_storage_root: None,
changes_trie_transaction: None,
changes_trie_transaction_storage_root: None,
}
}
}
impl<Transaction: Default, H: Hasher, N: BlockNumber> Default for StorageChanges<Transaction, H, N> {
fn default() -> Self {
Self {
main_storage_changes: Default::default(),
child_storage_changes: Default::default(),
offchain_storage_changes: Default::default(),
transaction: Default::default(),
transaction_storage_root: Default::default(),
changes_trie_transaction: None,
}
}
}
#[cfg(test)]
impl std::iter::FromIterator<(StorageKey, OverlayedValue)> for OverlayedChangeSet {
fn from_iter<T: IntoIterator<Item = (StorageKey, OverlayedValue)>>(iter: T) -> Self {
Self {
top: iter.into_iter().collect(),
children_default: Default::default(),
}
}
}
impl OverlayedValue {
pub fn value(&self) -> Option<&StorageValue> {
self.value.as_ref()
}
pub fn extrinsics(&self) -> Option<impl Iterator<Item=&u32>> {
self.extrinsics.as_ref().map(|v| v.iter())
}
}
impl OverlayedChangeSet {
pub fn is_empty(&self) -> bool {
self.top.is_empty() && self.children_default.is_empty()
}
pub fn clear(&mut self) {
self.top.clear();
self.children_default.clear();
}
}
impl OverlayedChanges {
pub fn is_empty(&self) -> bool {
self.prospective.is_empty() && self.committed.is_empty()
}
pub fn set_collect_extrinsics(&mut self, collect_extrinsics: bool) {
self.collect_extrinsics = collect_extrinsics;
}
pub fn storage(&self, key: &[u8]) -> Option<Option<&[u8]>> {
self.prospective.top.get(key)
.or_else(|| self.committed.top.get(key))
.map(|x| {
let size_read = x.value.as_ref().map(|x| x.len() as u64).unwrap_or(0);
self.stats.tally_read_modified(size_read);
x.value.as_ref().map(AsRef::as_ref)
})
}
#[must_use = "A change was registered, so this value MUST be modified."]
pub fn value_mut_or_insert_with(
&mut self,
key: &[u8],
init: impl Fn() -> StorageValue,
) -> &mut StorageValue {
let extrinsic_index = self.extrinsic_index();
let committed = &self.committed.top;
let mut entry = self.prospective.top.entry(key.to_vec())
.or_insert_with(|| {
if let Some(overlay_state) = committed.get(key).cloned() {
overlay_state
} else {
OverlayedValue { value: Some(init()), ..Default::default() }
}
});
if entry.value.is_none() {
entry.value = Some(Default::default());
}
if let Some(extrinsic) = extrinsic_index {
entry.extrinsics.get_or_insert_with(Default::default)
.insert(extrinsic);
}
entry.value.as_mut().expect("Initialized above; qed")
}
pub fn child_storage(&self, child_info: &ChildInfo, key: &[u8]) -> Option<Option<&[u8]>> {
if let Some(map) = self.prospective.children_default.get(child_info.storage_key()) {
if let Some(val) = map.0.get(key) {
let size_read = val.value.as_ref().map(|x| x.len() as u64).unwrap_or(0);
self.stats.tally_read_modified(size_read);
return Some(val.value.as_ref().map(AsRef::as_ref));
}
}
if let Some(map) = self.committed.children_default.get(child_info.storage_key()) {
if let Some(val) = map.0.get(key) {
let size_read = val.value.as_ref().map(|x| x.len() as u64).unwrap_or(0);
self.stats.tally_read_modified(size_read);
return Some(val.value.as_ref().map(AsRef::as_ref));
}
}
None
}
pub(crate) fn set_storage(&mut self, key: StorageKey, val: Option<StorageValue>) {
let size_write = val.as_ref().map(|x| x.len() as u64).unwrap_or(0);
self.stats.tally_write_overlay(size_write);
let extrinsic_index = self.extrinsic_index();
let entry = self.prospective.top.entry(key).or_default();
entry.value = val;
if let Some(extrinsic) = extrinsic_index {
entry.extrinsics.get_or_insert_with(Default::default)
.insert(extrinsic);
}
}
pub(crate) fn set_child_storage(
&mut self,
child_info: &ChildInfo,
key: StorageKey,
val: Option<StorageValue>,
) {
let size_write = val.as_ref().map(|x| x.len() as u64).unwrap_or(0);
self.stats.tally_write_overlay(size_write);
let extrinsic_index = self.extrinsic_index();
let storage_key = child_info.storage_key().to_vec();
let map_entry = self.prospective.children_default.entry(storage_key)
.or_insert_with(|| (Default::default(), child_info.to_owned()));
let updatable = map_entry.1.try_update(child_info);
debug_assert!(updatable);
let entry = map_entry.0.entry(key).or_default();
entry.value = val;
if let Some(extrinsic) = extrinsic_index {
entry.extrinsics.get_or_insert_with(Default::default)
.insert(extrinsic);
}
}
pub(crate) fn clear_child_storage(
&mut self,
child_info: &ChildInfo,
) {
let extrinsic_index = self.extrinsic_index();
let storage_key = child_info.storage_key();
let map_entry = self.prospective.children_default.entry(storage_key.to_vec())
.or_insert_with(|| (Default::default(), child_info.to_owned()));
let updatable = map_entry.1.try_update(child_info);
debug_assert!(updatable);
map_entry.0.values_mut().for_each(|e| {
if let Some(extrinsic) = extrinsic_index {
e.extrinsics.get_or_insert_with(Default::default)
.insert(extrinsic);
}
e.value = None;
});
if let Some((committed_map, _child_info)) = self.committed.children_default.get(storage_key) {
for (key, value) in committed_map.iter() {
if !map_entry.0.contains_key(key) {
map_entry.0.insert(key.clone(), OverlayedValue {
value: None,
extrinsics: extrinsic_index.map(|i| {
let mut e = value.extrinsics.clone()
.unwrap_or_else(|| BTreeSet::default());
e.insert(i);
e
}),
});
}
}
}
}
pub(crate) fn clear_prefix(&mut self, prefix: &[u8]) {
let extrinsic_index = self.extrinsic_index();
for (key, entry) in self.prospective.top.iter_mut() {
if key.starts_with(prefix) {
entry.value = None;
if let Some(extrinsic) = extrinsic_index {
entry.extrinsics.get_or_insert_with(Default::default)
.insert(extrinsic);
}
}
}
for key in self.committed.top.keys() {
if key.starts_with(prefix) {
let entry = self.prospective.top.entry(key.clone()).or_default();
entry.value = None;
if let Some(extrinsic) = extrinsic_index {
entry.extrinsics.get_or_insert_with(Default::default)
.insert(extrinsic);
}
}
}
}
pub(crate) fn clear_child_prefix(
&mut self,
child_info: &ChildInfo,
prefix: &[u8],
) {
let extrinsic_index = self.extrinsic_index();
let storage_key = child_info.storage_key();
let map_entry = self.prospective.children_default.entry(storage_key.to_vec())
.or_insert_with(|| (Default::default(), child_info.to_owned()));
let updatable = map_entry.1.try_update(child_info);
debug_assert!(updatable);
for (key, entry) in map_entry.0.iter_mut() {
if key.starts_with(prefix) {
entry.value = None;
if let Some(extrinsic) = extrinsic_index {
entry.extrinsics.get_or_insert_with(Default::default)
.insert(extrinsic);
}
}
}
if let Some((child_committed, _child_info)) = self.committed.children_default.get(storage_key) {
for key in child_committed.keys() {
if key.starts_with(prefix) {
let entry = map_entry.0.entry(key.clone()).or_default();
entry.value = None;
if let Some(extrinsic) = extrinsic_index {
entry.extrinsics.get_or_insert_with(Default::default)
.insert(extrinsic);
}
}
}
}
}
pub fn discard_prospective(&mut self) {
self.prospective.clear();
}
pub fn commit_prospective(&mut self) {
if self.committed.is_empty() {
mem::swap(&mut self.prospective, &mut self.committed);
} else {
let top_to_commit = mem::replace(&mut self.prospective.top, BTreeMap::new());
for (key, val) in top_to_commit.into_iter() {
let entry = self.committed.top.entry(key).or_default();
entry.value = val.value;
if let Some(prospective_extrinsics) = val.extrinsics {
entry.extrinsics.get_or_insert_with(Default::default)
.extend(prospective_extrinsics);
}
}
for (storage_key, (map, child_info)) in self.prospective.children_default.drain() {
let child_content = self.committed.children_default.entry(storage_key)
.or_insert_with(|| (Default::default(), child_info));
for (key, val) in map.into_iter() {
let entry = child_content.0.entry(key).or_default();
entry.value = val.value;
if let Some(prospective_extrinsics) = val.extrinsics {
entry.extrinsics.get_or_insert_with(Default::default)
.extend(prospective_extrinsics);
}
}
}
}
}
fn drain_committed(&mut self) -> (
impl Iterator<Item=(StorageKey, Option<StorageValue>)>,
impl Iterator<Item=(StorageKey, (impl Iterator<Item=(StorageKey, Option<StorageValue>)>, ChildInfo))>,
) {
assert!(self.prospective.is_empty());
(
std::mem::take(&mut self.committed.top)
.into_iter()
.map(|(k, v)| (k, v.value)),
std::mem::take(&mut self.committed.children_default)
.into_iter()
.map(|(sk, (v, ci))| (sk, (v.into_iter().map(|(k, v)| (k, v.value)), ci))),
)
}
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<_>>()
}
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())
}
pub fn clone_pending(&self) -> OverlayedChangeSet {
self.prospective.clone()
}
pub fn replace_pending(&mut self, pending: OverlayedChangeSet) {
self.prospective = pending;
}
pub fn into_storage_changes<
B: Backend<H>, H: Hasher, N: BlockNumber
>(
mut self,
backend: &B,
changes_trie_state: Option<&ChangesTrieState<H, N>>,
parent_hash: H::Out,
mut cache: StorageTransactionCache<B::Transaction, H, N>,
) -> Result<StorageChanges<B::Transaction, H, N>, String> where H::Out: Ord + Encode + 'static {
self.drain_storage_changes(backend, changes_trie_state, parent_hash, &mut cache)
}
pub fn drain_storage_changes<B: Backend<H>, H: Hasher, N: BlockNumber>(
&mut self,
backend: &B,
changes_trie_state: Option<&ChangesTrieState<H, N>>,
parent_hash: H::Out,
mut cache: &mut StorageTransactionCache<B::Transaction, H, N>,
) -> Result<StorageChanges<B::Transaction, H, N>, String> where H::Out: Ord + Encode + 'static {
if cache.transaction.is_none() {
self.storage_root(backend, &mut cache);
}
let (transaction, transaction_storage_root) = cache.transaction.take()
.and_then(|t| cache.transaction_storage_root.take().map(|tr| (t, tr)))
.expect("Transaction was be generated as part of `storage_root`; qed");
if cache.changes_trie_transaction.is_none() {
self.changes_trie_root(
backend,
changes_trie_state,
parent_hash,
false,
&mut cache,
).map_err(|_| "Failed to generate changes trie transaction")?;
}
let changes_trie_transaction = cache.changes_trie_transaction
.take()
.expect("Changes trie transaction was generated by `changes_trie_root`; qed");
let offchain_storage_changes = Default::default();
let (main_storage_changes, child_storage_changes) = self.drain_committed();
Ok(StorageChanges {
main_storage_changes: main_storage_changes.collect(),
child_storage_changes: child_storage_changes.map(|(sk, it)| (sk, it.0.collect())).collect(),
offchain_storage_changes,
transaction,
transaction_storage_root,
changes_trie_transaction,
})
}
#[cfg(test)]
pub(crate) fn set_extrinsic_index(&mut self, extrinsic_index: u32) {
self.prospective.top.insert(EXTRINSIC_INDEX.to_vec(), OverlayedValue {
value: Some(extrinsic_index.encode()),
extrinsics: None,
});
}
fn extrinsic_index(&self) -> Option<u32> {
match self.collect_extrinsics {
true => Some(
self.storage(EXTRINSIC_INDEX)
.and_then(|idx| idx.and_then(|idx| Decode::decode(&mut &*idx).ok()))
.unwrap_or(NO_EXTRINSIC_INDEX)),
false => None,
}
}
pub fn storage_root<H: Hasher, N: BlockNumber, B: Backend<H>>(
&self,
backend: &B,
cache: &mut StorageTransactionCache<B::Transaction, H, N>,
) -> H::Out
where H::Out: Ord + Encode,
{
let child_storage_keys = self.prospective.children_default.keys()
.chain(self.committed.children_default.keys());
let child_delta_iter = child_storage_keys.map(|storage_key|
(
self.default_child_info(storage_key)
.expect("child info initialized in either committed or prospective"),
self.committed.children_default.get(storage_key)
.into_iter()
.flat_map(|(map, _)|
map.iter().map(|(k, v)| (&k[..], v.value().map(|v| &v[..])))
)
.chain(
self.prospective.children_default.get(storage_key)
.into_iter()
.flat_map(|(map, _)|
map.iter().map(|(k, v)| (&k[..], v.value().map(|v| &v[..])))
)
),
)
);
let delta = self.committed
.top
.iter()
.map(|(k, v)| (&k[..], v.value().map(|v| &v[..])))
.chain(self.prospective.top.iter().map(|(k, v)| (&k[..], v.value().map(|v| &v[..]))));
let (root, transaction) = backend.full_storage_root(delta, child_delta_iter);
cache.transaction = Some(transaction);
cache.transaction_storage_root = Some(root);
root
}
pub fn changes_trie_root<'a, H: Hasher, N: BlockNumber, B: Backend<H>>(
&self,
backend: &B,
changes_trie_state: Option<&'a ChangesTrieState<'a, H, N>>,
parent_hash: H::Out,
panic_on_storage_error: bool,
cache: &mut StorageTransactionCache<B::Transaction, H, N>,
) -> Result<Option<H::Out>, ()> where H::Out: Ord + Encode + 'static {
build_changes_trie::<_, H, N>(
backend,
changes_trie_state,
self,
parent_hash,
panic_on_storage_error,
).map(|r| {
let root = r.as_ref().map(|r| r.1).clone();
cache.changes_trie_transaction = Some(r.map(|(db, _, cache)| (db, cache)));
cache.changes_trie_transaction_storage_root = Some(root);
root
})
}
pub fn default_child_info(&self, storage_key: &[u8]) -> Option<&ChildInfo> {
if let Some((_, ci)) = self.prospective.children_default.get(storage_key) {
return Some(&ci);
}
if let Some((_, ci)) = self.committed.children_default.get(storage_key) {
return Some(&ci);
}
None
}
pub fn next_storage_key_change(&self, key: &[u8]) -> Option<(&[u8], &OverlayedValue)> {
let range = (ops::Bound::Excluded(key), ops::Bound::Unbounded);
let next_prospective_key = self.prospective.top
.range::<[u8], _>(range)
.next()
.map(|(k, v)| (&k[..], v));
let next_committed_key = self.committed.top
.range::<[u8], _>(range)
.next()
.map(|(k, v)| (&k[..], v));
match (next_committed_key, next_prospective_key) {
(Some(committed_key), Some(prospective_key)) if committed_key.0 < prospective_key.0 =>
Some(committed_key),
(committed_key, None) => committed_key,
(_, prospective_key) => prospective_key,
}
}
pub fn next_child_storage_key_change(
&self,
storage_key: &[u8],
key: &[u8]
) -> Option<(&[u8], &OverlayedValue)> {
let range = (ops::Bound::Excluded(key), ops::Bound::Unbounded);
let next_prospective_key = self.prospective.children_default.get(storage_key)
.and_then(|(map, _)| map.range::<[u8], _>(range).next().map(|(k, v)| (&k[..], v)));
let next_committed_key = self.committed.children_default.get(storage_key)
.and_then(|(map, _)| map.range::<[u8], _>(range).next().map(|(k, v)| (&k[..], v)));
match (next_committed_key, next_prospective_key) {
(Some(committed_key), Some(prospective_key)) if committed_key.0 < prospective_key.0 =>
Some(committed_key),
(committed_key, None) => committed_key,
(_, prospective_key) => prospective_key,
}
}
}
#[cfg(test)]
impl From<Option<StorageValue>> for OverlayedValue {
fn from(value: Option<StorageValue>) -> OverlayedValue {
OverlayedValue { value, ..Default::default() }
}
}
#[cfg(test)]
mod tests {
use hex_literal::hex;
use sp_core::{
Blake2Hasher, traits::Externalities, storage::well_known_keys::EXTRINSIC_INDEX,
};
use crate::InMemoryBackend;
use crate::ext::Ext;
use super::*;
fn strip_extrinsic_index(map: &BTreeMap<StorageKey, OverlayedValue>)
-> BTreeMap<StorageKey, OverlayedValue>
{
let mut clone = map.clone();
clone.remove(&EXTRINSIC_INDEX.to_vec());
clone
}
#[test]
fn overlayed_storage_works() {
let mut overlayed = OverlayedChanges::default();
let key = vec![42, 69, 169, 142];
assert!(overlayed.storage(&key).is_none());
overlayed.set_storage(key.clone(), Some(vec![1, 2, 3]));
assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..]));
overlayed.commit_prospective();
assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..]));
overlayed.set_storage(key.clone(), Some(vec![]));
assert_eq!(overlayed.storage(&key).unwrap(), Some(&[][..]));
overlayed.set_storage(key.clone(), None);
assert!(overlayed.storage(&key).unwrap().is_none());
overlayed.discard_prospective();
assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..]));
overlayed.set_storage(key.clone(), None);
overlayed.commit_prospective();
assert!(overlayed.storage(&key).unwrap().is_none());
}
#[test]
fn overlayed_storage_root_works() {
let initial: BTreeMap<_, _> = vec![
(b"doe".to_vec(), b"reindeer".to_vec()),
(b"dog".to_vec(), b"puppyXXX".to_vec()),
(b"dogglesworth".to_vec(), b"catXXX".to_vec()),
(b"doug".to_vec(), b"notadog".to_vec()),
].into_iter().collect();
let backend = InMemoryBackend::<Blake2Hasher>::from(initial);
let mut overlay = OverlayedChanges {
committed: vec![
(b"dog".to_vec(), Some(b"puppy".to_vec()).into()),
(b"dogglesworth".to_vec(), Some(b"catYYY".to_vec()).into()),
(b"doug".to_vec(), Some(vec![]).into()),
].into_iter().collect(),
prospective: vec![
(b"dogglesworth".to_vec(), Some(b"cat".to_vec()).into()),
(b"doug".to_vec(), None.into()),
].into_iter().collect(),
..Default::default()
};
let mut offchain_overlay = Default::default();
let mut cache = StorageTransactionCache::default();
let mut ext = Ext::new(
&mut overlay,
&mut offchain_overlay,
&mut cache,
&backend,
crate::changes_trie::disabled_state::<_, u64>(),
None,
);
const ROOT: [u8; 32] = hex!("39245109cef3758c2eed2ccba8d9b370a917850af3824bc8348d505df2c298fa");
assert_eq!(&ext.storage_root()[..], &ROOT);
}
#[test]
fn extrinsic_changes_are_collected() {
let mut overlay = OverlayedChanges::default();
overlay.set_collect_extrinsics(true);
overlay.set_storage(vec![100], Some(vec![101]));
overlay.set_extrinsic_index(0);
overlay.set_storage(vec![1], Some(vec![2]));
overlay.set_extrinsic_index(1);
overlay.set_storage(vec![3], Some(vec![4]));
overlay.set_extrinsic_index(2);
overlay.set_storage(vec![1], Some(vec![6]));
assert_eq!(strip_extrinsic_index(&overlay.prospective.top),
vec![
(vec![1], OverlayedValue { value: Some(vec![6]),
extrinsics: Some(vec![0, 2].into_iter().collect()) }),
(vec![3], OverlayedValue { value: Some(vec![4]),
extrinsics: Some(vec![1].into_iter().collect()) }),
(vec![100], OverlayedValue { value: Some(vec![101]),
extrinsics: Some(vec![NO_EXTRINSIC_INDEX].into_iter().collect()) }),
].into_iter().collect());
overlay.commit_prospective();
overlay.set_extrinsic_index(3);
overlay.set_storage(vec![3], Some(vec![7]));
overlay.set_extrinsic_index(4);
overlay.set_storage(vec![1], Some(vec![8]));
assert_eq!(strip_extrinsic_index(&overlay.committed.top),
vec![
(vec![1], OverlayedValue { value: Some(vec![6]),
extrinsics: Some(vec![0, 2].into_iter().collect()) }),
(vec![3], OverlayedValue { value: Some(vec![4]),
extrinsics: Some(vec![1].into_iter().collect()) }),
(vec![100], OverlayedValue { value: Some(vec![101]),
extrinsics: Some(vec![NO_EXTRINSIC_INDEX].into_iter().collect()) }),
].into_iter().collect());
assert_eq!(strip_extrinsic_index(&overlay.prospective.top),
vec![
(vec![1], OverlayedValue { value: Some(vec![8]),
extrinsics: Some(vec![4].into_iter().collect()) }),
(vec![3], OverlayedValue { value: Some(vec![7]),
extrinsics: Some(vec![3].into_iter().collect()) }),
].into_iter().collect());
overlay.commit_prospective();
assert_eq!(strip_extrinsic_index(&overlay.committed.top),
vec![
(vec![1], OverlayedValue { value: Some(vec![8]),
extrinsics: Some(vec![0, 2, 4].into_iter().collect()) }),
(vec![3], OverlayedValue { value: Some(vec![7]),
extrinsics: Some(vec![1, 3].into_iter().collect()) }),
(vec![100], OverlayedValue { value: Some(vec![101]),
extrinsics: Some(vec![NO_EXTRINSIC_INDEX].into_iter().collect()) }),
].into_iter().collect());
assert_eq!(overlay.prospective,
Default::default());
}
#[test]
fn next_storage_key_change_works() {
let mut overlay = OverlayedChanges::default();
overlay.set_storage(vec![20], Some(vec![20]));
overlay.set_storage(vec![30], Some(vec![30]));
overlay.set_storage(vec![40], Some(vec![40]));
overlay.commit_prospective();
overlay.set_storage(vec![10], Some(vec![10]));
overlay.set_storage(vec![30], None);
let next_to_5 = overlay.next_storage_key_change(&[5]).unwrap();
assert_eq!(next_to_5.0.to_vec(), vec![10]);
assert_eq!(next_to_5.1.value, Some(vec![10]));
let next_to_10 = overlay.next_storage_key_change(&[10]).unwrap();
assert_eq!(next_to_10.0.to_vec(), vec![20]);
assert_eq!(next_to_10.1.value, Some(vec![20]));
let next_to_20 = overlay.next_storage_key_change(&[20]).unwrap();
assert_eq!(next_to_20.0.to_vec(), vec![30]);
assert_eq!(next_to_20.1.value, None);
let next_to_30 = overlay.next_storage_key_change(&[30]).unwrap();
assert_eq!(next_to_30.0.to_vec(), vec![40]);
assert_eq!(next_to_30.1.value, Some(vec![40]));
overlay.set_storage(vec![50], Some(vec![50]));
let next_to_40 = overlay.next_storage_key_change(&[40]).unwrap();
assert_eq!(next_to_40.0.to_vec(), vec![50]);
assert_eq!(next_to_40.1.value, Some(vec![50]));
}
#[test]
fn next_child_storage_key_change_works() {
let child_info = ChildInfo::new_default(b"Child1");
let child_info = &child_info;
let child = child_info.storage_key();
let mut overlay = OverlayedChanges::default();
overlay.set_child_storage(child_info, vec![20], Some(vec![20]));
overlay.set_child_storage(child_info, vec![30], Some(vec![30]));
overlay.set_child_storage(child_info, vec![40], Some(vec![40]));
overlay.commit_prospective();
overlay.set_child_storage(child_info, vec![10], Some(vec![10]));
overlay.set_child_storage(child_info, vec![30], None);
let next_to_5 = overlay.next_child_storage_key_change(child, &[5]).unwrap();
assert_eq!(next_to_5.0.to_vec(), vec![10]);
assert_eq!(next_to_5.1.value, Some(vec![10]));
let next_to_10 = overlay.next_child_storage_key_change(child, &[10]).unwrap();
assert_eq!(next_to_10.0.to_vec(), vec![20]);
assert_eq!(next_to_10.1.value, Some(vec![20]));
let next_to_20 = overlay.next_child_storage_key_change(child, &[20]).unwrap();
assert_eq!(next_to_20.0.to_vec(), vec![30]);
assert_eq!(next_to_20.1.value, None);
let next_to_30 = overlay.next_child_storage_key_change(child, &[30]).unwrap();
assert_eq!(next_to_30.0.to_vec(), vec![40]);
assert_eq!(next_to_30.1.value, Some(vec![40]));
overlay.set_child_storage(child_info, vec![50], Some(vec![50]));
let next_to_40 = overlay.next_child_storage_key_change(child, &[40]).unwrap();
assert_eq!(next_to_40.0.to_vec(), vec![50]);
assert_eq!(next_to_40.1.value, Some(vec![50]));
}
}