mod build;
mod build_cache;
mod build_iterator;
mod changes_iterator;
mod input;
mod prune;
mod storage;
mod surface_iterator;
pub use self::build_cache::{BuildCache, CachedBuildData, CacheAction};
pub use self::storage::InMemoryStorage;
pub use self::changes_iterator::{
key_changes, key_changes_proof,
key_changes_proof_check, key_changes_proof_check_with_db,
};
pub use self::prune::prune;
use std::collections::{HashMap, HashSet};
use std::convert::TryInto;
use hash_db::{Hasher, Prefix};
use num_traits::{One, Zero};
use codec::{Decode, Encode};
use sp_core;
use sp_core::storage::PrefixedStorageKey;
use sp_trie::{MemoryDB, DBValue, TrieMut};
use sp_trie::trie_types::TrieDBMut;
use crate::{
StorageKey,
backend::Backend,
overlayed_changes::OverlayedChanges,
changes_trie::{
build::prepare_input,
build_cache::{IncompleteCachedBuildData, IncompleteCacheAction},
},
};
pub trait BlockNumber:
Send + Sync + 'static +
std::fmt::Display +
Clone +
From<u32> + TryInto<u32> + One + Zero +
PartialEq + Ord +
std::hash::Hash +
std::ops::Add<Self, Output=Self> + ::std::ops::Sub<Self, Output=Self> +
std::ops::Mul<Self, Output=Self> + ::std::ops::Div<Self, Output=Self> +
std::ops::Rem<Self, Output=Self> +
std::ops::AddAssign<Self> +
num_traits::CheckedMul + num_traits::CheckedSub +
Decode + Encode
{}
impl<T> BlockNumber for T where T:
Send + Sync + 'static +
std::fmt::Display +
Clone +
From<u32> + TryInto<u32> + One + Zero +
PartialEq + Ord +
std::hash::Hash +
std::ops::Add<Self, Output=Self> + ::std::ops::Sub<Self, Output=Self> +
std::ops::Mul<Self, Output=Self> + ::std::ops::Div<Self, Output=Self> +
std::ops::Rem<Self, Output=Self> +
std::ops::AddAssign<Self> +
num_traits::CheckedMul + num_traits::CheckedSub +
Decode + Encode,
{}
#[derive(Debug)]
pub struct AnchorBlockId<Hash: std::fmt::Debug, Number: BlockNumber> {
pub hash: Hash,
pub number: Number,
}
pub struct State<'a, H, Number> {
pub config: Configuration,
pub zero: Number,
pub storage: &'a dyn Storage<H, Number>,
}
pub trait RootsStorage<H: Hasher, Number: BlockNumber>: Send + Sync {
fn build_anchor(&self, hash: H::Out) -> Result<AnchorBlockId<H::Out, Number>, String>;
fn root(&self, anchor: &AnchorBlockId<H::Out, Number>, block: Number) -> Result<Option<H::Out>, String>;
}
pub trait Storage<H: Hasher, Number: BlockNumber>: RootsStorage<H, Number> {
fn as_roots_storage(&self) -> &dyn RootsStorage<H, Number>;
fn with_cached_changed_keys(
&self,
root: &H::Out,
functor: &mut dyn FnMut(&HashMap<Option<PrefixedStorageKey>, HashSet<StorageKey>>),
) -> bool;
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>, String>;
}
pub struct TrieBackendStorageAdapter<'a, H: Hasher, Number: BlockNumber>(pub &'a dyn Storage<H, Number>);
impl<'a, H: Hasher, N: BlockNumber> crate::TrieBackendStorage<H> for TrieBackendStorageAdapter<'a, H, N> {
type Overlay = sp_trie::MemoryDB<H>;
fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>, String> {
self.0.get(key, prefix)
}
}
pub type Configuration = sp_core::ChangesTrieConfiguration;
#[derive(Clone)]
pub struct ConfigurationRange<'a, N> {
pub config: &'a Configuration,
pub zero: N,
pub end: Option<N>,
}
impl<'a, H, Number> State<'a, H, Number> {
pub fn new(
config: Configuration,
zero: Number,
storage: &'a dyn Storage<H, Number>,
) -> Self {
Self {
config,
zero,
storage,
}
}
}
impl<'a, H, Number: Clone> Clone for State<'a, H, Number> {
fn clone(&self) -> Self {
State {
config: self.config.clone(),
zero: self.zero.clone(),
storage: self.storage,
}
}
}
pub fn disabled_state<'a, H, Number>() -> Option<State<'a, H, Number>> {
None
}
pub fn build_changes_trie<'a, B: Backend<H>, H: Hasher, Number: BlockNumber>(
backend: &B,
state: Option<&'a State<'a, H, Number>>,
changes: &OverlayedChanges,
parent_hash: H::Out,
panic_on_storage_error: bool,
) -> Result<Option<(MemoryDB<H>, H::Out, CacheAction<H::Out, Number>)>, ()>
where
H::Out: Ord + 'static + Encode,
{
fn maybe_panic<R, E: std::fmt::Debug>(
res: std::result::Result<R, E>,
panic: bool,
) -> std::result::Result<R, ()> {
res.map(Ok)
.unwrap_or_else(|e| if panic {
panic!("changes trie: storage access is not allowed to fail within runtime: {:?}", e)
} else {
Err(())
})
}
let state = match state {
Some(state) => state,
None => return Ok(None),
};
let parent = state.storage.build_anchor(parent_hash).map_err(|_| ())?;
let block = parent.number.clone() + One::one();
let is_config_changed = match changes.storage(sp_core::storage::well_known_keys::CHANGES_TRIE_CONFIG) {
Some(Some(new_config)) => new_config != &state.config.encode()[..],
Some(None) => true,
None => false,
};
let config_range = ConfigurationRange {
config: &state.config,
zero: state.zero.clone(),
end: if is_config_changed { Some(block.clone()) } else { None },
};
let (input_pairs, child_input_pairs, digest_input_blocks) = maybe_panic(
prepare_input::<B, H, Number>(
backend,
state.storage,
config_range.clone(),
changes,
&parent,
),
panic_on_storage_error,
)?;
let mut cache_action = prepare_cached_build_data(config_range, block.clone());
let needs_changed_keys = cache_action.collects_changed_keys();
cache_action = cache_action.set_digest_input_blocks(digest_input_blocks);
let mut mdb = MemoryDB::default();
let mut child_roots = Vec::with_capacity(child_input_pairs.len());
for (child_index, input_pairs) in child_input_pairs {
let mut not_empty = false;
let mut root = Default::default();
{
let mut trie = TrieDBMut::<H>::new(&mut mdb, &mut root);
let mut storage_changed_keys = HashSet::new();
for input_pair in input_pairs {
if needs_changed_keys {
if let Some(key) = input_pair.key() {
storage_changed_keys.insert(key.to_vec());
}
}
let (key, value) = input_pair.into();
not_empty = true;
maybe_panic(trie.insert(&key, &value), panic_on_storage_error)?;
}
cache_action = cache_action.insert(
Some(child_index.storage_key.clone()),
storage_changed_keys,
);
}
if not_empty {
child_roots.push(input::InputPair::ChildIndex(child_index, root.as_ref().to_vec()));
}
}
let mut root = Default::default();
{
let mut trie = TrieDBMut::<H>::new(&mut mdb, &mut root);
for (key, value) in child_roots.into_iter().map(Into::into) {
maybe_panic(trie.insert(&key, &value), panic_on_storage_error)?;
}
let mut storage_changed_keys = HashSet::new();
for input_pair in input_pairs {
if needs_changed_keys {
if let Some(key) = input_pair.key() {
storage_changed_keys.insert(key.to_vec());
}
}
let (key, value) = input_pair.into();
maybe_panic(trie.insert(&key, &value), panic_on_storage_error)?;
}
cache_action = cache_action.insert(
None,
storage_changed_keys,
);
}
let cache_action = cache_action.complete(block, &root);
Ok(Some((mdb, root, cache_action)))
}
fn prepare_cached_build_data<Number: BlockNumber>(
config: ConfigurationRange<Number>,
block: Number,
) -> IncompleteCacheAction<Number> {
if !config.config.is_digest_build_enabled() {
return IncompleteCacheAction::Clear;
}
if config.end.as_ref() == Some(&block) {
return IncompleteCacheAction::Clear;
}
match config.config.digest_level_at_block(config.zero.clone(), block) {
Some((digest_level, _, _)) if digest_level == config.config.digest_levels => IncompleteCacheAction::Clear,
_ => IncompleteCacheAction::CacheBuildData(IncompleteCachedBuildData::new()),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cache_is_cleared_when_digests_are_disabled() {
let config = Configuration { digest_interval: 0, digest_levels: 0 };
let config_range = ConfigurationRange { zero: 0, end: None, config: &config };
assert_eq!(prepare_cached_build_data(config_range, 8u32), IncompleteCacheAction::Clear);
}
#[test]
fn build_data_is_cached_when_digests_are_enabled() {
let config = Configuration { digest_interval: 8, digest_levels: 2 };
let config_range = ConfigurationRange { zero: 0, end: None, config: &config };
assert!(prepare_cached_build_data(config_range.clone(), 4u32).collects_changed_keys());
assert!(prepare_cached_build_data(config_range.clone(), 7u32).collects_changed_keys());
assert!(prepare_cached_build_data(config_range, 8u32).collects_changed_keys());
}
#[test]
fn cache_is_cleared_when_digests_are_enabled_and_top_level_digest_is_built() {
let config = Configuration { digest_interval: 8, digest_levels: 2 };
let config_range = ConfigurationRange { zero: 0, end: None, config: &config };
assert_eq!(prepare_cached_build_data(config_range, 64u32), IncompleteCacheAction::Clear);
}
#[test]
fn cache_is_cleared_when_end_block_of_configuration_is_built() {
let config = Configuration { digest_interval: 8, digest_levels: 2 };
let config_range = ConfigurationRange { zero: 0, end: Some(4u32), config: &config };
assert_eq!(prepare_cached_build_data(config_range.clone(), 4u32), IncompleteCacheAction::Clear);
}
}