mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-27 11:38:01 +00:00
Set StateBackend::Transaction to PrefixedMemoryDB (#14612)
* Yep * Try to get it working everywhere * Make `from_raw_storage` start with an empty db * More fixes! * Make everything compile * Fix `child_storage_root` * Fix after merge * Cleanups * Update primitives/state-machine/src/overlayed_changes/mod.rs Co-authored-by: Davide Galassi <davxy@datawok.net> * Review comments * Fix issues * Silence warning * FMT * Clippy --------- Co-authored-by: Davide Galassi <davxy@datawok.net>
This commit is contained in:
@@ -21,7 +21,7 @@ mod changeset;
|
||||
mod offchain;
|
||||
|
||||
use self::changeset::OverlayedChangeSet;
|
||||
use crate::{backend::Backend, stats::StateMachineStats, DefaultError};
|
||||
use crate::{backend::Backend, stats::StateMachineStats, BackendTransaction, DefaultError};
|
||||
use codec::{Decode, Encode};
|
||||
use hash_db::Hasher;
|
||||
pub use offchain::OffchainOverlayedChanges;
|
||||
@@ -34,6 +34,7 @@ use sp_externalities::{Extension, Extensions};
|
||||
#[cfg(not(feature = "std"))]
|
||||
use sp_std::collections::btree_map::BTreeMap as Map;
|
||||
use sp_std::{collections::btree_set::BTreeSet, vec::Vec};
|
||||
use sp_trie::{empty_child_trie_root, LayoutV1};
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::{hash_map::Entry as MapEntry, HashMap as Map};
|
||||
#[cfg(feature = "std")]
|
||||
@@ -88,8 +89,7 @@ impl Extrinsics {
|
||||
/// The set of changes that are overlaid onto the backend.
|
||||
///
|
||||
/// It allows changes to be modified using nestable transactions.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct OverlayedChanges {
|
||||
pub struct OverlayedChanges<H: Hasher> {
|
||||
/// Top level storage changes.
|
||||
top: OverlayedChangeSet,
|
||||
/// Child storage changes. The map key is the child storage key without the common prefix.
|
||||
@@ -102,6 +102,52 @@ pub struct OverlayedChanges {
|
||||
collect_extrinsics: bool,
|
||||
/// Collect statistic on this execution.
|
||||
stats: StateMachineStats,
|
||||
/// Caches the "storage transaction" that is created while calling `storage_root`.
|
||||
///
|
||||
/// This transaction can be applied to the backend to persist the state changes.
|
||||
storage_transaction_cache: Option<StorageTransactionCache<H>>,
|
||||
}
|
||||
|
||||
impl<H: Hasher> Default for OverlayedChanges<H> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
top: Default::default(),
|
||||
children: Default::default(),
|
||||
offchain: Default::default(),
|
||||
transaction_index_ops: Default::default(),
|
||||
collect_extrinsics: Default::default(),
|
||||
stats: Default::default(),
|
||||
storage_transaction_cache: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher> Clone for OverlayedChanges<H> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
top: self.top.clone(),
|
||||
children: self.children.clone(),
|
||||
offchain: self.offchain.clone(),
|
||||
transaction_index_ops: self.transaction_index_ops.clone(),
|
||||
collect_extrinsics: self.collect_extrinsics,
|
||||
stats: self.stats.clone(),
|
||||
storage_transaction_cache: self.storage_transaction_cache.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher> sp_std::fmt::Debug for OverlayedChanges<H> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("OverlayedChanges")
|
||||
.field("top", &self.top)
|
||||
.field("children", &self.children)
|
||||
.field("offchain", &self.offchain)
|
||||
.field("transaction_index_ops", &self.transaction_index_ops)
|
||||
.field("collect_extrinsics", &self.collect_extrinsics)
|
||||
.field("stats", &self.stats)
|
||||
.field("storage_transaction_cache", &self.storage_transaction_cache)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Transaction index operation.
|
||||
@@ -129,7 +175,7 @@ pub enum IndexOperation {
|
||||
///
|
||||
/// This contains all the changes to the storage and transactions to apply theses changes to the
|
||||
/// backend.
|
||||
pub struct StorageChanges<Transaction, H: Hasher> {
|
||||
pub struct StorageChanges<H: Hasher> {
|
||||
/// All changes to the main storage.
|
||||
///
|
||||
/// A value of `None` means that it was deleted.
|
||||
@@ -142,7 +188,7 @@ pub struct StorageChanges<Transaction, H: Hasher> {
|
||||
/// [`main_storage_changes`](StorageChanges::main_storage_changes) and from
|
||||
/// [`child_storage_changes`](StorageChanges::child_storage_changes).
|
||||
/// [`offchain_storage_changes`](StorageChanges::offchain_storage_changes).
|
||||
pub transaction: Transaction,
|
||||
pub transaction: BackendTransaction<H>,
|
||||
/// The storage root after applying the transaction.
|
||||
pub transaction_storage_root: H::Out,
|
||||
/// Changes to the transaction index,
|
||||
@@ -151,7 +197,7 @@ pub struct StorageChanges<Transaction, H: Hasher> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<Transaction, H: Hasher> StorageChanges<Transaction, H> {
|
||||
impl<H: Hasher> StorageChanges<H> {
|
||||
/// Deconstruct into the inner values
|
||||
pub fn into_inner(
|
||||
self,
|
||||
@@ -159,7 +205,7 @@ impl<Transaction, H: Hasher> StorageChanges<Transaction, H> {
|
||||
StorageCollection,
|
||||
ChildStorageCollection,
|
||||
OffchainChangesCollection,
|
||||
Transaction,
|
||||
BackendTransaction<H>,
|
||||
H::Out,
|
||||
Vec<IndexOperation>,
|
||||
) {
|
||||
@@ -174,30 +220,7 @@ impl<Transaction, H: Hasher> StorageChanges<Transaction, H> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Storage transactions are calculated as part of the `storage_root`.
|
||||
/// These transactions can be reused for importing the block into the
|
||||
/// storage. So, we cache them to not require a recomputation of those transactions.
|
||||
pub struct StorageTransactionCache<Transaction, H: Hasher> {
|
||||
/// Contains the changes for the main and the child storages as one transaction.
|
||||
pub(crate) transaction: Option<Transaction>,
|
||||
/// The storage root after applying the transaction.
|
||||
pub(crate) transaction_storage_root: Option<H::Out>,
|
||||
}
|
||||
|
||||
impl<Transaction, H: Hasher> StorageTransactionCache<Transaction, H> {
|
||||
/// Reset the cached transactions.
|
||||
pub fn reset(&mut self) {
|
||||
*self = Self::default();
|
||||
}
|
||||
}
|
||||
|
||||
impl<Transaction, H: Hasher> Default for StorageTransactionCache<Transaction, H> {
|
||||
fn default() -> Self {
|
||||
Self { transaction: None, transaction_storage_root: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Transaction: Default, H: Hasher> Default for StorageChanges<Transaction, H> {
|
||||
impl<H: Hasher> Default for StorageChanges<H> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
main_storage_changes: Default::default(),
|
||||
@@ -211,7 +234,46 @@ impl<Transaction: Default, H: Hasher> Default for StorageChanges<Transaction, H>
|
||||
}
|
||||
}
|
||||
|
||||
impl OverlayedChanges {
|
||||
/// Storage transactions are calculated as part of the `storage_root`.
|
||||
/// These transactions can be reused for importing the block into the
|
||||
/// storage. So, we cache them to not require a recomputation of those transactions.
|
||||
struct StorageTransactionCache<H: Hasher> {
|
||||
/// Contains the changes for the main and the child storages as one transaction.
|
||||
transaction: BackendTransaction<H>,
|
||||
/// The storage root after applying the transaction.
|
||||
transaction_storage_root: H::Out,
|
||||
}
|
||||
|
||||
impl<H: Hasher> StorageTransactionCache<H> {
|
||||
fn into_inner(self) -> (BackendTransaction<H>, H::Out) {
|
||||
(self.transaction, self.transaction_storage_root)
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher> Clone for StorageTransactionCache<H> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
transaction: self.transaction.clone(),
|
||||
transaction_storage_root: self.transaction_storage_root,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher> sp_std::fmt::Debug for StorageTransactionCache<H> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
let mut debug = f.debug_struct("StorageTransactionCache");
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
debug.field("transaction_storage_root", &self.transaction_storage_root);
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
debug.field("transaction_storage_root", &self.transaction_storage_root.as_ref());
|
||||
|
||||
debug.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Hasher> OverlayedChanges<H> {
|
||||
/// Whether no changes are contained in the top nor in any of the child changes.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.top.is_empty() && self.children.is_empty()
|
||||
@@ -234,6 +296,12 @@ impl OverlayedChanges {
|
||||
})
|
||||
}
|
||||
|
||||
/// Should be called when there are changes that require to reset the
|
||||
/// `storage_transaction_cache`.
|
||||
fn mark_dirty(&mut self) {
|
||||
self.storage_transaction_cache = None;
|
||||
}
|
||||
|
||||
/// Returns mutable reference to current value.
|
||||
/// If there is no value in the overlay, the given callback is used to initiate the value.
|
||||
/// Warning this function registers a change, so the mutable reference MUST be modified.
|
||||
@@ -245,6 +313,8 @@ impl OverlayedChanges {
|
||||
key: &[u8],
|
||||
init: impl Fn() -> StorageValue,
|
||||
) -> &mut StorageValue {
|
||||
self.mark_dirty();
|
||||
|
||||
let value = self.top.modify(key.to_vec(), init, self.extrinsic_index());
|
||||
|
||||
// if the value was deleted initialise it back with an empty vec
|
||||
@@ -266,6 +336,8 @@ impl OverlayedChanges {
|
||||
///
|
||||
/// Can be rolled back or committed when called inside a transaction.
|
||||
pub fn set_storage(&mut self, key: StorageKey, val: Option<StorageValue>) {
|
||||
self.mark_dirty();
|
||||
|
||||
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());
|
||||
@@ -282,6 +354,8 @@ impl OverlayedChanges {
|
||||
key: StorageKey,
|
||||
val: Option<StorageValue>,
|
||||
) {
|
||||
self.mark_dirty();
|
||||
|
||||
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);
|
||||
@@ -300,6 +374,8 @@ impl OverlayedChanges {
|
||||
///
|
||||
/// Can be rolled back or committed when called inside a transaction.
|
||||
pub(crate) fn clear_child_storage(&mut self, child_info: &ChildInfo) -> u32 {
|
||||
self.mark_dirty();
|
||||
|
||||
let extrinsic_index = self.extrinsic_index();
|
||||
let storage_key = child_info.storage_key().to_vec();
|
||||
let top = &self.top;
|
||||
@@ -316,6 +392,8 @@ impl OverlayedChanges {
|
||||
///
|
||||
/// Can be rolled back or committed when called inside a transaction.
|
||||
pub(crate) fn clear_prefix(&mut self, prefix: &[u8]) -> u32 {
|
||||
self.mark_dirty();
|
||||
|
||||
self.top.clear_where(|key, _| key.starts_with(prefix), self.extrinsic_index())
|
||||
}
|
||||
|
||||
@@ -323,6 +401,8 @@ impl OverlayedChanges {
|
||||
///
|
||||
/// Can be rolled back or committed when called inside a transaction
|
||||
pub(crate) fn clear_child_prefix(&mut self, child_info: &ChildInfo, prefix: &[u8]) -> u32 {
|
||||
self.mark_dirty();
|
||||
|
||||
let extrinsic_index = self.extrinsic_index();
|
||||
let storage_key = child_info.storage_key().to_vec();
|
||||
let top = &self.top;
|
||||
@@ -364,6 +444,8 @@ impl OverlayedChanges {
|
||||
/// Any changes made during that transaction are discarded. Returns an error if
|
||||
/// there is no open transaction that can be rolled back.
|
||||
pub fn rollback_transaction(&mut self) -> Result<(), NoOpenTransaction> {
|
||||
self.mark_dirty();
|
||||
|
||||
self.top.rollback_transaction()?;
|
||||
retain_map(&mut self.children, |_, (changeset, _)| {
|
||||
changeset
|
||||
@@ -432,32 +514,6 @@ impl OverlayedChanges {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Consume all changes (top + children) and return them.
|
||||
///
|
||||
/// After calling this function no more changes are contained in this changeset.
|
||||
///
|
||||
/// Panics:
|
||||
/// Panics if `transaction_depth() > 0`
|
||||
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))),
|
||||
)
|
||||
}
|
||||
|
||||
/// Consume all changes (top + children) and return them.
|
||||
///
|
||||
/// After calling this function no more changes are contained in this changeset.
|
||||
@@ -495,42 +551,33 @@ impl OverlayedChanges {
|
||||
&self.transaction_index_ops
|
||||
}
|
||||
|
||||
/// Convert this instance with all changes into a [`StorageChanges`] instance.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn into_storage_changes<B: Backend<H>, H: Hasher>(
|
||||
mut self,
|
||||
backend: &B,
|
||||
mut cache: StorageTransactionCache<B::Transaction, H>,
|
||||
state_version: StateVersion,
|
||||
) -> Result<StorageChanges<B::Transaction, H>, DefaultError>
|
||||
where
|
||||
H::Out: Ord + Encode + 'static,
|
||||
{
|
||||
self.drain_storage_changes(backend, &mut cache, state_version)
|
||||
}
|
||||
|
||||
/// Drain all changes into a [`StorageChanges`] instance. Leave empty overlay in place.
|
||||
pub fn drain_storage_changes<B: Backend<H>, H: Hasher>(
|
||||
pub fn drain_storage_changes<B: Backend<H>>(
|
||||
&mut self,
|
||||
backend: &B,
|
||||
cache: &mut StorageTransactionCache<B::Transaction, H>,
|
||||
state_version: StateVersion,
|
||||
) -> Result<StorageChanges<B::Transaction, H>, DefaultError>
|
||||
) -> Result<StorageChanges<H>, DefaultError>
|
||||
where
|
||||
H::Out: Ord + Encode + 'static,
|
||||
{
|
||||
// If the transaction does not exist, we generate it.
|
||||
if cache.transaction.is_none() {
|
||||
self.storage_root(backend, cache, state_version);
|
||||
}
|
||||
let (transaction, transaction_storage_root) = match self.storage_transaction_cache.take() {
|
||||
Some(cache) => cache.into_inner(),
|
||||
// If the transaction does not exist, we generate it.
|
||||
None => {
|
||||
self.storage_root(backend, state_version);
|
||||
self.storage_transaction_cache
|
||||
.take()
|
||||
.expect("`storage_transaction_cache` was just initialized; qed")
|
||||
.into_inner()
|
||||
},
|
||||
};
|
||||
|
||||
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");
|
||||
use sp_std::mem::take;
|
||||
let main_storage_changes = take(&mut self.top).drain_commited();
|
||||
let child_storage_changes = take(&mut self.children)
|
||||
.into_iter()
|
||||
.map(|(key, (val, info))| (key, (val.drain_commited(), info)));
|
||||
|
||||
let (main_storage_changes, child_storage_changes) = self.drain_committed();
|
||||
let offchain_storage_changes = self.offchain_drain_committed().collect();
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
@@ -562,29 +609,29 @@ impl OverlayedChanges {
|
||||
/// Changes that are made outside of extrinsics, are marked with
|
||||
/// `NO_EXTRINSIC_INDEX` index.
|
||||
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,
|
||||
}
|
||||
self.collect_extrinsics.then(|| {
|
||||
self.storage(EXTRINSIC_INDEX)
|
||||
.and_then(|idx| idx.and_then(|idx| Decode::decode(&mut &*idx).ok()))
|
||||
.unwrap_or(NO_EXTRINSIC_INDEX)
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate the storage root using `backend` and all changes
|
||||
/// as seen by the current transaction.
|
||||
///
|
||||
/// Returns the storage root and caches storage transaction in the given `cache`.
|
||||
pub fn storage_root<H: Hasher, B: Backend<H>>(
|
||||
&self,
|
||||
/// Returns the storage root and whether it was already cached.
|
||||
pub fn storage_root<B: Backend<H>>(
|
||||
&mut self,
|
||||
backend: &B,
|
||||
cache: &mut StorageTransactionCache<B::Transaction, H>,
|
||||
state_version: StateVersion,
|
||||
) -> H::Out
|
||||
) -> (H::Out, bool)
|
||||
where
|
||||
H::Out: Ord + Encode,
|
||||
{
|
||||
if let Some(cache) = &self.storage_transaction_cache {
|
||||
return (cache.transaction_storage_root, true)
|
||||
}
|
||||
|
||||
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[..]))))
|
||||
@@ -592,10 +639,72 @@ impl OverlayedChanges {
|
||||
|
||||
let (root, transaction) = backend.full_storage_root(delta, child_delta, state_version);
|
||||
|
||||
cache.transaction = Some(transaction);
|
||||
cache.transaction_storage_root = Some(root);
|
||||
self.storage_transaction_cache =
|
||||
Some(StorageTransactionCache { transaction, transaction_storage_root: root });
|
||||
|
||||
root
|
||||
(root, false)
|
||||
}
|
||||
|
||||
/// Generate the child storage root using `backend` and all child changes
|
||||
/// as seen by the current transaction.
|
||||
///
|
||||
/// Returns the child storage root and whether it was already cached.
|
||||
pub fn child_storage_root<B: Backend<H>>(
|
||||
&mut self,
|
||||
child_info: &ChildInfo,
|
||||
backend: &B,
|
||||
state_version: StateVersion,
|
||||
) -> Result<(H::Out, bool), B::Error>
|
||||
where
|
||||
H::Out: Ord + Encode + Decode,
|
||||
{
|
||||
let storage_key = child_info.storage_key();
|
||||
let prefixed_storage_key = child_info.prefixed_storage_key();
|
||||
|
||||
if self.storage_transaction_cache.is_some() {
|
||||
let root = self
|
||||
.storage(prefixed_storage_key.as_slice())
|
||||
.map(|v| Ok(v.map(|v| v.to_vec())))
|
||||
.or_else(|| backend.storage(prefixed_storage_key.as_slice()).map(Some).transpose())
|
||||
.transpose()?
|
||||
.flatten()
|
||||
.and_then(|k| Decode::decode(&mut &k[..]).ok())
|
||||
// V1 is equivalent to V0 on empty root.
|
||||
.unwrap_or_else(empty_child_trie_root::<LayoutV1<H>>);
|
||||
|
||||
return Ok((root, true))
|
||||
}
|
||||
|
||||
let root = if let Some((changes, info)) = self.child_changes(storage_key) {
|
||||
let delta = changes.map(|(k, v)| (k.as_ref(), v.value().map(AsRef::as_ref)));
|
||||
Some(backend.child_storage_root(info, delta, state_version))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let root = if let Some((root, is_empty, _)) = root {
|
||||
// We store update in the overlay in order to be able to use
|
||||
// 'self.storage_transaction' cache. This is brittle as it rely on Ext only querying
|
||||
// the trie backend for storage root.
|
||||
// A better design would be to manage 'child_storage_transaction' in a
|
||||
// similar way as 'storage_transaction' but for each child trie.
|
||||
self.set_storage(prefixed_storage_key.into_inner(), (!is_empty).then(|| root.encode()));
|
||||
|
||||
self.mark_dirty();
|
||||
|
||||
root
|
||||
} else {
|
||||
// empty overlay
|
||||
let root = backend
|
||||
.storage(prefixed_storage_key.as_slice())?
|
||||
.and_then(|k| Decode::decode(&mut &k[..]).ok())
|
||||
// V1 is equivalent to V0 on empty root.
|
||||
.unwrap_or_else(empty_child_trie_root::<LayoutV1<H>>);
|
||||
|
||||
root
|
||||
};
|
||||
|
||||
Ok((root, false))
|
||||
}
|
||||
|
||||
/// Returns an iterator over the keys (in lexicographic order) following `key` (excluding `key`)
|
||||
@@ -639,7 +748,7 @@ impl OverlayedChanges {
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl From<sp_core::storage::Storage> for OverlayedChanges {
|
||||
impl<H: Hasher> From<sp_core::storage::Storage> for OverlayedChanges<H> {
|
||||
fn from(storage: sp_core::storage::Storage) -> Self {
|
||||
Self {
|
||||
top: storage.top.into(),
|
||||
@@ -742,7 +851,8 @@ impl<'a> OverlayedExtensions<'a> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{ext::Ext, InMemoryBackend};
|
||||
use crate::{ext::Ext, new_in_mem, InMemoryBackend};
|
||||
use array_bytes::bytes2hex;
|
||||
use sp_core::{traits::Externalities, Blake2Hasher};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
@@ -755,7 +865,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn overlayed_storage_works() {
|
||||
let mut overlayed = OverlayedChanges::default();
|
||||
let mut overlayed = OverlayedChanges::<Blake2Hasher>::default();
|
||||
|
||||
let key = vec![42, 69, 169, 142];
|
||||
|
||||
@@ -790,7 +900,7 @@ mod tests {
|
||||
fn offchain_overlayed_storage_transactions_works() {
|
||||
use sp_core::offchain::STORAGE_PREFIX;
|
||||
fn check_offchain_content(
|
||||
state: &OverlayedChanges,
|
||||
state: &OverlayedChanges<Blake2Hasher>,
|
||||
nb_commit: usize,
|
||||
expected: Vec<(Vec<u8>, Option<Vec<u8>>)>,
|
||||
) {
|
||||
@@ -867,18 +977,61 @@ mod tests {
|
||||
overlay.set_storage(b"dogglesworth".to_vec(), Some(b"cat".to_vec()));
|
||||
overlay.set_storage(b"doug".to_vec(), None);
|
||||
|
||||
let mut cache = StorageTransactionCache::default();
|
||||
let mut ext = Ext::new(&mut overlay, &mut cache, &backend, None);
|
||||
let root = array_bytes::hex2bytes_unchecked(
|
||||
"39245109cef3758c2eed2ccba8d9b370a917850af3824bc8348d505df2c298fa",
|
||||
);
|
||||
{
|
||||
let mut ext = Ext::new(&mut overlay, &backend, None);
|
||||
let root = "39245109cef3758c2eed2ccba8d9b370a917850af3824bc8348d505df2c298fa";
|
||||
|
||||
assert_eq!(&ext.storage_root(state_version)[..], &root);
|
||||
assert_eq!(bytes2hex("", &ext.storage_root(state_version)), root);
|
||||
// Calling a second time should use it from the cache
|
||||
assert_eq!(bytes2hex("", &ext.storage_root(state_version)), root);
|
||||
}
|
||||
|
||||
// Check that the storage root is recalculated
|
||||
overlay.set_storage(b"doug2".to_vec(), Some(b"yes".to_vec()));
|
||||
|
||||
let mut ext = Ext::new(&mut overlay, &backend, None);
|
||||
let root = "5c0a4e35cb967de785e1cb8743e6f24b6ff6d45155317f2078f6eb3fc4ff3e3d";
|
||||
assert_eq!(bytes2hex("", &ext.storage_root(state_version)), root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlayed_child_storage_root_works() {
|
||||
let state_version = StateVersion::default();
|
||||
let child_info = ChildInfo::new_default(b"Child1");
|
||||
let child_info = &child_info;
|
||||
let backend = new_in_mem::<Blake2Hasher>();
|
||||
let mut overlay = OverlayedChanges::<Blake2Hasher>::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 mut ext = Ext::new(&mut overlay, &backend, None);
|
||||
let child_root = "c02965e1df4dc5baf6977390ce67dab1d7a9b27a87c1afe27b50d29cc990e0f5";
|
||||
let root = "eafb765909c3ed5afd92a0c564acf4620d0234b31702e8e8e9b48da72a748838";
|
||||
|
||||
assert_eq!(
|
||||
bytes2hex("", &ext.child_storage_root(child_info, state_version)),
|
||||
child_root,
|
||||
);
|
||||
|
||||
assert_eq!(bytes2hex("", &ext.storage_root(state_version)), root);
|
||||
|
||||
// Calling a second time should use it from the cache
|
||||
assert_eq!(
|
||||
bytes2hex("", &ext.child_storage_root(child_info, state_version)),
|
||||
child_root,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extrinsic_changes_are_collected() {
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
let mut overlay = OverlayedChanges::<Blake2Hasher>::default();
|
||||
overlay.set_collect_extrinsics(true);
|
||||
|
||||
overlay.start_transaction();
|
||||
@@ -919,7 +1072,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn next_storage_key_change_works() {
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
let mut overlay = OverlayedChanges::<Blake2Hasher>::default();
|
||||
overlay.start_transaction();
|
||||
overlay.set_storage(vec![20], Some(vec![20]));
|
||||
overlay.set_storage(vec![30], Some(vec![30]));
|
||||
@@ -960,7 +1113,7 @@ mod tests {
|
||||
let child_info = ChildInfo::new_default(b"Child1");
|
||||
let child_info = &child_info;
|
||||
let child = child_info.storage_key();
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
let mut overlay = OverlayedChanges::<Blake2Hasher>::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]));
|
||||
|
||||
Reference in New Issue
Block a user