mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 03:01:07 +00:00
Allow updating configuration of changes tries (#3201)
* DigestItem::ChangesTrieSignal * introduce changes_trie::State * introduce config activation block * ChangesTrieSignal::as_new_configuration * moved well_known_cache_keys to client * extracted DbChangesTrieStorage to separate file * change meaning of none in blockchain cache * changes trie config (FULL) cache draft * eliminating const ChangesTrieConfiguration * delay pruning * continue elimination * do not prune CT config from cache * removed redundant code * fix some TODOs * introduce ConfigurationRange * use Configuration range in build * build skewed digest * remove debug print * extracted surface iterator * key_changes works with skewed digests * fix client build * add test for NeverPrune * fix TODO * fixed some TODOs * more tests * fixing TODOs * fixed compilation * update runtime version * git rid of large tuple * too long lines * config_activation_block -> zero * obsolete TODO * removed unjustified expect * update TODOs with issue number * new CT pruning algorithm fixed cache + multiple blocks finalization track CT configuraiton on light clients support CT configuration change revert revert CT config test new CT pruning algorithm fixed cache + multiple blocks finalization track CT configuraiton on light clients support CT configuration change revert revert CT config test * BlockIdOrHeader isn't really required * removed debug leftovers + some docs * more docs * more post-merge fixes * more post-merge fixes * revertes some unnecessary changes * reverted unnecessary changes * fix compilation + unnecessary changes * (restart CI) * fix cache update when finalizing multiple blocks * fixed tests * collect_extrinsics -> set_collect_extrinsics * restore lost test * do not calculate block number twice * Update primitives/blockchain/src/error.rs Co-Authored-By: cheme <emericchevalier.pro@gmail.com> * map_err -> unwrap_or * document get_at Result * delete abandoned file * added weight for set_changes_trie_config * prefer_configs -> fail_if_disabled * Update client/api/src/backend.rs Co-Authored-By: cheme <emericchevalier.pro@gmail.com> * Update client/db/src/changes_tries_storage.rs Co-Authored-By: cheme <emericchevalier.pro@gmail.com> * CommitOperation+merge -> CommitOperations * fixed test compilation * merged two different CTRange structs * lost file * uggrade db from v0 to v1 (init CT cache + add column) * fix after merge Co-authored-by: cheme <emericchevalier.pro@gmail.com> Co-authored-by: Gavin Wood <github@gavwood.com>
This commit is contained in:
committed by
Gavin Wood
parent
45fbf09dac
commit
febf29390a
@@ -356,7 +356,6 @@ mod test {
|
||||
OverlayedChanges,
|
||||
Configuration,
|
||||
) {
|
||||
let config = Configuration { digest_interval: 4, digest_levels: 2 };
|
||||
let backend: InMemoryBackend<_> = vec![
|
||||
(vec![100], vec![255]),
|
||||
(vec![101], vec![255]),
|
||||
@@ -465,8 +464,9 @@ mod test {
|
||||
].into_iter().collect(), CHILD_INFO_1.to_owned())),
|
||||
].into_iter().collect(),
|
||||
},
|
||||
changes_trie_config: Some(config.clone()),
|
||||
collect_extrinsics: true,
|
||||
};
|
||||
let config = Configuration { digest_interval: 4, digest_levels: 2 };
|
||||
|
||||
(backend, storage, changes, config)
|
||||
}
|
||||
@@ -553,7 +553,6 @@ mod test {
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: zero + 4, key: vec![100] }, vec![0, 2]),
|
||||
]),
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
test_with_zero(0);
|
||||
@@ -597,7 +596,6 @@ mod test {
|
||||
InputPair::ExtrinsicIndex(ExtrinsicIndex { block: zero + 16, key: vec![100] }, vec![0, 2]),
|
||||
]),
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
test_with_zero(0);
|
||||
@@ -706,8 +704,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn cache_is_used_when_changes_trie_is_built() {
|
||||
let (backend, mut storage, changes, _) = prepare_for_build(0);
|
||||
let config = changes.changes_trie_config.as_ref().unwrap();
|
||||
let (backend, mut storage, changes, config) = prepare_for_build(0);
|
||||
let parent = AnchorBlockId { hash: Default::default(), number: 15 };
|
||||
|
||||
// override some actual values from storage with values from the cache
|
||||
@@ -770,6 +767,5 @@ mod test {
|
||||
InputPair::DigestIndex(DigestIndex { block: 16u64, key: vec![106] }, vec![4]),
|
||||
],
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ 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, oldest_non_pruned_trie};
|
||||
pub use self::prune::prune;
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::convert::TryInto;
|
||||
@@ -121,6 +121,18 @@ pub struct AnchorBlockId<Hash: std::fmt::Debug, Number: BlockNumber> {
|
||||
pub number: Number,
|
||||
}
|
||||
|
||||
/// Changes tries state at some block.
|
||||
pub struct State<'a, H, Number> {
|
||||
/// Configuration that is active at given block.
|
||||
pub config: Configuration,
|
||||
/// Configuration activation block number. Zero if it is the first coonfiguration on the chain,
|
||||
/// or number of the block that have emit NewConfiguration signal (thus activating configuration
|
||||
/// starting from the **next** block).
|
||||
pub zero: Number,
|
||||
/// Underlying changes tries storage reference.
|
||||
pub storage: &'a dyn Storage<H, Number>,
|
||||
}
|
||||
|
||||
/// Changes trie storage. Provides access to trie roots and trie nodes.
|
||||
pub trait RootsStorage<H: Hasher, Number: BlockNumber>: Send + Sync {
|
||||
/// Resolve hash of the block into anchor.
|
||||
@@ -170,14 +182,43 @@ pub struct ConfigurationRange<'a, N> {
|
||||
pub end: Option<N>,
|
||||
}
|
||||
|
||||
impl<'a, H, Number> State<'a, H, Number> {
|
||||
/// Create state with given config and storage.
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create state where changes tries are disabled.
|
||||
pub fn disabled_state<'a, H, Number>() -> Option<State<'a, H, Number>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Compute the changes trie root and transaction for given block.
|
||||
/// Returns Err(()) if unknown `parent_hash` has been passed.
|
||||
/// Returns Ok(None) if there's no data to perform computation.
|
||||
/// Panics if background storage returns an error (and `panic_on_storage_error` is `true`) OR
|
||||
/// if insert to MemoryDB fails.
|
||||
pub fn build_changes_trie<'a, B: Backend<H>, S: Storage<H, Number>, H: Hasher, Number: BlockNumber>(
|
||||
/// Panics if background storage returns an error OR if insert to MemoryDB fails.
|
||||
pub fn build_changes_trie<'a, B: Backend<H>, H: Hasher, Number: BlockNumber>(
|
||||
backend: &B,
|
||||
storage: Option<&'a S>,
|
||||
state: Option<&'a State<'a, H, Number>>,
|
||||
changes: &OverlayedChanges,
|
||||
parent_hash: H::Out,
|
||||
panic_on_storage_error: bool,
|
||||
@@ -185,11 +226,6 @@ pub fn build_changes_trie<'a, B: Backend<H>, S: Storage<H, Number>, H: Hasher, N
|
||||
where
|
||||
H::Out: Ord + 'static + Encode,
|
||||
{
|
||||
let (storage, config) = match (storage, changes.changes_trie_config.as_ref()) {
|
||||
(Some(storage), Some(config)) => (storage, config),
|
||||
_ => return Ok(None),
|
||||
};
|
||||
|
||||
/// Panics when `res.is_err() && panic`, otherwise it returns `Err(())` on an error.
|
||||
fn maybe_panic<R, E: std::fmt::Debug>(
|
||||
res: std::result::Result<R, E>,
|
||||
@@ -203,23 +239,35 @@ pub fn build_changes_trie<'a, B: Backend<H>, S: Storage<H, Number>, H: Hasher, N
|
||||
})
|
||||
}
|
||||
|
||||
// FIXME: remove this in https://github.com/paritytech/substrate/pull/3201
|
||||
let config = ConfigurationRange {
|
||||
config,
|
||||
zero: Zero::zero(),
|
||||
end: None,
|
||||
// when storage isn't provided, changes tries aren't created
|
||||
let state = match state {
|
||||
Some(state) => state,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
// build_anchor error should not be considered fatal
|
||||
let parent = storage.build_anchor(parent_hash).map_err(|_| ())?;
|
||||
let parent = state.storage.build_anchor(parent_hash).map_err(|_| ())?;
|
||||
let block = parent.number.clone() + One::one();
|
||||
|
||||
// prepare configuration range - we already know zero block. Current block may be the end block if configuration
|
||||
// has been changed in this block
|
||||
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 },
|
||||
};
|
||||
|
||||
// storage errors are considered fatal (similar to situations when runtime fetches values from storage)
|
||||
let (input_pairs, child_input_pairs, digest_input_blocks) = maybe_panic(
|
||||
prepare_input::<B, H, Number>(
|
||||
backend,
|
||||
storage,
|
||||
config.clone(),
|
||||
state.storage,
|
||||
config_range.clone(),
|
||||
changes,
|
||||
&parent,
|
||||
),
|
||||
@@ -227,7 +275,7 @@ pub fn build_changes_trie<'a, B: Backend<H>, S: Storage<H, Number>, H: Hasher, N
|
||||
)?;
|
||||
|
||||
// prepare cached data
|
||||
let mut cache_action = prepare_cached_build_data(config, block.clone());
|
||||
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);
|
||||
|
||||
|
||||
@@ -19,52 +19,26 @@
|
||||
use hash_db::Hasher;
|
||||
use sp_trie::Recorder;
|
||||
use log::warn;
|
||||
use num_traits::{One, Zero};
|
||||
use num_traits::One;
|
||||
use crate::proving_backend::ProvingBackendRecorder;
|
||||
use crate::trie_backend_essence::TrieBackendEssence;
|
||||
use crate::changes_trie::{AnchorBlockId, Configuration, Storage, BlockNumber};
|
||||
use crate::changes_trie::{AnchorBlockId, Storage, BlockNumber};
|
||||
use crate::changes_trie::storage::TrieBackendAdapter;
|
||||
use crate::changes_trie::input::{ChildIndex, InputKey};
|
||||
use codec::{Decode, Codec};
|
||||
|
||||
/// Get number of oldest block for which changes trie is not pruned
|
||||
/// given changes trie configuration, pruning parameter and number of
|
||||
/// best finalized block.
|
||||
pub fn oldest_non_pruned_trie<Number: BlockNumber>(
|
||||
config: &Configuration,
|
||||
min_blocks_to_keep: Number,
|
||||
best_finalized_block: Number,
|
||||
) -> Number {
|
||||
let max_digest_interval = config.max_digest_interval();
|
||||
let best_finalized_block_rem = best_finalized_block.clone() % max_digest_interval.into();
|
||||
let max_digest_block = best_finalized_block - best_finalized_block_rem;
|
||||
match pruning_range(config, min_blocks_to_keep, max_digest_block) {
|
||||
Some((_, last_pruned_block)) => last_pruned_block + One::one(),
|
||||
None => One::one(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Prune obsolete changes tries. Pruning happens at the same block, where highest
|
||||
/// level digest is created. Pruning guarantees to save changes tries for last
|
||||
/// `min_blocks_to_keep` blocks. We only prune changes tries at `max_digest_interval`
|
||||
/// ranges.
|
||||
/// Returns MemoryDB that contains all deleted changes tries nodes.
|
||||
pub fn prune<S: Storage<H, Number>, H: Hasher, Number: BlockNumber, F: FnMut(H::Out)>(
|
||||
config: &Configuration,
|
||||
storage: &S,
|
||||
min_blocks_to_keep: Number,
|
||||
pub fn prune<H: Hasher, Number: BlockNumber, F: FnMut(H::Out)>(
|
||||
storage: &dyn Storage<H, Number>,
|
||||
first: Number,
|
||||
last: Number,
|
||||
current_block: &AnchorBlockId<H::Out, Number>,
|
||||
mut remove_trie_node: F,
|
||||
) where H::Out: Codec {
|
||||
|
||||
// select range for pruning
|
||||
let (first, last) = match pruning_range(config, min_blocks_to_keep, current_block.number.clone()) {
|
||||
Some((first, last)) => (first, last),
|
||||
None => return,
|
||||
};
|
||||
|
||||
// delete changes trie for every block in range
|
||||
// FIXME: limit `max_digest_interval` so that this cycle won't involve huge ranges
|
||||
let mut block = first;
|
||||
loop {
|
||||
if block >= last.clone() + One::one() {
|
||||
@@ -112,8 +86,8 @@ pub fn prune<S: Storage<H, Number>, H: Hasher, Number: BlockNumber, F: FnMut(H::
|
||||
}
|
||||
|
||||
// Prune a trie.
|
||||
fn prune_trie<S: Storage<H, Number>, H: Hasher, Number: BlockNumber, F: FnMut(H::Out)>(
|
||||
storage: &S,
|
||||
fn prune_trie<H: Hasher, Number: BlockNumber, F: FnMut(H::Out)>(
|
||||
storage: &dyn Storage<H, Number>,
|
||||
root: H::Out,
|
||||
remove_trie_node: &mut F,
|
||||
) where H::Out: Codec {
|
||||
@@ -136,100 +110,26 @@ fn prune_trie<S: Storage<H, Number>, H: Hasher, Number: BlockNumber, F: FnMut(H:
|
||||
}
|
||||
}
|
||||
|
||||
/// Select blocks range (inclusive from both ends) for pruning changes tries in.
|
||||
fn pruning_range<Number: BlockNumber>(
|
||||
config: &Configuration,
|
||||
min_blocks_to_keep: Number,
|
||||
block: Number,
|
||||
) -> Option<(Number, Number)> {
|
||||
// compute number of changes tries we actually want to keep
|
||||
let (prune_interval, blocks_to_keep) = if config.is_digest_build_enabled() {
|
||||
// we only CAN prune at block where max-level-digest is created
|
||||
let max_digest_interval = match config.digest_level_at_block(Zero::zero(), block.clone()) {
|
||||
Some((digest_level, digest_interval, _)) if digest_level == config.digest_levels =>
|
||||
digest_interval,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
// compute maximal number of high-level digests to keep
|
||||
let max_digest_intervals_to_keep = max_digest_intervals_to_keep(min_blocks_to_keep, max_digest_interval);
|
||||
|
||||
// number of blocks BEFORE current block where changes tries are not pruned
|
||||
(
|
||||
max_digest_interval,
|
||||
max_digest_intervals_to_keep.checked_mul(&max_digest_interval.into())
|
||||
)
|
||||
} else {
|
||||
(
|
||||
1,
|
||||
Some(min_blocks_to_keep)
|
||||
)
|
||||
};
|
||||
|
||||
// last block for which changes trie is pruned
|
||||
let last_block_to_prune = blocks_to_keep.and_then(|b| block.checked_sub(&b));
|
||||
let first_block_to_prune = last_block_to_prune
|
||||
.clone()
|
||||
.and_then(|b| b.checked_sub(&prune_interval.into()));
|
||||
|
||||
last_block_to_prune
|
||||
.and_then(|last| first_block_to_prune.map(|first| (first + One::one(), last)))
|
||||
}
|
||||
|
||||
/// Select pruning delay for the changes tries. To make sure we could build a changes
|
||||
/// trie at block B, we need an access to previous:
|
||||
/// max_digest_interval = config.digest_interval ^ config.digest_levels
|
||||
/// blocks. So we can only prune blocks that are earlier than B - max_digest_interval.
|
||||
/// The pruning_delay stands for number of max_digest_interval-s that we want to keep:
|
||||
/// 0 or 1: means that only last changes trie is guaranteed to exists;
|
||||
/// 2: the last changes trie + previous changes trie
|
||||
/// ...
|
||||
fn max_digest_intervals_to_keep<Number: BlockNumber>(
|
||||
min_blocks_to_keep: Number,
|
||||
max_digest_interval: u32,
|
||||
) -> Number {
|
||||
// config.digest_level_at_block ensures that it is not zero
|
||||
debug_assert!(max_digest_interval != 0);
|
||||
|
||||
let max_digest_intervals_to_keep = min_blocks_to_keep / max_digest_interval.into();
|
||||
if max_digest_intervals_to_keep.is_zero() {
|
||||
One::one()
|
||||
} else {
|
||||
max_digest_intervals_to_keep
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashSet;
|
||||
use sp_trie::MemoryDB;
|
||||
use sp_core::Blake2Hasher;
|
||||
use sp_core::{H256, Blake2Hasher};
|
||||
use crate::backend::insert_into_memory_db;
|
||||
use crate::changes_trie::storage::InMemoryStorage;
|
||||
use codec::Encode;
|
||||
use super::*;
|
||||
|
||||
fn config(interval: u32, levels: u32) -> Configuration {
|
||||
Configuration {
|
||||
digest_interval: interval,
|
||||
digest_levels: levels,
|
||||
}
|
||||
}
|
||||
|
||||
fn prune_by_collect<S: Storage<H, u64>, H: Hasher>(
|
||||
config: &Configuration,
|
||||
storage: &S,
|
||||
min_blocks_to_keep: u64,
|
||||
fn prune_by_collect(
|
||||
storage: &dyn Storage<Blake2Hasher, u64>,
|
||||
first: u64,
|
||||
last: u64,
|
||||
current_block: u64,
|
||||
) -> HashSet<H::Out> where H::Out: Codec {
|
||||
) -> HashSet<H256> {
|
||||
let mut pruned_trie_nodes = HashSet::new();
|
||||
prune(
|
||||
config,
|
||||
storage,
|
||||
min_blocks_to_keep,
|
||||
&AnchorBlockId { hash: Default::default(), number: current_block },
|
||||
|node| { pruned_trie_nodes.insert(node); },
|
||||
);
|
||||
let anchor = AnchorBlockId { hash: Default::default(), number: current_block };
|
||||
prune(storage, first, last, &anchor,
|
||||
|node| { pruned_trie_nodes.insert(node); });
|
||||
pruned_trie_nodes
|
||||
}
|
||||
|
||||
@@ -243,7 +143,9 @@ mod tests {
|
||||
&mut mdb1, vec![(vec![10], vec![20])]).unwrap();
|
||||
let mut mdb2 = MemoryDB::<Blake2Hasher>::default();
|
||||
let root2 = insert_into_memory_db::<Blake2Hasher, _>(
|
||||
&mut mdb2, vec![(vec![11], vec![21]), (vec![12], vec![22])]).unwrap();
|
||||
&mut mdb2,
|
||||
vec![(vec![11], vec![21]), (vec![12], vec![22])],
|
||||
).unwrap();
|
||||
let mut mdb3 = MemoryDB::<Blake2Hasher>::default();
|
||||
let ch_root3 = insert_into_memory_db::<Blake2Hasher, _>(
|
||||
&mut mdb3, vec![(vec![110], vec![120])]).unwrap();
|
||||
@@ -254,7 +156,9 @@ mod tests {
|
||||
]).unwrap();
|
||||
let mut mdb4 = MemoryDB::<Blake2Hasher>::default();
|
||||
let root4 = insert_into_memory_db::<Blake2Hasher, _>(
|
||||
&mut mdb4, vec![(vec![15], vec![25])]).unwrap();
|
||||
&mut mdb4,
|
||||
vec![(vec![15], vec![25])],
|
||||
).unwrap();
|
||||
let storage = InMemoryStorage::new();
|
||||
storage.insert(65, root1, mdb1);
|
||||
storage.insert(66, root2, mdb2);
|
||||
@@ -264,109 +168,20 @@ mod tests {
|
||||
storage
|
||||
}
|
||||
|
||||
// l1-digest is created every 2 blocks
|
||||
// l2-digest is created every 4 blocks
|
||||
// we do not want to keep any additional changes tries
|
||||
// => only one l2-digest is saved AND it is pruned once next is created
|
||||
let config = Configuration { digest_interval: 2, digest_levels: 2 };
|
||||
let storage = prepare_storage();
|
||||
assert!(prune_by_collect(&config, &storage, 0, 69).is_empty());
|
||||
assert!(prune_by_collect(&config, &storage, 0, 70).is_empty());
|
||||
assert!(prune_by_collect(&config, &storage, 0, 71).is_empty());
|
||||
let non_empty = prune_by_collect(&config, &storage, 0, 72);
|
||||
assert!(!non_empty.is_empty());
|
||||
storage.remove_from_storage(&non_empty);
|
||||
assert!(storage.into_mdb().drain().is_empty());
|
||||
assert!(prune_by_collect(&storage, 20, 30, 90).is_empty());
|
||||
assert!(!storage.into_mdb().drain().is_empty());
|
||||
|
||||
// l1-digest is created every 2 blocks
|
||||
// l2-digest is created every 4 blocks
|
||||
// we want keep 1 additional changes tries
|
||||
let config = Configuration { digest_interval: 2, digest_levels: 2 };
|
||||
let storage = prepare_storage();
|
||||
assert!(prune_by_collect(&config, &storage, 8, 69).is_empty());
|
||||
assert!(prune_by_collect(&config, &storage, 8, 70).is_empty());
|
||||
assert!(prune_by_collect(&config, &storage, 8, 71).is_empty());
|
||||
assert!(prune_by_collect(&config, &storage, 8, 72).is_empty());
|
||||
assert!(prune_by_collect(&config, &storage, 8, 73).is_empty());
|
||||
assert!(prune_by_collect(&config, &storage, 8, 74).is_empty());
|
||||
assert!(prune_by_collect(&config, &storage, 8, 75).is_empty());
|
||||
let non_empty = prune_by_collect(&config, &storage, 8, 76);
|
||||
assert!(!non_empty.is_empty());
|
||||
storage.remove_from_storage(&non_empty);
|
||||
assert!(storage.into_mdb().drain().is_empty());
|
||||
let prune60_65 = prune_by_collect(&storage, 60, 65, 90);
|
||||
assert!(!prune60_65.is_empty());
|
||||
storage.remove_from_storage(&prune60_65);
|
||||
assert!(!storage.into_mdb().drain().is_empty());
|
||||
|
||||
// l1-digest is created every 2 blocks
|
||||
// we want keep 2 additional changes tries
|
||||
let config = Configuration { digest_interval: 2, digest_levels: 1 };
|
||||
let storage = prepare_storage();
|
||||
assert!(prune_by_collect(&config, &storage, 4, 69).is_empty());
|
||||
let non_empty = prune_by_collect(&config, &storage, 4, 70);
|
||||
assert!(!non_empty.is_empty());
|
||||
storage.remove_from_storage(&non_empty);
|
||||
assert!(prune_by_collect(&config, &storage, 4, 71).is_empty());
|
||||
let non_empty = prune_by_collect(&config, &storage, 4, 72);
|
||||
assert!(!non_empty.is_empty());
|
||||
storage.remove_from_storage(&non_empty);
|
||||
let prune60_70 = prune_by_collect(&storage, 60, 70, 90);
|
||||
assert!(!prune60_70.is_empty());
|
||||
storage.remove_from_storage(&prune60_70);
|
||||
assert!(storage.into_mdb().drain().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pruning_range_works() {
|
||||
// DIGESTS ARE NOT CREATED + NO TRIES ARE PRUNED
|
||||
assert_eq!(pruning_range(&config(10, 0), 2u64, 2u64), None);
|
||||
|
||||
// DIGESTS ARE NOT CREATED + SOME TRIES ARE PRUNED
|
||||
assert_eq!(pruning_range(&config(10, 0), 100u64, 110u64), Some((10, 10)));
|
||||
assert_eq!(pruning_range(&config(10, 0), 100u64, 210u64), Some((110, 110)));
|
||||
|
||||
// DIGESTS ARE CREATED + NO TRIES ARE PRUNED
|
||||
|
||||
assert_eq!(pruning_range(&config(10, 2), 2u64, 0u64), None);
|
||||
assert_eq!(pruning_range(&config(10, 2), 30u64, 100u64), None);
|
||||
assert_eq!(pruning_range(&config(::std::u32::MAX, 2), 1u64, 1024u64), None);
|
||||
assert_eq!(pruning_range(&config(::std::u32::MAX, 2), ::std::u64::MAX, 1024u64), None);
|
||||
assert_eq!(pruning_range(&config(32, 2), 2048u64, 512u64), None);
|
||||
assert_eq!(pruning_range(&config(32, 2), 2048u64, 1024u64), None);
|
||||
|
||||
// DIGESTS ARE CREATED + SOME TRIES ARE PRUNED
|
||||
|
||||
// when we do not want to keep any highest-level-digests
|
||||
// (system forces to keep at least one)
|
||||
assert_eq!(pruning_range(&config(4, 2), 0u64, 32u64), Some((1, 16)));
|
||||
assert_eq!(pruning_range(&config(4, 2), 0u64, 64u64), Some((33, 48)));
|
||||
// when we want to keep 1 (last) highest-level-digest
|
||||
assert_eq!(pruning_range(&config(4, 2), 16u64, 32u64), Some((1, 16)));
|
||||
assert_eq!(pruning_range(&config(4, 2), 16u64, 64u64), Some((33, 48)));
|
||||
// when we want to keep 1 (last) + 1 additional level digests
|
||||
assert_eq!(pruning_range(&config(32, 2), 4096u64, 5120u64), Some((1, 1024)));
|
||||
assert_eq!(pruning_range(&config(32, 2), 4096u64, 6144u64), Some((1025, 2048)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn max_digest_intervals_to_keep_works() {
|
||||
assert_eq!(max_digest_intervals_to_keep(1024u64, 1025), 1u64);
|
||||
assert_eq!(max_digest_intervals_to_keep(1024u64, 1023), 1u64);
|
||||
assert_eq!(max_digest_intervals_to_keep(1024u64, 512), 2u64);
|
||||
assert_eq!(max_digest_intervals_to_keep(1024u64, 511), 2u64);
|
||||
assert_eq!(max_digest_intervals_to_keep(1024u64, 100), 10u64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn oldest_non_pruned_trie_works() {
|
||||
// when digests are not created at all
|
||||
assert_eq!(oldest_non_pruned_trie(&config(0, 0), 100u64, 10u64), 1);
|
||||
assert_eq!(oldest_non_pruned_trie(&config(0, 0), 100u64, 110u64), 11);
|
||||
|
||||
// when only l1 digests are created
|
||||
assert_eq!(oldest_non_pruned_trie(&config(100, 1), 100u64, 50u64), 1);
|
||||
assert_eq!(oldest_non_pruned_trie(&config(100, 1), 100u64, 110u64), 1);
|
||||
assert_eq!(oldest_non_pruned_trie(&config(100, 1), 100u64, 210u64), 101);
|
||||
|
||||
// when l2 digests are created
|
||||
assert_eq!(oldest_non_pruned_trie(&config(100, 2), 100u64, 50u64), 1);
|
||||
assert_eq!(oldest_non_pruned_trie(&config(100, 2), 100u64, 110u64), 1);
|
||||
assert_eq!(oldest_non_pruned_trie(&config(100, 2), 100u64, 210u64), 1);
|
||||
assert_eq!(oldest_non_pruned_trie(&config(100, 2), 100u64, 10110u64), 1);
|
||||
assert_eq!(oldest_non_pruned_trie(&config(100, 2), 100u64, 20110u64), 10001);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
use crate::{
|
||||
backend::Backend, OverlayedChanges, StorageTransactionCache,
|
||||
changes_trie::Storage as ChangesTrieStorage,
|
||||
changes_trie::State as ChangesTrieState,
|
||||
};
|
||||
|
||||
use hash_db::Hasher;
|
||||
@@ -65,7 +65,7 @@ impl<B: error::Error, E: error::Error> error::Error for Error<B, E> {
|
||||
}
|
||||
|
||||
/// Wraps a read-only backend, call executor, and current overlayed changes.
|
||||
pub struct Ext<'a, H, N, B, T>
|
||||
pub struct Ext<'a, H, N, B>
|
||||
where
|
||||
H: Hasher,
|
||||
B: 'a + Backend<H>,
|
||||
@@ -77,8 +77,8 @@ pub struct Ext<'a, H, N, B, T>
|
||||
backend: &'a B,
|
||||
/// The cache for the storage transactions.
|
||||
storage_transaction_cache: &'a mut StorageTransactionCache<B::Transaction, H, N>,
|
||||
/// Changes trie storage to read from.
|
||||
changes_trie_storage: Option<&'a T>,
|
||||
/// Changes trie state to read from.
|
||||
changes_trie_state: Option<ChangesTrieState<'a, H, N>>,
|
||||
/// Pseudo-unique id used for tracing.
|
||||
pub id: u16,
|
||||
/// Dummy usage of N arg.
|
||||
@@ -87,12 +87,11 @@ pub struct Ext<'a, H, N, B, T>
|
||||
extensions: Option<&'a mut Extensions>,
|
||||
}
|
||||
|
||||
impl<'a, H, N, B, T> Ext<'a, H, N, B, T>
|
||||
impl<'a, H, N, B> Ext<'a, H, N, B>
|
||||
where
|
||||
H: Hasher,
|
||||
H::Out: Ord + 'static + codec::Codec,
|
||||
B: 'a + Backend<H>,
|
||||
T: 'a + ChangesTrieStorage<H, N>,
|
||||
N: crate::changes_trie::BlockNumber,
|
||||
{
|
||||
|
||||
@@ -101,13 +100,13 @@ where
|
||||
overlay: &'a mut OverlayedChanges,
|
||||
storage_transaction_cache: &'a mut StorageTransactionCache<B::Transaction, H, N>,
|
||||
backend: &'a B,
|
||||
changes_trie_storage: Option<&'a T>,
|
||||
changes_trie_state: Option<ChangesTrieState<'a, H, N>>,
|
||||
extensions: Option<&'a mut Extensions>,
|
||||
) -> Self {
|
||||
Ext {
|
||||
overlay,
|
||||
backend,
|
||||
changes_trie_storage,
|
||||
changes_trie_state,
|
||||
storage_transaction_cache,
|
||||
id: rand::random(),
|
||||
_phantom: Default::default(),
|
||||
@@ -124,12 +123,11 @@ where
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl<'a, H, N, B, T> Ext<'a, H, N, B, T>
|
||||
impl<'a, H, N, B> Ext<'a, H, N, B>
|
||||
where
|
||||
H: Hasher,
|
||||
H::Out: Ord + 'static,
|
||||
B: 'a + Backend<H>,
|
||||
T: 'a + ChangesTrieStorage<H, N>,
|
||||
N: crate::changes_trie::BlockNumber,
|
||||
{
|
||||
pub fn storage_pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
|
||||
@@ -146,12 +144,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H, B, T, N> Externalities for Ext<'a, H, N, B, T>
|
||||
impl<'a, H, B, N> Externalities for Ext<'a, H, N, B>
|
||||
where
|
||||
H: Hasher,
|
||||
H::Out: Ord + 'static + codec::Codec,
|
||||
B: 'a + Backend<H>,
|
||||
T: 'a + ChangesTrieStorage<H, N>,
|
||||
N: crate::changes_trie::BlockNumber,
|
||||
{
|
||||
fn storage(&self, key: &[u8]) -> Option<Vec<u8>> {
|
||||
@@ -564,7 +561,7 @@ where
|
||||
let _guard = sp_panic_handler::AbortGuard::force_abort();
|
||||
let root = self.overlay.changes_trie_root(
|
||||
self.backend,
|
||||
self.changes_trie_storage.clone(),
|
||||
self.changes_trie_state.as_ref(),
|
||||
Decode::decode(&mut &parent_hash[..]).map_err(|e|
|
||||
trace!(
|
||||
target: "state-trace",
|
||||
@@ -586,11 +583,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, H, B, T, N> sp_externalities::ExtensionStore for Ext<'a, H, N, B, T>
|
||||
impl<'a, H, B, N> sp_externalities::ExtensionStore for Ext<'a, H, N, B>
|
||||
where
|
||||
H: Hasher,
|
||||
B: 'a + Backend<H>,
|
||||
T: 'a + ChangesTrieStorage<H, N>,
|
||||
N: crate::changes_trie::BlockNumber,
|
||||
{
|
||||
fn extension_by_type_id(&mut self, type_id: TypeId) -> Option<&mut dyn Any> {
|
||||
@@ -602,19 +598,19 @@ where
|
||||
mod tests {
|
||||
use super::*;
|
||||
use hex_literal::hex;
|
||||
use num_traits::Zero;
|
||||
use codec::Encode;
|
||||
use sp_core::{H256, Blake2Hasher, storage::well_known_keys::EXTRINSIC_INDEX, map};
|
||||
use crate::{
|
||||
changes_trie::{
|
||||
Configuration as ChangesTrieConfiguration,
|
||||
InMemoryStorage as InMemoryChangesTrieStorage,
|
||||
InMemoryStorage as TestChangesTrieStorage,
|
||||
}, InMemoryBackend, overlayed_changes::OverlayedValue,
|
||||
};
|
||||
use sp_core::storage::{Storage, StorageChild};
|
||||
|
||||
type TestBackend = InMemoryBackend<Blake2Hasher>;
|
||||
type TestChangesTrieStorage = InMemoryChangesTrieStorage<Blake2Hasher, u64>;
|
||||
type TestExt<'a> = Ext<'a, Blake2Hasher, u64, TestBackend, TestChangesTrieStorage>;
|
||||
type TestExt<'a> = Ext<'a, Blake2Hasher, u64, TestBackend>;
|
||||
|
||||
fn prepare_overlay_with_changes() -> OverlayedChanges {
|
||||
OverlayedChanges {
|
||||
@@ -629,10 +625,14 @@ mod tests {
|
||||
}),
|
||||
].into_iter().collect(),
|
||||
committed: Default::default(),
|
||||
changes_trie_config: Some(ChangesTrieConfiguration {
|
||||
digest_interval: 0,
|
||||
digest_levels: 0,
|
||||
}),
|
||||
collect_extrinsics: true,
|
||||
}
|
||||
}
|
||||
|
||||
fn changes_trie_config() -> ChangesTrieConfiguration {
|
||||
ChangesTrieConfiguration {
|
||||
digest_interval: 0,
|
||||
digest_levels: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -646,13 +646,11 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn storage_changes_root_is_none_when_extrinsic_changes_are_none() {
|
||||
fn storage_changes_root_is_none_when_state_is_not_provided() {
|
||||
let mut overlay = prepare_overlay_with_changes();
|
||||
let mut cache = StorageTransactionCache::default();
|
||||
overlay.changes_trie_config = None;
|
||||
let storage = TestChangesTrieStorage::with_blocks(vec![(100, Default::default())]);
|
||||
let backend = TestBackend::default();
|
||||
let mut ext = TestExt::new(&mut overlay, &mut cache, &backend, Some(&storage), None);
|
||||
let mut ext = TestExt::new(&mut overlay, &mut cache, &backend, None, None);
|
||||
assert_eq!(ext.storage_changes_root(&H256::default().encode()).unwrap(), None);
|
||||
}
|
||||
|
||||
@@ -661,8 +659,9 @@ mod tests {
|
||||
let mut overlay = prepare_overlay_with_changes();
|
||||
let mut cache = StorageTransactionCache::default();
|
||||
let storage = TestChangesTrieStorage::with_blocks(vec![(99, Default::default())]);
|
||||
let state = Some(ChangesTrieState::new(changes_trie_config(), Zero::zero(), &storage));
|
||||
let backend = TestBackend::default();
|
||||
let mut ext = TestExt::new(&mut overlay, &mut cache, &backend, Some(&storage), None);
|
||||
let mut ext = TestExt::new(&mut overlay, &mut cache, &backend, state, None);
|
||||
assert_eq!(
|
||||
ext.storage_changes_root(&H256::default().encode()).unwrap(),
|
||||
Some(hex!("bb0c2ef6e1d36d5490f9766cfcc7dfe2a6ca804504c3bb206053890d6dd02376").to_vec()),
|
||||
@@ -675,8 +674,9 @@ mod tests {
|
||||
let mut cache = StorageTransactionCache::default();
|
||||
overlay.prospective.top.get_mut(&vec![1]).unwrap().value = None;
|
||||
let storage = TestChangesTrieStorage::with_blocks(vec![(99, Default::default())]);
|
||||
let state = Some(ChangesTrieState::new(changes_trie_config(), Zero::zero(), &storage));
|
||||
let backend = TestBackend::default();
|
||||
let mut ext = TestExt::new(&mut overlay, &mut cache, &backend, Some(&storage), None);
|
||||
let mut ext = TestExt::new(&mut overlay, &mut cache, &backend, state, None);
|
||||
assert_eq!(
|
||||
ext.storage_changes_root(&H256::default().encode()).unwrap(),
|
||||
Some(hex!("96f5aae4690e7302737b6f9b7f8567d5bbb9eac1c315f80101235a92d9ec27f4").to_vec()),
|
||||
|
||||
@@ -23,8 +23,8 @@ use log::{warn, trace};
|
||||
use hash_db::Hasher;
|
||||
use codec::{Decode, Encode, Codec};
|
||||
use sp_core::{
|
||||
storage::{well_known_keys, ChildInfo}, NativeOrEncoded, NeverNativeValue,
|
||||
traits::{CodeExecutor, CallInWasmExt}, hexdisplay::HexDisplay
|
||||
storage::ChildInfo, NativeOrEncoded, NeverNativeValue,
|
||||
traits::{CodeExecutor, CallInWasmExt}, hexdisplay::HexDisplay,
|
||||
};
|
||||
use overlayed_changes::OverlayedChangeSet;
|
||||
use sp_externalities::Extensions;
|
||||
@@ -49,15 +49,17 @@ pub use ext::Ext;
|
||||
pub use backend::Backend;
|
||||
pub use changes_trie::{
|
||||
AnchorBlockId as ChangesTrieAnchorBlockId,
|
||||
State as ChangesTrieState,
|
||||
Storage as ChangesTrieStorage,
|
||||
RootsStorage as ChangesTrieRootsStorage,
|
||||
InMemoryStorage as InMemoryChangesTrieStorage,
|
||||
BuildCache as ChangesTrieBuildCache,
|
||||
CacheAction as ChangesTrieCacheAction,
|
||||
ConfigurationRange as ChangesTrieConfigurationRange,
|
||||
key_changes, key_changes_proof, key_changes_proof_check,
|
||||
key_changes, key_changes_proof,
|
||||
key_changes_proof_check, key_changes_proof_check_with_db,
|
||||
prune as prune_changes_tries,
|
||||
oldest_non_pruned_trie as oldest_non_pruned_changes_trie,
|
||||
disabled_state as disabled_changes_trie_state,
|
||||
BlockNumber as ChangesTrieBlockNumber,
|
||||
};
|
||||
pub use overlayed_changes::{OverlayedChanges, StorageChanges, StorageTransactionCache};
|
||||
@@ -171,7 +173,7 @@ fn always_untrusted_wasm<E, R: Decode>() -> ExecutionManager<DefaultHandler<R, E
|
||||
}
|
||||
|
||||
/// The substrate state machine.
|
||||
pub struct StateMachine<'a, B, H, N, T, Exec>
|
||||
pub struct StateMachine<'a, B, H, N, Exec>
|
||||
where
|
||||
H: Hasher,
|
||||
B: Backend<H>,
|
||||
@@ -183,23 +185,22 @@ pub struct StateMachine<'a, B, H, N, T, Exec>
|
||||
call_data: &'a [u8],
|
||||
overlay: &'a mut OverlayedChanges,
|
||||
extensions: Extensions,
|
||||
changes_trie_storage: Option<&'a T>,
|
||||
changes_trie_state: Option<ChangesTrieState<'a, H, N>>,
|
||||
_marker: PhantomData<(H, N)>,
|
||||
storage_transaction_cache: Option<&'a mut StorageTransactionCache<B::Transaction, H, N>>,
|
||||
}
|
||||
|
||||
impl<'a, B, H, N, T, Exec> StateMachine<'a, B, H, N, T, Exec> where
|
||||
impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where
|
||||
H: Hasher,
|
||||
H::Out: Ord + 'static + codec::Codec,
|
||||
Exec: CodeExecutor + Clone + 'static,
|
||||
B: Backend<H>,
|
||||
T: ChangesTrieStorage<H, N>,
|
||||
N: crate::changes_trie::BlockNumber,
|
||||
{
|
||||
/// Creates new substrate state machine.
|
||||
pub fn new(
|
||||
backend: &'a B,
|
||||
changes_trie_storage: Option<&'a T>,
|
||||
changes_trie_state: Option<ChangesTrieState<'a, H, N>>,
|
||||
overlay: &'a mut OverlayedChanges,
|
||||
exec: &'a Exec,
|
||||
method: &'a str,
|
||||
@@ -215,7 +216,7 @@ impl<'a, B, H, N, T, Exec> StateMachine<'a, B, H, N, T, Exec> where
|
||||
call_data,
|
||||
extensions,
|
||||
overlay,
|
||||
changes_trie_storage,
|
||||
changes_trie_state,
|
||||
_marker: PhantomData,
|
||||
storage_transaction_cache: None,
|
||||
}
|
||||
@@ -273,7 +274,7 @@ impl<'a, B, H, N, T, Exec> StateMachine<'a, B, H, N, T, Exec> where
|
||||
self.overlay,
|
||||
cache,
|
||||
self.backend,
|
||||
self.changes_trie_storage.clone(),
|
||||
self.changes_trie_state.clone(),
|
||||
Some(&mut self.extensions),
|
||||
);
|
||||
|
||||
@@ -388,20 +389,8 @@ impl<'a, B, H, N, T, Exec> StateMachine<'a, B, H, N, T, Exec> where
|
||||
CallResult<R, Exec::Error>,
|
||||
) -> CallResult<R, Exec::Error>
|
||||
{
|
||||
// read changes trie configuration. The reason why we're doing it here instead of the
|
||||
// `OverlayedChanges` constructor is that we need proofs for this read as a part of
|
||||
// proof-of-execution on light clients. And the proof is recorded by the backend which
|
||||
// is created after OverlayedChanges
|
||||
|
||||
let init_overlay = |overlay: &mut OverlayedChanges, final_check: bool, backend: &B| {
|
||||
let changes_trie_config = try_read_overlay_value(
|
||||
overlay,
|
||||
backend,
|
||||
well_known_keys::CHANGES_TRIE_CONFIG
|
||||
)?;
|
||||
set_changes_trie_config(overlay, changes_trie_config, final_check)
|
||||
};
|
||||
init_overlay(self.overlay, false, &self.backend)?;
|
||||
let changes_tries_enabled = self.changes_trie_state.is_some();
|
||||
self.overlay.set_collect_extrinsics(changes_tries_enabled);
|
||||
|
||||
let result = {
|
||||
let orig_prospective = self.overlay.prospective.clone();
|
||||
@@ -433,16 +422,12 @@ impl<'a, B, H, N, T, Exec> StateMachine<'a, B, H, N, T, Exec> where
|
||||
}
|
||||
};
|
||||
|
||||
if result.is_ok() {
|
||||
init_overlay(self.overlay, true, self.backend)?;
|
||||
}
|
||||
|
||||
result.map_err(|e| Box::new(e) as _)
|
||||
}
|
||||
}
|
||||
|
||||
/// Prove execution using the given state backend, overlayed changes, and call executor.
|
||||
pub fn prove_execution<B, H, Exec>(
|
||||
pub fn prove_execution<B, H, N, Exec>(
|
||||
mut backend: B,
|
||||
overlay: &mut OverlayedChanges,
|
||||
exec: &Exec,
|
||||
@@ -454,10 +439,11 @@ where
|
||||
H: Hasher,
|
||||
H::Out: Ord + 'static + codec::Codec,
|
||||
Exec: CodeExecutor + Clone + 'static,
|
||||
N: crate::changes_trie::BlockNumber,
|
||||
{
|
||||
let trie_backend = backend.as_trie_backend()
|
||||
.ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box<dyn Error>)?;
|
||||
prove_execution_on_trie_backend(trie_backend, overlay, exec, method, call_data)
|
||||
prove_execution_on_trie_backend::<_, _, N, _>(trie_backend, overlay, exec, method, call_data)
|
||||
}
|
||||
|
||||
/// Prove execution using the given trie backend, overlayed changes, and call executor.
|
||||
@@ -469,7 +455,7 @@ where
|
||||
///
|
||||
/// Note: changes to code will be in place if this call is made again. For running partial
|
||||
/// blocks (e.g. a transaction at a time), ensure a different method is used.
|
||||
pub fn prove_execution_on_trie_backend<S, H, Exec>(
|
||||
pub fn prove_execution_on_trie_backend<S, H, N, Exec>(
|
||||
trie_backend: &TrieBackend<S, H>,
|
||||
overlay: &mut OverlayedChanges,
|
||||
exec: &Exec,
|
||||
@@ -481,9 +467,10 @@ where
|
||||
H: Hasher,
|
||||
H::Out: Ord + 'static + codec::Codec,
|
||||
Exec: CodeExecutor + 'static + Clone,
|
||||
N: crate::changes_trie::BlockNumber,
|
||||
{
|
||||
let proving_backend = proving_backend::ProvingBackend::new(trie_backend);
|
||||
let mut sm = StateMachine::<_, H, _, InMemoryChangesTrieStorage<H, u64>, Exec>::new(
|
||||
let mut sm = StateMachine::<_, H, N, Exec>::new(
|
||||
&proving_backend, None, overlay, exec, method, call_data, Extensions::default(),
|
||||
);
|
||||
|
||||
@@ -496,7 +483,7 @@ where
|
||||
}
|
||||
|
||||
/// Check execution proof, generated by `prove_execution` call.
|
||||
pub fn execution_proof_check<H, Exec>(
|
||||
pub fn execution_proof_check<H, N, Exec>(
|
||||
root: H::Out,
|
||||
proof: StorageProof,
|
||||
overlay: &mut OverlayedChanges,
|
||||
@@ -508,13 +495,14 @@ where
|
||||
H: Hasher,
|
||||
Exec: CodeExecutor + Clone + 'static,
|
||||
H::Out: Ord + 'static + codec::Codec,
|
||||
N: crate::changes_trie::BlockNumber,
|
||||
{
|
||||
let trie_backend = create_proof_check_backend::<H>(root.into(), proof)?;
|
||||
execution_proof_check_on_trie_backend(&trie_backend, overlay, exec, method, call_data)
|
||||
execution_proof_check_on_trie_backend::<_, N, _>(&trie_backend, overlay, exec, method, call_data)
|
||||
}
|
||||
|
||||
/// Check execution proof on proving backend, generated by `prove_execution` call.
|
||||
pub fn execution_proof_check_on_trie_backend<H, Exec>(
|
||||
pub fn execution_proof_check_on_trie_backend<H, N, Exec>(
|
||||
trie_backend: &TrieBackend<MemoryDB<H>, H>,
|
||||
overlay: &mut OverlayedChanges,
|
||||
exec: &Exec,
|
||||
@@ -525,8 +513,9 @@ where
|
||||
H: Hasher,
|
||||
H::Out: Ord + 'static + codec::Codec,
|
||||
Exec: CodeExecutor + Clone + 'static,
|
||||
N: crate::changes_trie::BlockNumber,
|
||||
{
|
||||
let mut sm = StateMachine::<_, H, _, InMemoryChangesTrieStorage<H, u64>, Exec>::new(
|
||||
let mut sm = StateMachine::<_, H, N, Exec>::new(
|
||||
trie_backend, None, overlay, exec, method, call_data, Extensions::default(),
|
||||
);
|
||||
|
||||
@@ -692,44 +681,6 @@ where
|
||||
.map_err(|e| Box::new(e) as Box<dyn Error>)
|
||||
}
|
||||
|
||||
/// Sets overlayed changes' changes trie configuration. Returns error if configuration
|
||||
/// differs from previous OR config decode has failed.
|
||||
fn set_changes_trie_config(
|
||||
overlay: &mut OverlayedChanges,
|
||||
config: Option<Vec<u8>>,
|
||||
final_check: bool,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let config = match config {
|
||||
Some(v) => Some(Decode::decode(&mut &v[..])
|
||||
.map_err(|_| Box::new("Failed to decode changes trie configuration".to_owned()) as Box<dyn Error>)?),
|
||||
None => None,
|
||||
};
|
||||
|
||||
if final_check && overlay.changes_trie_config.is_some() != config.is_some() {
|
||||
return Err(Box::new("Changes trie configuration change is not supported".to_owned()));
|
||||
}
|
||||
|
||||
if let Some(config) = config {
|
||||
if !overlay.set_changes_trie_config(config) {
|
||||
return Err(Box::new("Changes trie configuration change is not supported".to_owned()));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reads storage value from overlay or from the backend.
|
||||
fn try_read_overlay_value<H, B>(
|
||||
overlay: &OverlayedChanges,
|
||||
backend: &B, key: &[u8],
|
||||
) -> Result<Option<Vec<u8>>, Box<dyn Error>> where H: Hasher, B: Backend<H> {
|
||||
match overlay.storage(key).map(|x| x.map(|x| x.to_vec())) {
|
||||
Some(value) => Ok(value),
|
||||
None => backend
|
||||
.storage(key)
|
||||
.map_err(|err| Box::new(ExecutionError::Backend(format!("{}", err))) as Box<dyn Error>),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::BTreeMap;
|
||||
@@ -737,10 +688,7 @@ mod tests {
|
||||
use overlayed_changes::OverlayedValue;
|
||||
use super::*;
|
||||
use super::ext::Ext;
|
||||
use super::changes_trie::{
|
||||
InMemoryStorage as InMemoryChangesTrieStorage,
|
||||
Configuration as ChangesTrieConfig,
|
||||
};
|
||||
use super::changes_trie::Configuration as ChangesTrieConfig;
|
||||
use sp_core::{Blake2Hasher, map, traits::Externalities, storage::ChildStorageKey};
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -770,7 +718,7 @@ mod tests {
|
||||
) -> (CallResult<R, Self::Error>, bool) {
|
||||
if self.change_changes_trie_config {
|
||||
ext.place_storage(
|
||||
well_known_keys::CHANGES_TRIE_CONFIG.to_vec(),
|
||||
sp_core::storage::well_known_keys::CHANGES_TRIE_CONFIG.to_vec(),
|
||||
Some(
|
||||
ChangesTrieConfig {
|
||||
digest_interval: 777,
|
||||
@@ -816,11 +764,10 @@ mod tests {
|
||||
fn execute_works() {
|
||||
let backend = trie_backend::tests::test_trie();
|
||||
let mut overlayed_changes = Default::default();
|
||||
let changes_trie_storage = InMemoryChangesTrieStorage::<Blake2Hasher, u64>::new();
|
||||
|
||||
let mut state_machine = StateMachine::new(
|
||||
&backend,
|
||||
Some(&changes_trie_storage),
|
||||
changes_trie::disabled_state::<_, u64>(),
|
||||
&mut overlayed_changes,
|
||||
&DummyCodeExecutor {
|
||||
change_changes_trie_config: false,
|
||||
@@ -844,11 +791,10 @@ mod tests {
|
||||
fn execute_works_with_native_else_wasm() {
|
||||
let backend = trie_backend::tests::test_trie();
|
||||
let mut overlayed_changes = Default::default();
|
||||
let changes_trie_storage = InMemoryChangesTrieStorage::<Blake2Hasher, u64>::new();
|
||||
|
||||
let mut state_machine = StateMachine::new(
|
||||
&backend,
|
||||
Some(&changes_trie_storage),
|
||||
changes_trie::disabled_state::<_, u64>(),
|
||||
&mut overlayed_changes,
|
||||
&DummyCodeExecutor {
|
||||
change_changes_trie_config: false,
|
||||
@@ -869,11 +815,10 @@ mod tests {
|
||||
let mut consensus_failed = false;
|
||||
let backend = trie_backend::tests::test_trie();
|
||||
let mut overlayed_changes = Default::default();
|
||||
let changes_trie_storage = InMemoryChangesTrieStorage::<Blake2Hasher, u64>::new();
|
||||
|
||||
let mut state_machine = StateMachine::new(
|
||||
&backend,
|
||||
Some(&changes_trie_storage),
|
||||
changes_trie::disabled_state::<_, u64>(),
|
||||
&mut overlayed_changes,
|
||||
&DummyCodeExecutor {
|
||||
change_changes_trie_config: false,
|
||||
@@ -910,7 +855,7 @@ mod tests {
|
||||
// fetch execution proof from 'remote' full node
|
||||
let remote_backend = trie_backend::tests::test_trie();
|
||||
let remote_root = remote_backend.storage_root(std::iter::empty()).0;
|
||||
let (remote_result, remote_proof) = prove_execution(
|
||||
let (remote_result, remote_proof) = prove_execution::<_, _, u64, _>(
|
||||
remote_backend,
|
||||
&mut Default::default(),
|
||||
&executor,
|
||||
@@ -919,7 +864,7 @@ mod tests {
|
||||
).unwrap();
|
||||
|
||||
// check proof locally
|
||||
let local_result = execution_proof_check::<Blake2Hasher, _>(
|
||||
let local_result = execution_proof_check::<Blake2Hasher, u64, _>(
|
||||
remote_root,
|
||||
remote_proof,
|
||||
&mut Default::default(),
|
||||
@@ -956,13 +901,12 @@ mod tests {
|
||||
};
|
||||
|
||||
{
|
||||
let changes_trie_storage = InMemoryChangesTrieStorage::<Blake2Hasher, u64>::new();
|
||||
let mut cache = StorageTransactionCache::default();
|
||||
let mut ext = Ext::new(
|
||||
&mut overlay,
|
||||
&mut cache,
|
||||
backend,
|
||||
Some(&changes_trie_storage),
|
||||
changes_trie::disabled_state::<_, u64>(),
|
||||
None,
|
||||
);
|
||||
ext.clear_prefix(b"ab");
|
||||
@@ -987,14 +931,13 @@ mod tests {
|
||||
fn set_child_storage_works() {
|
||||
let mut state = InMemoryBackend::<Blake2Hasher>::default();
|
||||
let backend = state.as_trie_backend().unwrap();
|
||||
let changes_trie_storage = InMemoryChangesTrieStorage::<Blake2Hasher, u64>::new();
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
let mut cache = StorageTransactionCache::default();
|
||||
let mut ext = Ext::new(
|
||||
&mut overlay,
|
||||
&mut cache,
|
||||
backend,
|
||||
Some(&changes_trie_storage),
|
||||
changes_trie::disabled_state::<_, u64>(),
|
||||
None,
|
||||
);
|
||||
|
||||
@@ -1080,30 +1023,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cannot_change_changes_trie_config() {
|
||||
let backend = trie_backend::tests::test_trie();
|
||||
let mut overlayed_changes = Default::default();
|
||||
let changes_trie_storage = InMemoryChangesTrieStorage::<Blake2Hasher, u64>::new();
|
||||
|
||||
let mut state_machine = StateMachine::new(
|
||||
&backend,
|
||||
Some(&changes_trie_storage),
|
||||
&mut overlayed_changes,
|
||||
&DummyCodeExecutor {
|
||||
change_changes_trie_config: true,
|
||||
native_available: false,
|
||||
native_succeeds: true,
|
||||
fallback_succeeds: true,
|
||||
},
|
||||
"test",
|
||||
&[],
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
assert!(state_machine.execute(ExecutionStrategy::NativeWhenPossible).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn child_storage_uuid() {
|
||||
const CHILD_INFO_1: ChildInfo<'static> = ChildInfo::new_default(b"unique_id_1");
|
||||
@@ -1115,13 +1034,12 @@ mod tests {
|
||||
let subtrie2 = ChildStorageKey::from_slice(b":child_storage:default:sub_test2").unwrap();
|
||||
let mut transaction = {
|
||||
let backend = test_trie();
|
||||
let changes_trie_storage = InMemoryChangesTrieStorage::<Blake2Hasher, u64>::new();
|
||||
let mut cache = StorageTransactionCache::default();
|
||||
let mut ext = Ext::new(
|
||||
&mut overlay,
|
||||
&mut cache,
|
||||
&backend,
|
||||
Some(&changes_trie_storage),
|
||||
changes_trie::disabled_state::<_, u64>(),
|
||||
None,
|
||||
);
|
||||
ext.set_child_storage(subtrie1, CHILD_INFO_1, b"abc".to_vec(), b"def".to_vec());
|
||||
@@ -1139,28 +1057,4 @@ mod tests {
|
||||
}
|
||||
assert!(!duplicate);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cannot_change_changes_trie_config_with_native_else_wasm() {
|
||||
let backend = trie_backend::tests::test_trie();
|
||||
let mut overlayed_changes = Default::default();
|
||||
let changes_trie_storage = InMemoryChangesTrieStorage::<Blake2Hasher, u64>::new();
|
||||
|
||||
let mut state_machine = StateMachine::new(
|
||||
&backend,
|
||||
Some(&changes_trie_storage),
|
||||
&mut overlayed_changes,
|
||||
&DummyCodeExecutor {
|
||||
change_changes_trie_config: true,
|
||||
native_available: false,
|
||||
native_succeeds: true,
|
||||
fallback_succeeds: true,
|
||||
},
|
||||
"test",
|
||||
&[],
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
assert!(state_machine.execute(ExecutionStrategy::NativeElseWasm).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
use crate::{
|
||||
backend::Backend, ChangesTrieTransaction,
|
||||
changes_trie::{
|
||||
NO_EXTRINSIC_INDEX, Configuration as ChangesTrieConfig, BlockNumber, build_changes_trie,
|
||||
Storage as ChangesTrieStorage,
|
||||
NO_EXTRINSIC_INDEX, BlockNumber, build_changes_trie,
|
||||
State as ChangesTrieState,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -43,9 +43,8 @@ pub struct OverlayedChanges {
|
||||
pub(crate) prospective: OverlayedChangeSet,
|
||||
/// Committed changes.
|
||||
pub(crate) committed: OverlayedChangeSet,
|
||||
/// Changes trie configuration. None by default, but could be installed by the
|
||||
/// runtime if it supports change tries.
|
||||
pub(crate) changes_trie_config: Option<ChangesTrieConfig>,
|
||||
/// True if extrinsiscs stats must be collected.
|
||||
pub(crate) collect_extrinsics: bool,
|
||||
}
|
||||
|
||||
/// The storage value, used inside OverlayedChanges.
|
||||
@@ -184,20 +183,9 @@ impl OverlayedChanges {
|
||||
self.prospective.is_empty() && self.committed.is_empty()
|
||||
}
|
||||
|
||||
/// Sets the changes trie configuration.
|
||||
///
|
||||
/// Returns false if configuration has been set already and we now trying
|
||||
/// to install different configuration. This isn't supported now.
|
||||
pub(crate) fn set_changes_trie_config(&mut self, config: ChangesTrieConfig) -> bool {
|
||||
if let Some(ref old_config) = self.changes_trie_config {
|
||||
// we do not support changes trie configuration' change now
|
||||
if *old_config != config {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
self.changes_trie_config = Some(config);
|
||||
true
|
||||
/// Ask to collect/not to collect extrinsics indices where key(s) has been changed.
|
||||
pub fn set_collect_extrinsics(&mut self, collect_extrinsics: bool) {
|
||||
self.collect_extrinsics = collect_extrinsics;
|
||||
}
|
||||
|
||||
/// Returns a double-Option: None if the key is unknown (i.e. and the query should be referred
|
||||
@@ -442,11 +430,11 @@ impl OverlayedChanges {
|
||||
|
||||
/// Convert this instance with all changes into a [`StorageChanges`] instance.
|
||||
pub fn into_storage_changes<
|
||||
B: Backend<H>, H: Hasher, N: BlockNumber, T: ChangesTrieStorage<H, N>
|
||||
B: Backend<H>, H: Hasher, N: BlockNumber
|
||||
>(
|
||||
self,
|
||||
backend: &B,
|
||||
changes_trie_storage: Option<&T>,
|
||||
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 {
|
||||
@@ -463,7 +451,7 @@ impl OverlayedChanges {
|
||||
if cache.changes_trie_transaction.is_none() {
|
||||
self.changes_trie_root(
|
||||
backend,
|
||||
changes_trie_storage,
|
||||
changes_trie_state,
|
||||
parent_hash,
|
||||
false,
|
||||
&mut cache,
|
||||
@@ -501,7 +489,7 @@ impl OverlayedChanges {
|
||||
/// Changes that are made outside of extrinsics, are marked with
|
||||
/// `NO_EXTRINSIC_INDEX` index.
|
||||
fn extrinsic_index(&self) -> Option<u32> {
|
||||
match self.changes_trie_config.is_some() {
|
||||
match self.collect_extrinsics {
|
||||
true => Some(
|
||||
self.storage(EXTRINSIC_INDEX)
|
||||
.and_then(|idx| idx.and_then(|idx| Decode::decode(&mut &*idx).ok()))
|
||||
@@ -557,17 +545,17 @@ impl OverlayedChanges {
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics on storage error, when `panic_on_storage_error` is set.
|
||||
pub fn changes_trie_root<H: Hasher, N: BlockNumber, B: Backend<H>, T: ChangesTrieStorage<H, N>>(
|
||||
pub fn changes_trie_root<'a, H: Hasher, N: BlockNumber, B: Backend<H>>(
|
||||
&self,
|
||||
backend: &B,
|
||||
changes_trie_storage: Option<&T>,
|
||||
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::<_, T, H, N>(
|
||||
build_changes_trie::<_, H, N>(
|
||||
backend,
|
||||
changes_trie_storage,
|
||||
changes_trie_state,
|
||||
self,
|
||||
parent_hash,
|
||||
panic_on_storage_error,
|
||||
@@ -656,7 +644,6 @@ mod tests {
|
||||
Blake2Hasher, traits::Externalities, storage::well_known_keys::EXTRINSIC_INDEX,
|
||||
};
|
||||
use crate::InMemoryBackend;
|
||||
use crate::changes_trie::InMemoryStorage as InMemoryChangesTrieStorage;
|
||||
use crate::ext::Ext;
|
||||
use super::*;
|
||||
|
||||
@@ -718,13 +705,12 @@ mod tests {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let changes_trie_storage = InMemoryChangesTrieStorage::<Blake2Hasher, u64>::new();
|
||||
let mut cache = StorageTransactionCache::default();
|
||||
let mut ext = Ext::new(
|
||||
&mut overlay,
|
||||
&mut cache,
|
||||
&backend,
|
||||
Some(&changes_trie_storage),
|
||||
crate::changes_trie::disabled_state::<_, u64>(),
|
||||
None,
|
||||
);
|
||||
const ROOT: [u8; 32] = hex!("39245109cef3758c2eed2ccba8d9b370a917850af3824bc8348d505df2c298fa");
|
||||
@@ -732,57 +718,10 @@ mod tests {
|
||||
assert_eq!(&ext.storage_root()[..], &ROOT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn changes_trie_configuration_is_saved() {
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
assert!(overlay.changes_trie_config.is_none());
|
||||
assert_eq!(
|
||||
overlay.set_changes_trie_config(
|
||||
ChangesTrieConfig { digest_interval: 4, digest_levels: 1, },
|
||||
),
|
||||
true,
|
||||
);
|
||||
assert!(overlay.changes_trie_config.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn changes_trie_configuration_is_saved_twice() {
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
assert!(overlay.changes_trie_config.is_none());
|
||||
assert_eq!(overlay.set_changes_trie_config(ChangesTrieConfig {
|
||||
digest_interval: 4, digest_levels: 1,
|
||||
}), true);
|
||||
overlay.set_extrinsic_index(0);
|
||||
overlay.set_storage(vec![1], Some(vec![2]));
|
||||
assert_eq!(overlay.set_changes_trie_config(ChangesTrieConfig {
|
||||
digest_interval: 4, digest_levels: 1,
|
||||
}), true);
|
||||
assert_eq!(
|
||||
strip_extrinsic_index(&overlay.prospective.top),
|
||||
vec![
|
||||
(vec![1], OverlayedValue { value: Some(vec![2]),
|
||||
extrinsics: Some(vec![0].into_iter().collect()) }),
|
||||
].into_iter().collect(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panics_when_trying_to_save_different_changes_trie_configuration() {
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
assert_eq!(overlay.set_changes_trie_config(ChangesTrieConfig {
|
||||
digest_interval: 4, digest_levels: 1,
|
||||
}), true);
|
||||
assert_eq!(overlay.set_changes_trie_config(ChangesTrieConfig {
|
||||
digest_interval: 2, digest_levels: 1,
|
||||
}), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extrinsic_changes_are_collected() {
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
let _ = overlay.set_changes_trie_config(ChangesTrieConfig {
|
||||
digest_interval: 4, digest_levels: 1,
|
||||
});
|
||||
overlay.set_collect_extrinsics(true);
|
||||
|
||||
overlay.set_storage(vec![100], Some(vec![101]));
|
||||
|
||||
|
||||
@@ -17,12 +17,15 @@
|
||||
//! Test implementation for Externalities.
|
||||
|
||||
use std::any::{Any, TypeId};
|
||||
use codec::Decode;
|
||||
use hash_db::Hasher;
|
||||
use crate::{
|
||||
backend::Backend, OverlayedChanges, StorageTransactionCache, ext::Ext, InMemoryBackend,
|
||||
changes_trie::{
|
||||
Configuration as ChangesTrieConfiguration,
|
||||
InMemoryStorage as ChangesTrieInMemoryStorage,
|
||||
BlockNumber as ChangesTrieBlockNumber,
|
||||
State as ChangesTrieState,
|
||||
},
|
||||
};
|
||||
use sp_core::{
|
||||
@@ -45,6 +48,7 @@ where
|
||||
<InMemoryBackend<H> as Backend<H>>::Transaction, H, N
|
||||
>,
|
||||
backend: InMemoryBackend<H>,
|
||||
changes_trie_config: Option<ChangesTrieConfiguration>,
|
||||
changes_trie_storage: ChangesTrieInMemoryStorage<H, N>,
|
||||
extensions: Extensions,
|
||||
}
|
||||
@@ -54,12 +58,19 @@ impl<H: Hasher, N: ChangesTrieBlockNumber> TestExternalities<H, N>
|
||||
H::Out: Ord + 'static + codec::Codec
|
||||
{
|
||||
/// Get externalities implementation.
|
||||
pub fn ext(&mut self) -> Ext<H, N, InMemoryBackend<H>, ChangesTrieInMemoryStorage<H, N>> {
|
||||
pub fn ext(&mut self) -> Ext<H, N, InMemoryBackend<H>> {
|
||||
Ext::new(
|
||||
&mut self.overlay,
|
||||
&mut self.storage_transaction_cache,
|
||||
&self.backend,
|
||||
Some(&self.changes_trie_storage),
|
||||
match self.changes_trie_config.clone() {
|
||||
Some(config) => Some(ChangesTrieState {
|
||||
config,
|
||||
zero: 0.into(),
|
||||
storage: &self.changes_trie_storage,
|
||||
}),
|
||||
None => None,
|
||||
},
|
||||
Some(&mut self.extensions),
|
||||
)
|
||||
}
|
||||
@@ -72,21 +83,19 @@ impl<H: Hasher, N: ChangesTrieBlockNumber> TestExternalities<H, N>
|
||||
/// Create a new instance of `TestExternalities` with code and storage.
|
||||
pub fn new_with_code(code: &[u8], mut storage: Storage) -> Self {
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
let changes_trie_config = storage.top.get(CHANGES_TRIE_CONFIG)
|
||||
.and_then(|v| Decode::decode(&mut &v[..]).ok());
|
||||
overlay.set_collect_extrinsics(changes_trie_config.is_some());
|
||||
|
||||
assert!(storage.top.keys().all(|key| !is_child_storage_key(key)));
|
||||
assert!(storage.children.keys().all(|key| is_child_storage_key(key)));
|
||||
|
||||
super::set_changes_trie_config(
|
||||
&mut overlay,
|
||||
storage.top.get(&CHANGES_TRIE_CONFIG.to_vec()).cloned(),
|
||||
false,
|
||||
).expect("changes trie configuration is correct in test env; qed");
|
||||
|
||||
storage.top.insert(HEAP_PAGES.to_vec(), 8u64.encode());
|
||||
storage.top.insert(CODE.to_vec(), code.to_vec());
|
||||
|
||||
TestExternalities {
|
||||
overlay,
|
||||
changes_trie_config,
|
||||
changes_trie_storage: ChangesTrieInMemoryStorage::new(),
|
||||
backend: storage.into(),
|
||||
extensions: Default::default(),
|
||||
|
||||
Reference in New Issue
Block a user