Refactored in-mem backed to use the actual trie (#5730)

This commit is contained in:
Arkadiy Paronyan
2020-04-23 19:00:36 +02:00
committed by GitHub
parent ca3f54b4af
commit 59f13e6e0d
10 changed files with 180 additions and 305 deletions
+9 -4
View File
@@ -464,7 +464,7 @@ pub struct BlockImportOperation<Block: BlockT> {
pending_block: Option<PendingBlock<Block>>,
pending_cache: HashMap<CacheKeyId, Vec<u8>>,
old_state: InMemoryBackend<HashFor<Block>>,
new_state: Option<InMemoryBackend<HashFor<Block>>>,
new_state: Option<<InMemoryBackend<HashFor<Block>> as StateBackend<HashFor<Block>>>::Transaction>,
aux: Vec<(Vec<u8>, Option<Vec<u8>>)>,
finalized_blocks: Vec<(BlockId<Block>, Option<Justification>)>,
set_head: Option<BlockId<Block>>,
@@ -502,7 +502,7 @@ impl<Block: BlockT> backend::BlockImportOperation<Block> for BlockImportOperatio
&mut self,
update: <InMemoryBackend<HashFor<Block>> as StateBackend<HashFor<Block>>>::Transaction,
) -> sp_blockchain::Result<()> {
self.new_state = Some(self.old_state.update(update));
self.new_state = Some(update);
Ok(())
}
@@ -525,7 +525,7 @@ impl<Block: BlockT> backend::BlockImportOperation<Block> for BlockImportOperatio
child_delta
);
self.new_state = Some(InMemoryBackend::from(transaction));
self.new_state = Some(transaction);
Ok(root)
}
@@ -638,7 +638,12 @@ impl<Block: BlockT> backend::Backend<Block> for Backend<Block> where Block::Hash
let hash = header.hash();
self.states.write().insert(hash, operation.new_state.unwrap_or_else(|| old_state.clone()));
let new_state = match operation.new_state {
Some(state) => old_state.update_backend(*header.state_root(), state),
None => old_state.clone(),
};
self.states.write().insert(hash, new_state);
self.blockchain.insert(hash, header, justification, body, pending_block.state)?;
}
+1 -1
View File
@@ -615,7 +615,7 @@ mod tests {
header: Header {
parent_hash: [69u8; 32].into(),
number: 1,
state_root: hex!("489ae9b57a19bb4733a264dc64bbcae9b140a904657a681ed3bb5fbbe8cf412b").into(),
state_root: hex!("409fb5a14aeb8b8c59258b503396a56dee45a0ee28a78de3e622db957425e275").into(),
extrinsics_root: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into(),
digest: Digest { logs: vec![], },
},
@@ -16,12 +16,9 @@
//! State machine backends. These manage the code and storage of contracts.
use log::warn;
use hash_db::Hasher;
use codec::{Decode, Encode};
use sp_core::{traits::RuntimeCode, storage::{ChildInfo, well_known_keys}};
use sp_trie::{TrieMut, MemoryDB, trie_types::TrieDBMut};
use crate::{
trie_backend::TrieBackend,
@@ -334,17 +331,20 @@ impl<H: Hasher, KF: sp_trie::KeyFunction<H>> Consolidate for sp_trie::GenericMem
}
/// Insert input pairs into memory db.
pub(crate) fn insert_into_memory_db<H, I>(mdb: &mut MemoryDB<H>, input: I) -> Option<H::Out>
#[cfg(test)]
pub(crate) fn insert_into_memory_db<H, I>(mdb: &mut sp_trie::MemoryDB<H>, input: I) -> Option<H::Out>
where
H: Hasher,
I: IntoIterator<Item=(StorageKey, StorageValue)>,
{
use sp_trie::{TrieMut, trie_types::TrieDBMut};
let mut root = <H as Hasher>::Out::default();
{
let mut trie = TrieDBMut::<H>::new(mdb, &mut root);
for (key, value) in input {
if let Err(e) = trie.insert(&key, &value) {
warn!(target: "trie", "Failed to write to trie: {}", e);
log::warn!(target: "trie", "Failed to write to trie: {}", e);
return None;
}
}
@@ -19,7 +19,7 @@
use std::{
collections::BTreeMap, any::{TypeId, Any}, iter::FromIterator, ops::Bound
};
use crate::{Backend, InMemoryBackend, StorageKey, StorageValue};
use crate::{Backend, StorageKey, StorageValue};
use hash_db::Hasher;
use sp_trie::{TrieConfiguration, empty_child_trie_root};
use sp_trie::trie_types::Layout;
@@ -284,8 +284,7 @@ impl Externalities for BasicExternalities {
) -> Vec<u8> {
if let Some(child) = self.inner.children_default.get(child_info.storage_key()) {
let delta = child.data.clone().into_iter().map(|(k, v)| (k, Some(v)));
InMemoryBackend::<Blake2Hasher>::default()
crate::in_memory_backend::new_in_mem::<Blake2Hasher>()
.child_storage_root(&child.child_info, delta).0
} else {
empty_child_trie_root::<Layout<Blake2Hasher>>()
@@ -19,74 +19,60 @@
use crate::{
StorageKey, StorageValue, StorageCollection,
trie_backend::TrieBackend,
backend::{Backend, insert_into_memory_db},
stats::UsageInfo,
};
use std::{error, fmt, collections::{BTreeMap, HashMap}, marker::PhantomData, ops};
use std::{collections::{BTreeMap, HashMap}};
use hash_db::Hasher;
use sp_trie::{
MemoryDB, child_trie_root, empty_child_trie_root, TrieConfiguration, trie_types::Layout,
MemoryDB, TrieMut,
trie_types::TrieDBMut,
};
use codec::Codec;
use sp_core::storage::{ChildInfo, ChildType, Storage};
use sp_core::storage::{ChildInfo, Storage};
/// Error impossible.
// FIXME: use `!` type when stabilized. https://github.com/rust-lang/rust/issues/35121
#[derive(Debug)]
pub enum Void {}
impl fmt::Display for Void {
fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
match *self {}
}
}
impl error::Error for Void {
fn description(&self) -> &str { "unreachable error" }
}
/// In-memory backend. Fully recomputes tries each time `as_trie_backend` is called but useful for
/// tests and proof checking.
pub struct InMemory<H: Hasher> {
inner: HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>>,
// This field is only needed for returning reference in `as_trie_backend`.
trie: Option<TrieBackend<MemoryDB<H>, H>>,
_hasher: PhantomData<H>,
}
impl<H: Hasher> std::fmt::Debug for InMemory<H> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "InMemory ({} values)", self.inner.len())
}
}
impl<H: Hasher> Default for InMemory<H> {
fn default() -> Self {
InMemory {
inner: Default::default(),
trie: None,
_hasher: PhantomData,
/// Insert input pairs into memory db.
fn insert_into_memory_db<H, I>(mut root: H::Out, mdb: &mut MemoryDB<H>, input: I) -> H::Out
where
H: Hasher,
I: IntoIterator<Item=(StorageKey, Option<StorageValue>)>,
{
{
let mut trie = if root == Default::default() {
TrieDBMut::<H>::new(mdb, &mut root)
} else {
TrieDBMut::<H>::from_existing(mdb, &mut root).unwrap()
};
for (key, value) in input {
if let Err(e) = match value {
Some(value) => {
trie.insert(&key, &value)
},
None => {
trie.remove(&key)
},
} {
panic!("Failed to write to trie: {}", e);
}
}
trie.commit();
}
root
}
impl<H: Hasher> Clone for InMemory<H> {
fn clone(&self) -> Self {
InMemory {
inner: self.inner.clone(),
trie: None,
_hasher: PhantomData,
}
}
/// Create a new empty instance of in-memory backend.
pub fn new_in_mem<H: Hasher>() -> TrieBackend<MemoryDB<H>, H>
where
H::Out: Codec + Ord,
{
let db = MemoryDB::default();
let mut backend = TrieBackend::new(db, Default::default());
backend.insert(std::iter::empty());
backend
}
impl<H: Hasher> PartialEq for InMemory<H> {
fn eq(&self, other: &Self) -> bool {
self.inner.eq(&other.inner)
}
}
impl<H: Hasher> InMemory<H> {
impl<H: Hasher> TrieBackend<MemoryDB<H>, H>
where
H::Out: Codec + Ord,
{
/// Copy the state, with applied updates
pub fn update<
T: IntoIterator<Item = (Option<ChildInfo>, StorageCollection)>
@@ -94,59 +80,117 @@ impl<H: Hasher> InMemory<H> {
&self,
changes: T,
) -> Self {
let mut inner = self.inner.clone();
for (child_info, key_values) in changes.into_iter() {
let entry = inner.entry(child_info).or_default();
for (key, val) in key_values {
match val {
Some(v) => { entry.insert(key, v); },
None => { entry.remove(&key); },
}
let mut clone = self.clone();
clone.insert(changes);
clone
}
/// Insert values into backend trie.
pub fn insert<
T: IntoIterator<Item = (Option<ChildInfo>, StorageCollection)>
>(
&mut self,
changes: T,
) {
let mut new_child_roots = Vec::new();
let mut root_map = None;
let root = self.root().clone();
for (child_info, map) in changes {
if let Some(child_info) = child_info.as_ref() {
let prefix_storage_key = child_info.prefixed_storage_key();
let ch = insert_into_memory_db::<H, _>(root, self.backend_storage_mut(), map.clone().into_iter());
new_child_roots.push((prefix_storage_key.into_inner(), Some(ch.as_ref().into())));
} else {
root_map = Some(map);
}
}
inner.into()
let root = match root_map {
Some(map) => insert_into_memory_db::<H, _>(
root,
self.backend_storage_mut(),
map.clone().into_iter().chain(new_child_roots.into_iter()),
),
None => insert_into_memory_db::<H, _>(
root,
self.backend_storage_mut(),
new_child_roots.into_iter(),
),
};
self.essence.set_root(root);
}
/// Merge trie nodes into this backend.
pub fn update_backend(&self, root: H::Out, changes: MemoryDB<H>) -> Self {
let mut clone = self.backend_storage().clone();
clone.consolidate(changes);
Self::new(clone, root)
}
/// Compare with another in-memory backend.
pub fn eq(&self, other: &Self) -> bool {
self.root() == other.root()
}
}
impl<H: Hasher> Clone for TrieBackend<MemoryDB<H>, H>
where
H::Out: Codec + Ord,
{
fn clone(&self) -> Self {
TrieBackend::new(self.backend_storage().clone(), self.root().clone())
}
}
impl<H: Hasher> Default for TrieBackend<MemoryDB<H>, H>
where
H::Out: Codec + Ord,
{
fn default() -> Self {
new_in_mem()
}
}
impl<H: Hasher> From<HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>>>
for InMemory<H>
for TrieBackend<MemoryDB<H>, H>
where
H::Out: Codec + Ord,
{
fn from(inner: HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>>) -> Self {
InMemory {
inner,
trie: None,
_hasher: PhantomData,
}
let mut backend = new_in_mem();
backend.insert(inner.into_iter().map(|(k, m)| (k, m.into_iter().map(|(k, v)| (k, Some(v))).collect())));
backend
}
}
impl<H: Hasher> From<Storage> for InMemory<H> {
impl<H: Hasher> From<Storage> for TrieBackend<MemoryDB<H>, H>
where
H::Out: Codec + Ord,
{
fn from(inners: Storage) -> Self {
let mut inner: HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>>
= inners.children_default.into_iter().map(|(_k, c)| (Some(c.child_info), c.data)).collect();
inner.insert(None, inners.top);
InMemory {
inner,
trie: None,
_hasher: PhantomData,
}
inner.into()
}
}
impl<H: Hasher> From<BTreeMap<StorageKey, StorageValue>> for InMemory<H> {
impl<H: Hasher> From<BTreeMap<StorageKey, StorageValue>> for TrieBackend<MemoryDB<H>, H>
where
H::Out: Codec + Ord,
{
fn from(inner: BTreeMap<StorageKey, StorageValue>) -> Self {
let mut expanded = HashMap::new();
expanded.insert(None, inner);
InMemory {
inner: expanded,
trie: None,
_hasher: PhantomData,
}
expanded.into()
}
}
impl<H: Hasher> From<Vec<(Option<ChildInfo>, StorageCollection)>>
for InMemory<H> {
for TrieBackend<MemoryDB<H>, H>
where
H::Out: Codec + Ord,
{
fn from(
inner: Vec<(Option<ChildInfo>, StorageCollection)>,
) -> Self {
@@ -164,213 +208,16 @@ impl<H: Hasher> From<Vec<(Option<ChildInfo>, StorageCollection)>>
}
}
impl<H: Hasher> InMemory<H> {
/// Child storage infos iterator.
pub fn child_storage_infos(&self) -> impl Iterator<Item = &ChildInfo> {
self.inner.iter().filter_map(|item| item.0.as_ref())
}
}
impl<H: Hasher> Backend<H> for InMemory<H> where H::Out: Codec {
type Error = Void;
type Transaction = Vec<(
Option<ChildInfo>,
StorageCollection,
)>;
type TrieBackendStorage = MemoryDB<H>;
fn storage(&self, key: &[u8]) -> Result<Option<StorageValue>, Self::Error> {
Ok(self.inner.get(&None).and_then(|map| map.get(key).map(Clone::clone)))
}
fn child_storage(
&self,
child_info: &ChildInfo,
key: &[u8],
) -> Result<Option<StorageValue>, Self::Error> {
Ok(self.inner.get(&Some(child_info.to_owned()))
.and_then(|map| map.get(key).map(Clone::clone)))
}
fn exists_storage(&self, key: &[u8]) -> Result<bool, Self::Error> {
Ok(self.inner.get(&None).map(|map| map.get(key).is_some()).unwrap_or(false))
}
fn for_keys_with_prefix<F: FnMut(&[u8])>(&self, prefix: &[u8], f: F) {
self.inner.get(&None)
.map(|map| map.keys().filter(|key| key.starts_with(prefix)).map(|k| &**k).for_each(f));
}
fn for_key_values_with_prefix<F: FnMut(&[u8], &[u8])>(&self, prefix: &[u8], mut f: F) {
self.inner.get(&None).map(|map| map.iter().filter(|(key, _val)| key.starts_with(prefix))
.for_each(|(k, v)| f(k, v)));
}
fn for_keys_in_child_storage<F: FnMut(&[u8])>(
&self,
child_info: &ChildInfo,
mut f: F,
) {
self.inner.get(&Some(child_info.to_owned()))
.map(|map| map.keys().for_each(|k| f(&k)));
}
fn for_child_keys_with_prefix<F: FnMut(&[u8])>(
&self,
child_info: &ChildInfo,
prefix: &[u8],
f: F,
) {
self.inner.get(&Some(child_info.to_owned()))
.map(|map| map.keys().filter(|key| key.starts_with(prefix)).map(|k| &**k).for_each(f));
}
fn storage_root<I>(&self, delta: I) -> (H::Out, Self::Transaction)
where
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>,
<H as Hasher>::Out: Ord,
{
let existing_pairs = self.inner.get(&None)
.into_iter()
.flat_map(|map| map.iter().map(|(k, v)| (k.clone(), Some(v.clone()))));
let transaction: Vec<_> = delta.into_iter().collect();
let root = Layout::<H>::trie_root(existing_pairs.chain(transaction.iter().cloned())
.collect::<HashMap<_, _>>()
.into_iter()
.filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val)))
);
let full_transaction = transaction.into_iter().collect();
(root, vec![(None, full_transaction)])
}
fn child_storage_root<I>(
&self,
child_info: &ChildInfo,
delta: I,
) -> (H::Out, bool, Self::Transaction)
where
I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>,
H::Out: Ord
{
let child_type = child_info.child_type();
let child_info = Some(child_info.to_owned());
let existing_pairs = self.inner.get(&child_info)
.into_iter()
.flat_map(|map| map.iter().map(|(k, v)| (k.clone(), Some(v.clone()))));
let transaction: Vec<_> = delta.into_iter().collect();
let root = child_trie_root::<Layout<H>, _, _, _>(
existing_pairs.chain(transaction.iter().cloned())
.collect::<HashMap<_, _>>()
.into_iter()
.filter_map(|(k, maybe_val)| maybe_val.map(|val| (k, val)))
);
let full_transaction = transaction.into_iter().collect();
let is_default = match child_type {
ChildType::ParentKeyId => root == empty_child_trie_root::<Layout<H>>(),
};
(root, is_default, vec![(child_info, full_transaction)])
}
fn next_storage_key(&self, key: &[u8]) -> Result<Option<StorageKey>, Self::Error> {
let range = (ops::Bound::Excluded(key), ops::Bound::Unbounded);
let next_key = self.inner.get(&None)
.and_then(|map| map.range::<[u8], _>(range).next().map(|(k, _)| k).cloned());
Ok(next_key)
}
fn next_child_storage_key(
&self,
child_info: &ChildInfo,
key: &[u8],
) -> Result<Option<StorageKey>, Self::Error> {
let range = (ops::Bound::Excluded(key), ops::Bound::Unbounded);
let next_key = self.inner.get(&Some(child_info.to_owned()))
.and_then(|map| map.range::<[u8], _>(range).next().map(|(k, _)| k).cloned());
Ok(next_key)
}
fn pairs(&self) -> Vec<(StorageKey, StorageValue)> {
self.inner.get(&None)
.into_iter()
.flat_map(|map| map.iter().map(|(k, v)| (k.clone(), v.clone())))
.collect()
}
fn keys(&self, prefix: &[u8]) -> Vec<StorageKey> {
self.inner.get(&None)
.into_iter()
.flat_map(|map| map.keys().filter(|k| k.starts_with(prefix)).cloned())
.collect()
}
fn child_keys(
&self,
child_info: &ChildInfo,
prefix: &[u8],
) -> Vec<StorageKey> {
self.inner.get(&Some(child_info.to_owned()))
.into_iter()
.flat_map(|map| map.keys().filter(|k| k.starts_with(prefix)).cloned())
.collect()
}
fn as_trie_backend(&mut self)-> Option<&TrieBackend<Self::TrieBackendStorage, H>> {
let mut mdb = MemoryDB::default();
let mut new_child_roots = Vec::new();
let mut root_map = None;
for (child_info, map) in &self.inner {
if let Some(child_info) = child_info.as_ref() {
let prefix_storage_key = child_info.prefixed_storage_key();
let ch = insert_into_memory_db::<H, _>(&mut mdb, map.clone().into_iter())?;
new_child_roots.push((prefix_storage_key.into_inner(), ch.as_ref().into()));
} else {
root_map = Some(map);
}
}
let root = match root_map {
Some(map) => insert_into_memory_db::<H, _>(
&mut mdb,
map.clone().into_iter().chain(new_child_roots.into_iter()),
)?,
None => insert_into_memory_db::<H, _>(
&mut mdb,
new_child_roots.into_iter(),
)?,
};
self.trie = Some(TrieBackend::new(mdb, root));
self.trie.as_ref()
}
fn register_overlay_stats(&mut self, _stats: &crate::stats::StateMachineStats) { }
fn usage_info(&self) -> UsageInfo {
UsageInfo::empty()
}
fn wipe(&self) -> Result<(), Self::Error> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use sp_runtime::traits::BlakeTwo256;
use crate::backend::Backend;
/// Assert in memory backend with only child trie keys works as trie backend.
#[test]
fn in_memory_with_child_trie_only() {
let storage = InMemory::<BlakeTwo256>::default();
let storage = new_in_mem::<BlakeTwo256>();
let child_info = ChildInfo::new_default(b"1");
let child_info = &child_info;
let mut storage = storage.update(
@@ -72,7 +72,7 @@ pub use proving_backend::{
pub use trie_backend_essence::{TrieBackendStorage, Storage};
pub use trie_backend::TrieBackend;
pub use error::{Error, ExecutionError};
pub use in_memory_backend::InMemory as InMemoryBackend;
pub use in_memory_backend::new_in_mem;
pub use stats::{UsageInfo, UsageUnit, StateMachineStats};
pub use sp_core::traits::CloneableSpawn;
@@ -87,6 +87,9 @@ pub type ChangesTrieTransaction<H, N> = (
ChangesTrieCacheAction<<H as Hasher>::Out, N>,
);
/// Trie backend with in-memory storage.
pub type InMemoryBackend<H> = TrieBackend<MemoryDB<H>, H>;
/// Strategy for executing a call into the runtime.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum ExecutionStrategy {
@@ -1001,7 +1004,7 @@ mod tests {
fn set_child_storage_works() {
let child_info = ChildInfo::new_default(b"sub1");
let child_info = &child_info;
let mut state = InMemoryBackend::<BlakeTwo256>::default();
let mut state = new_in_mem::<BlakeTwo256>();
let backend = state.as_trie_backend().unwrap();
let mut overlay = OverlayedChanges::default();
let mut cache = StorageTransactionCache::default();
@@ -391,9 +391,10 @@ mod tests {
];
let in_memory = InMemoryBackend::<BlakeTwo256>::default();
let mut in_memory = in_memory.update(contents);
let child_storage_keys = vec![child_info_1.to_owned(), child_info_2.to_owned()];
let in_memory_root = in_memory.full_storage_root::<_, Vec<_>, _>(
::std::iter::empty(),
in_memory.child_storage_infos().map(|k|(k.to_owned(), Vec::new()))
child_storage_keys.into_iter().map(|k|(k.to_owned(), Vec::new()))
).0;
(0..64).for_each(|i| assert_eq!(
in_memory.storage(&[i]).unwrap().unwrap(),
@@ -41,7 +41,7 @@ use sp_externalities::{Extensions, Extension};
/// Simple HashMap-based Externalities impl.
pub struct TestExternalities<H: Hasher, N: ChangesTrieBlockNumber = u64>
where
H::Out: codec::Codec,
H::Out: codec::Codec + Ord,
{
overlay: OverlayedChanges,
storage_transaction_cache: StorageTransactionCache<
@@ -113,7 +113,7 @@ impl<H: Hasher, N: ChangesTrieBlockNumber> TestExternalities<H, N>
/// Insert key/value into backend
pub fn insert(&mut self, k: StorageKey, v: StorageValue) {
self.backend = self.backend.update(vec![(None, vec![(k, Some(v))])]);
self.backend.insert(vec![(None, vec![(k, Some(v))])]);
}
/// Registers the given extension for this instance.
@@ -157,7 +157,7 @@ impl<H: Hasher, N: ChangesTrieBlockNumber> TestExternalities<H, N>
}
impl<H: Hasher, N: ChangesTrieBlockNumber> std::fmt::Debug for TestExternalities<H, N>
where H::Out: codec::Codec,
where H::Out: Ord + codec::Codec,
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "overlay: {:?}\nbackend: {:?}", self.overlay, self.backend.pairs())
@@ -193,7 +193,7 @@ impl<H: Hasher, N: ChangesTrieBlockNumber> From<Storage> for TestExternalities<H
impl<H, N> sp_externalities::ExtensionStore for TestExternalities<H, N> where
H: Hasher,
H::Out: codec::Codec,
H::Out: Ord + codec::Codec,
N: ChangesTrieBlockNumber,
{
fn extension_by_type_id(&mut self, type_id: TypeId) -> Option<&mut dyn Any> {
@@ -230,7 +230,7 @@ mod tests {
ext.set_storage(b"doe".to_vec(), b"reindeer".to_vec());
ext.set_storage(b"dog".to_vec(), b"puppy".to_vec());
ext.set_storage(b"dogglesworth".to_vec(), b"cat".to_vec());
const ROOT: [u8; 32] = hex!("2a340d3dfd52f5992c6b117e9e45f479e6da5afffafeb26ab619cf137a95aeb8");
const ROOT: [u8; 32] = hex!("555d4777b52e9196e3f6373c556cc661e79cd463f881ab9e921e70fc30144bf4");
assert_eq!(&ext.storage_root()[..], &ROOT);
}
@@ -29,7 +29,7 @@ use crate::{
/// Patricia trie-based backend. Transaction type is an overlay of changes to commit.
pub struct TrieBackend<S: TrieBackendStorage<H>, H: Hasher> {
essence: TrieBackendEssence<S, H>,
pub (crate) essence: TrieBackendEssence<S, H>,
}
impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackend<S, H> where H::Out: Codec {
@@ -50,6 +50,11 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackend<S, H> where H::Out: Codec
self.essence.backend_storage()
}
/// Get backend storage reference.
pub fn backend_storage_mut(&mut self) -> &mut S {
self.essence.backend_storage_mut()
}
/// Get trie root.
pub fn root(&self) -> &H::Out {
self.essence.root()
@@ -179,6 +184,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
&mut write_overlay,
);
let delta: Vec<_> = delta.into_iter().collect();
match delta_trie_root::<Layout<H>, _, _, _, _>(&mut eph, root, delta) {
Ok(ret) => root = ret,
Err(e) => warn!(target: "trie", "Failed to write to trie: {}", e),
@@ -243,6 +249,10 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
fn usage_info(&self) -> crate::UsageInfo {
crate::UsageInfo::empty()
}
fn wipe(&self) -> Result<(), Self::Error> {
Ok(())
}
}
#[cfg(test)]
@@ -55,11 +55,21 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
&self.storage
}
/// Get backend storage reference.
pub fn backend_storage_mut(&mut self) -> &mut S {
&mut self.storage
}
/// Get trie root.
pub fn root(&self) -> &H::Out {
&self.root
}
/// Set trie root. This is useful for testing.
pub fn set_root(&mut self, root: H::Out) {
self.root = root;
}
/// Consumes self and returns underlying storage.
pub fn into_storage(self) -> S {
self.storage