mod changeset;
use crate::{
backend::Backend,
stats::StateMachineStats,
};
use sp_std::vec::Vec;
use self::changeset::OverlayedChangeSet;
#[cfg(feature = "std")]
use crate::{
ChangesTrieTransaction,
changes_trie::{
build_changes_trie,
State as ChangesTrieState,
},
};
use crate::changes_trie::BlockNumber;
#[cfg(feature = "std")]
use std::collections::HashMap as Map;
#[cfg(not(feature = "std"))]
use sp_std::collections::btree_map::BTreeMap as Map;
use sp_std::collections::btree_set::BTreeSet;
use codec::{Decode, Encode};
use sp_core::storage::{well_known_keys::EXTRINSIC_INDEX, ChildInfo};
#[cfg(feature = "std")]
use sp_core::offchain::storage::OffchainOverlayedChanges;
use hash_db::Hasher;
use crate::DefaultError;
pub use self::changeset::{OverlayedValue, NoOpenTransaction, AlreadyInRuntime, NotInRuntime};
pub const NO_EXTRINSIC_INDEX: u32 = 0xffffffff;
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, Eq, PartialEq, Clone)]
pub struct Extrinsics(Vec<u32>);
impl Extrinsics {
fn copy_extrinsics_into(&self, dest: &mut BTreeSet<u32>) {
dest.extend(self.0.iter())
}
fn insert(&mut self, ext: u32) {
if Some(&ext) != self.0.last() {
self.0.push(ext);
}
}
fn extend(&mut self, other: Self) {
self.0.extend(other.0.into_iter());
}
}
#[derive(Debug, Default, Clone)]
pub struct OverlayedChanges {
top: OverlayedChangeSet,
children: Map<StorageKey, (OverlayedChangeSet, ChildInfo)>,
collect_extrinsics: bool,
stats: StateMachineStats,
}
pub struct StorageChanges<Transaction, H: Hasher, N: BlockNumber> {
pub main_storage_changes: StorageCollection,
pub child_storage_changes: ChildStorageCollection,
#[cfg(feature = "std")]
pub offchain_storage_changes: OffchainOverlayedChanges,
pub transaction: Transaction,
pub transaction_storage_root: H::Out,
#[cfg(feature = "std")]
pub changes_trie_transaction: Option<ChangesTrieTransaction<H, N>>,
#[cfg(not(feature = "std"))]
pub _ph: sp_std::marker::PhantomData<N>,
}
#[cfg(feature = "std")]
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>,
#[cfg(feature = "std")]
pub(crate) changes_trie_transaction: Option<Option<ChangesTrieTransaction<H, N>>>,
#[cfg(feature = "std")]
pub(crate) changes_trie_transaction_storage_root: Option<Option<H::Out>>,
#[cfg(not(feature = "std"))]
pub(crate) _ph: sp_std::marker::PhantomData<N>,
}
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,
#[cfg(feature = "std")]
changes_trie_transaction: None,
#[cfg(feature = "std")]
changes_trie_transaction_storage_root: None,
#[cfg(not(feature = "std"))]
_ph: Default::default(),
}
}
}
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(),
#[cfg(feature = "std")]
offchain_storage_changes: Default::default(),
transaction: Default::default(),
transaction_storage_root: Default::default(),
#[cfg(feature = "std")]
changes_trie_transaction: None,
#[cfg(not(feature = "std"))]
_ph: Default::default(),
}
}
}
impl OverlayedChanges {
pub fn is_empty(&self) -> bool {
self.top.is_empty() && self.children.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.top.get(key).map(|x| {
let value = x.value();
let size_read = value.map(|x| x.len() as u64).unwrap_or(0);
self.stats.tally_read_modified(size_read);
value.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 value = self.top.modify(key.to_vec(), init, self.extrinsic_index());
value.get_or_insert_with(StorageValue::default)
}
pub fn child_storage(&self, child_info: &ChildInfo, key: &[u8]) -> Option<Option<&[u8]>> {
let map = self.children.get(child_info.storage_key())?;
let value = map.0.get(key)?.value();
let size_read = value.map(|x| x.len() as u64).unwrap_or(0);
self.stats.tally_read_modified(size_read);
Some(value.map(AsRef::as_ref))
}
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);
self.top.set(key, val, self.extrinsic_index());
}
pub(crate) fn set_child_storage(
&mut self,
child_info: &ChildInfo,
key: StorageKey,
val: Option<StorageValue>,
) {
let extrinsic_index = self.extrinsic_index();
let size_write = val.as_ref().map(|x| x.len() as u64).unwrap_or(0);
self.stats.tally_write_overlay(size_write);
let storage_key = child_info.storage_key().to_vec();
let top = &self.top;
let (changeset, info) = self.children.entry(storage_key).or_insert_with(||
(
top.spawn_child(),
child_info.clone()
)
);
let updatable = info.try_update(child_info);
debug_assert!(updatable);
changeset.set(key, val, extrinsic_index);
}
pub(crate) fn clear_child_storage(
&mut self,
child_info: &ChildInfo,
) {
let extrinsic_index = self.extrinsic_index();
let storage_key = child_info.storage_key().to_vec();
let top = &self.top;
let (changeset, info) = self.children.entry(storage_key).or_insert_with(||
(
top.spawn_child(),
child_info.clone()
)
);
let updatable = info.try_update(child_info);
debug_assert!(updatable);
changeset.clear_where(|_, _| true, extrinsic_index);
}
pub(crate) fn clear_prefix(&mut self, prefix: &[u8]) {
self.top.clear_where(|key, _| key.starts_with(prefix), self.extrinsic_index());
}
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().to_vec();
let top = &self.top;
let (changeset, info) = self.children.entry(storage_key).or_insert_with(||
(
top.spawn_child(),
child_info.clone()
)
);
let updatable = info.try_update(child_info);
debug_assert!(updatable);
changeset.clear_where(|key, _| key.starts_with(prefix), extrinsic_index);
}
pub fn transaction_depth(&self) -> usize {
self.top.transaction_depth()
}
pub fn start_transaction(&mut self) {
self.top.start_transaction();
for (_, (changeset, _)) in self.children.iter_mut() {
changeset.start_transaction();
}
}
pub fn rollback_transaction(&mut self) -> Result<(), NoOpenTransaction> {
self.top.rollback_transaction()?;
retain_map(&mut self.children, |_, (changeset, _)| {
changeset.rollback_transaction()
.expect("Top and children changesets are started in lockstep; qed");
!changeset.is_empty()
});
Ok(())
}
pub fn commit_transaction(&mut self) -> Result<(), NoOpenTransaction> {
self.top.commit_transaction()?;
for (_, (changeset, _)) in self.children.iter_mut() {
changeset.commit_transaction()
.expect("Top and children changesets are started in lockstep; qed");
}
Ok(())
}
pub fn enter_runtime(&mut self) -> Result<(), AlreadyInRuntime> {
self.top.enter_runtime()?;
for (_, (changeset, _)) in self.children.iter_mut() {
changeset.enter_runtime()
.expect("Top and children changesets are entering runtime in lockstep; qed")
}
Ok(())
}
pub fn exit_runtime(&mut self) -> Result<(), NotInRuntime> {
self.top.exit_runtime()?;
for (_, (changeset, _)) in self.children.iter_mut() {
changeset.exit_runtime()
.expect("Top and children changesets are entering runtime in lockstep; qed");
}
Ok(())
}
fn drain_committed(&mut self) -> (
impl Iterator<Item=(StorageKey, Option<StorageValue>)>,
impl Iterator<Item=(StorageKey, (impl Iterator<Item=(StorageKey, Option<StorageValue>)>, ChildInfo))>,
) {
use sp_std::mem::take;
(
take(&mut self.top).drain_commited(),
take(&mut self.children).into_iter()
.map(|(key, (val, info))| (
key,
(val.drain_commited(), info)
)
),
)
}
pub fn children(&self)
-> impl Iterator<Item=(impl Iterator<Item=(&StorageKey, &OverlayedValue)>, &ChildInfo)> {
self.children.iter().map(|(_, v)| (v.0.changes(), &v.1))
}
pub fn changes(&self) -> impl Iterator<Item=(&StorageKey, &OverlayedValue)> {
self.top.changes()
}
pub fn child_changes(&self, key: &[u8])
-> Option<(impl Iterator<Item=(&StorageKey, &OverlayedValue)>, &ChildInfo)> {
self.children.get(key).map(|(overlay, info)| (overlay.changes(), info))
}
#[cfg(feature = "std")]
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>, DefaultError>
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,
#[cfg(feature = "std")]
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>, DefaultError>
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");
#[cfg(feature = "std")]
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")?;
}
#[cfg(feature = "std")]
let changes_trie_transaction = cache.changes_trie_transaction
.take()
.expect("Changes trie transaction was generated by `changes_trie_root`; qed");
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(),
#[cfg(feature = "std")]
offchain_storage_changes: Default::default(),
transaction,
transaction_storage_root,
#[cfg(feature = "std")]
changes_trie_transaction,
#[cfg(not(feature = "std"))]
_ph: Default::default(),
})
}
#[cfg(test)]
pub(crate) fn set_extrinsic_index(&mut self, extrinsic_index: u32) {
self.top.set(EXTRINSIC_INDEX.to_vec(), Some(extrinsic_index.encode()), 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 delta = self.changes().map(|(k, v)| (&k[..], v.value().map(|v| &v[..])));
let child_delta = self.children()
.map(|(changes, info)| (info, changes.map(
|(k, v)| (&k[..], v.value().map(|v| &v[..]))
)));
let (root, transaction) = backend.full_storage_root(delta, child_delta);
cache.transaction = Some(transaction);
cache.transaction_storage_root = Some(root);
root
}
#[cfg(feature = "std")]
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 next_storage_key_change(&self, key: &[u8]) -> Option<(&[u8], &OverlayedValue)> {
self.top.next_change(key)
}
pub fn next_child_storage_key_change(
&self,
storage_key: &[u8],
key: &[u8]
) -> Option<(&[u8], &OverlayedValue)> {
self.children
.get(storage_key)
.and_then(|(overlay, _)|
overlay.next_change(key)
)
}
}
#[cfg(feature = "std")]
fn retain_map<K, V, F>(map: &mut Map<K, V>, f: F)
where
K: std::cmp::Eq + std::hash::Hash,
F: FnMut(&K, &mut V) -> bool,
{
map.retain(f);
}
#[cfg(not(feature = "std"))]
fn retain_map<K, V, F>(map: &mut Map<K, V>, mut f: F)
where
K: Ord,
F: FnMut(&K, &mut V) -> bool,
{
let old = sp_std::mem::replace(map, Map::default());
for (k, mut v) in old.into_iter() {
if f(&k, &mut v) {
map.insert(k, v);
}
}
}
#[cfg(test)]
mod tests {
use hex_literal::hex;
use sp_core::{Blake2Hasher, traits::Externalities};
use crate::InMemoryBackend;
use crate::ext::Ext;
use super::*;
use std::collections::BTreeMap;
fn assert_extrinsics(
overlay: &OverlayedChangeSet,
key: impl AsRef<[u8]>,
expected: Vec<u32>,
) {
assert_eq!(
overlay.get(key.as_ref()).unwrap().extrinsics().into_iter().collect::<Vec<_>>(),
expected
)
}
#[test]
fn overlayed_storage_works() {
let mut overlayed = OverlayedChanges::default();
let key = vec![42, 69, 169, 142];
assert!(overlayed.storage(&key).is_none());
overlayed.start_transaction();
overlayed.set_storage(key.clone(), Some(vec![1, 2, 3]));
assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..]));
overlayed.commit_transaction().unwrap();
assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..]));
overlayed.start_transaction();
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.rollback_transaction().unwrap();
assert_eq!(overlayed.storage(&key).unwrap(), Some(&[1, 2, 3][..]));
overlayed.set_storage(key.clone(), None);
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::default();
overlay.set_collect_extrinsics(false);
overlay.start_transaction();
overlay.set_storage(b"dog".to_vec(), Some(b"puppy".to_vec()));
overlay.set_storage(b"dogglesworth".to_vec(), Some(b"catYYY".to_vec()));
overlay.set_storage(b"doug".to_vec(), Some(vec![]));
overlay.commit_transaction().unwrap();
overlay.start_transaction();
overlay.set_storage(b"dogglesworth".to_vec(), Some(b"cat".to_vec()));
overlay.set_storage(b"doug".to_vec(), None);
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.start_transaction();
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_extrinsics(&overlay.top, vec![1], vec![0, 2]);
assert_extrinsics(&overlay.top, vec![3], vec![1]);
assert_extrinsics(&overlay.top, vec![100], vec![NO_EXTRINSIC_INDEX]);
overlay.start_transaction();
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_extrinsics(&overlay.top, vec![1], vec![0, 2, 4]);
assert_extrinsics(&overlay.top, vec![3], vec![1, 3]);
assert_extrinsics(&overlay.top, vec![100], vec![NO_EXTRINSIC_INDEX]);
overlay.rollback_transaction().unwrap();
assert_extrinsics(&overlay.top, vec![1], vec![0, 2]);
assert_extrinsics(&overlay.top, vec![3], vec![1]);
assert_extrinsics(&overlay.top, vec![100], vec![NO_EXTRINSIC_INDEX]);
}
#[test]
fn next_storage_key_change_works() {
let mut overlay = OverlayedChanges::default();
overlay.start_transaction();
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_transaction().unwrap();
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.start_transaction();
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_transaction().unwrap();
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]));
}
}