tp_state_machine/changes_trie/
mod.rs

1// This file is part of Tetcore.
2
3// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Changes trie related structures and functions.
19//!
20//! Changes trie is a trie built of { storage key => extrinsics } pairs
21//! at the end of each block. For every changed storage key it contains
22//! a pair, mapping key to the set of extrinsics where it has been changed.
23//!
24//! Optionally, every N blocks, additional level1-digest nodes are appended
25//! to the changes trie, containing pairs { storage key => blocks }. For every
26//! storage key that has been changed in PREVIOUS N-1 blocks (except for genesis
27//! block) it contains a pair, mapping this key to the set of blocks where it
28//! has been changed.
29//!
30//! Optionally, every N^digest_level (where digest_level > 1) blocks, additional
31//! digest_level digest is created. It is built out of pairs { storage key => digest
32//! block }, containing entries for every storage key that has been changed in
33//! the last N*digest_level-1 blocks (except for genesis block), mapping these keys
34//! to the set of lower-level digest blocks.
35//!
36//! Changes trie configuration could change within a time. The range of blocks, where
37//! configuration has been active, is given by two blocks: zero and end. Zero block is
38//! the block where configuration has been set. But the first changes trie that uses
39//! this configuration will be built at the block zero+1. If configuration deactivates
40//! at some block, this will be the end block of the configuration. It is also the
41//! zero block of the next configuration.
42//!
43//! If configuration has the end block, it also means that 'skewed digest' has/should
44//! been built at that block. If this is the block where max-level digest should have
45//! been created, than it is simply max-level digest of this configuration. Otherwise,
46//! it is the digest that covers all blocks since last max-level digest block was
47//! created.
48//!
49//! Changes trie only contains the top level storage changes. Sub-level changes
50//! are propagated through its storage root on the top level storage.
51
52mod build;
53mod build_cache;
54mod build_iterator;
55mod changes_iterator;
56mod input;
57mod prune;
58mod storage;
59mod surface_iterator;
60
61pub use self::build_cache::{BuildCache, CachedBuildData, CacheAction};
62pub use self::storage::InMemoryStorage;
63pub use self::changes_iterator::{
64	key_changes, key_changes_proof,
65	key_changes_proof_check, key_changes_proof_check_with_db,
66};
67pub use self::prune::prune;
68
69use std::collections::{HashMap, HashSet};
70use std::convert::TryInto;
71use tetsy_hash_db::{Hasher, Prefix};
72use num_traits::{One, Zero};
73use codec::{Decode, Encode};
74use tet_core;
75use tet_core::storage::PrefixedStorageKey;
76use tp_trie::{MemoryDB, DBValue, TrieMut};
77use tp_trie::trie_types::TrieDBMut;
78use crate::{
79	StorageKey,
80	backend::Backend,
81	overlayed_changes::OverlayedChanges,
82	changes_trie::{
83		build::prepare_input,
84		build_cache::{IncompleteCachedBuildData, IncompleteCacheAction},
85	},
86};
87
88/// Requirements for block number that can be used with changes tries.
89pub trait BlockNumber:
90	Send + Sync + 'static +
91	std::fmt::Display +
92	Clone +
93	From<u32> + TryInto<u32> + One + Zero +
94	PartialEq + Ord +
95	std::hash::Hash +
96	std::ops::Add<Self, Output=Self> + ::std::ops::Sub<Self, Output=Self> +
97	std::ops::Mul<Self, Output=Self> + ::std::ops::Div<Self, Output=Self> +
98	std::ops::Rem<Self, Output=Self> +
99	std::ops::AddAssign<Self> +
100	num_traits::CheckedMul + num_traits::CheckedSub +
101	Decode + Encode
102{}
103
104impl<T> BlockNumber for T where T:
105	Send + Sync + 'static +
106	std::fmt::Display +
107	Clone +
108	From<u32> + TryInto<u32> + One + Zero +
109	PartialEq + Ord +
110	std::hash::Hash +
111	std::ops::Add<Self, Output=Self> + ::std::ops::Sub<Self, Output=Self> +
112	std::ops::Mul<Self, Output=Self> + ::std::ops::Div<Self, Output=Self> +
113	std::ops::Rem<Self, Output=Self> +
114	std::ops::AddAssign<Self> +
115	num_traits::CheckedMul + num_traits::CheckedSub +
116	Decode + Encode,
117{}
118
119/// Block identifier that could be used to determine fork of this block.
120#[derive(Debug)]
121pub struct AnchorBlockId<Hash: std::fmt::Debug, Number: BlockNumber> {
122	/// Hash of this block.
123	pub hash: Hash,
124	/// Number of this block.
125	pub number: Number,
126}
127
128/// Changes tries state at some block.
129pub struct State<'a, H, Number> {
130	/// Configuration that is active at given block.
131	pub config: Configuration,
132	/// Configuration activation block number. Zero if it is the first configuration on the chain,
133	/// or number of the block that have emit NewConfiguration signal (thus activating configuration
134	/// starting from the **next** block).
135	pub zero: Number,
136	/// Underlying changes tries storage reference.
137	pub storage: &'a dyn Storage<H, Number>,
138}
139
140/// Changes trie storage. Provides access to trie roots and trie nodes.
141pub trait RootsStorage<H: Hasher, Number: BlockNumber>: Send + Sync {
142	/// Resolve hash of the block into anchor.
143	fn build_anchor(&self, hash: H::Out) -> Result<AnchorBlockId<H::Out, Number>, String>;
144	/// Get changes trie root for the block with given number which is an ancestor (or the block
145	/// itself) of the anchor_block (i.e. anchor_block.number >= block).
146	fn root(&self, anchor: &AnchorBlockId<H::Out, Number>, block: Number) -> Result<Option<H::Out>, String>;
147}
148
149/// Changes trie storage. Provides access to trie roots and trie nodes.
150pub trait Storage<H: Hasher, Number: BlockNumber>: RootsStorage<H, Number> {
151	/// Casts from self reference to RootsStorage reference.
152	fn as_roots_storage(&self) -> &dyn RootsStorage<H, Number>;
153	/// Execute given functor with cached entry for given trie root.
154	/// Returns true if the functor has been called (cache entry exists) and false otherwise.
155	fn with_cached_changed_keys(
156		&self,
157		root: &H::Out,
158		functor: &mut dyn FnMut(&HashMap<Option<PrefixedStorageKey>, HashSet<StorageKey>>),
159	) -> bool;
160	/// Get a trie node.
161	fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>, String>;
162}
163
164/// Changes trie storage -> trie backend essence adapter.
165pub struct TrieBackendStorageAdapter<'a, H: Hasher, Number: BlockNumber>(pub &'a dyn Storage<H, Number>);
166
167impl<'a, H: Hasher, N: BlockNumber> crate::TrieBackendStorage<H> for TrieBackendStorageAdapter<'a, H, N> {
168	type Overlay = tp_trie::MemoryDB<H>;
169
170	fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>, String> {
171		self.0.get(key, prefix)
172	}
173}
174
175/// Changes trie configuration.
176pub type Configuration = tet_core::ChangesTrieConfiguration;
177
178/// Blocks range where configuration has been constant.
179#[derive(Clone)]
180pub struct ConfigurationRange<'a, N> {
181	/// Active configuration.
182	pub config: &'a Configuration,
183	/// Zero block of this configuration. The configuration is active starting from the next block.
184	pub zero: N,
185	/// End block of this configuration. It is the last block where configuration has been active.
186	pub end: Option<N>,
187}
188
189impl<'a, H, Number> State<'a, H, Number> {
190	/// Create state with given config and storage.
191	pub fn new(
192		config: Configuration,
193		zero: Number,
194		storage: &'a dyn Storage<H, Number>,
195	) -> Self {
196		Self {
197			config,
198			zero,
199			storage,
200		}
201	}
202}
203
204impl<'a, H, Number: Clone> Clone for State<'a, H, Number> {
205	fn clone(&self) -> Self {
206		State {
207			config: self.config.clone(),
208			zero: self.zero.clone(),
209			storage: self.storage,
210		}
211	}
212}
213
214/// Create state where changes tries are disabled.
215pub fn disabled_state<'a, H, Number>() -> Option<State<'a, H, Number>> {
216	None
217}
218
219/// Compute the changes trie root and transaction for given block.
220/// Returns Err(()) if unknown `parent_hash` has been passed.
221/// Returns Ok(None) if there's no data to perform computation.
222/// Panics if background storage returns an error OR if insert to MemoryDB fails.
223pub fn build_changes_trie<'a, B: Backend<H>, H: Hasher, Number: BlockNumber>(
224	backend: &B,
225	state: Option<&'a State<'a, H, Number>>,
226	changes: &OverlayedChanges,
227	parent_hash: H::Out,
228	panic_on_storage_error: bool,
229) -> Result<Option<(MemoryDB<H>, H::Out, CacheAction<H::Out, Number>)>, ()>
230	where
231		H::Out: Ord + 'static + Encode,
232{
233	/// Panics when `res.is_err() && panic`, otherwise it returns `Err(())` on an error.
234	fn maybe_panic<R, E: std::fmt::Debug>(
235		res: std::result::Result<R, E>,
236		panic: bool,
237	) -> std::result::Result<R, ()> {
238		res.map(Ok)
239			.unwrap_or_else(|e| if panic {
240				panic!("changes trie: storage access is not allowed to fail within runtime: {:?}", e)
241			} else {
242				Err(())
243			})
244	}
245
246	// when storage isn't provided, changes tries aren't created
247	let state = match state {
248		Some(state) => state,
249		None => return Ok(None),
250	};
251
252	// build_anchor error should not be considered fatal
253	let parent = state.storage.build_anchor(parent_hash).map_err(|_| ())?;
254	let block = parent.number.clone() + One::one();
255
256	// prepare configuration range - we already know zero block. Current block may be the end block if configuration
257	// has been changed in this block
258	let is_config_changed = match changes.storage(tet_core::storage::well_known_keys::CHANGES_TRIE_CONFIG) {
259		Some(Some(new_config)) => new_config != &state.config.encode()[..],
260		Some(None) => true,
261		None => false,
262	};
263	let config_range = ConfigurationRange {
264		config: &state.config,
265		zero: state.zero.clone(),
266		end: if is_config_changed { Some(block.clone()) } else { None },
267	};
268
269	// storage errors are considered fatal (similar to situations when runtime fetches values from storage)
270	let (input_pairs, child_input_pairs, digest_input_blocks) = maybe_panic(
271		prepare_input::<B, H, Number>(
272			backend,
273			state.storage,
274			config_range.clone(),
275			changes,
276			&parent,
277		),
278		panic_on_storage_error,
279	)?;
280
281	// prepare cached data
282	let mut cache_action = prepare_cached_build_data(config_range, block.clone());
283	let needs_changed_keys = cache_action.collects_changed_keys();
284	cache_action = cache_action.set_digest_input_blocks(digest_input_blocks);
285
286	let mut mdb = MemoryDB::default();
287	let mut child_roots = Vec::with_capacity(child_input_pairs.len());
288	for (child_index, input_pairs) in child_input_pairs {
289		let mut not_empty = false;
290		let mut root = Default::default();
291		{
292			let mut trie = TrieDBMut::<H>::new(&mut mdb, &mut root);
293			let mut storage_changed_keys = HashSet::new();
294			for input_pair in input_pairs {
295				if needs_changed_keys {
296					if let Some(key) = input_pair.key() {
297						storage_changed_keys.insert(key.to_vec());
298					}
299				}
300
301				let (key, value) = input_pair.into();
302				not_empty = true;
303				maybe_panic(trie.insert(&key, &value), panic_on_storage_error)?;
304			}
305
306			cache_action = cache_action.insert(
307				Some(child_index.storage_key.clone()),
308				storage_changed_keys,
309			);
310		}
311		if not_empty {
312			child_roots.push(input::InputPair::ChildIndex(child_index, root.as_ref().to_vec()));
313		}
314	}
315	let mut root = Default::default();
316	{
317		let mut trie = TrieDBMut::<H>::new(&mut mdb, &mut root);
318		for (key, value) in child_roots.into_iter().map(Into::into) {
319			maybe_panic(trie.insert(&key, &value), panic_on_storage_error)?;
320		}
321
322		let mut storage_changed_keys = HashSet::new();
323		for input_pair in input_pairs {
324			if needs_changed_keys {
325				if let Some(key) = input_pair.key() {
326					storage_changed_keys.insert(key.to_vec());
327				}
328			}
329
330			let (key, value) = input_pair.into();
331			maybe_panic(trie.insert(&key, &value), panic_on_storage_error)?;
332		}
333
334		cache_action = cache_action.insert(
335			None,
336			storage_changed_keys,
337		);
338	}
339
340	let cache_action = cache_action.complete(block, &root);
341	Ok(Some((mdb, root, cache_action)))
342}
343
344/// Prepare empty cached build data for given block.
345fn prepare_cached_build_data<Number: BlockNumber>(
346	config: ConfigurationRange<Number>,
347	block: Number,
348) -> IncompleteCacheAction<Number> {
349	// when digests are not enabled in configuration, we do not need to cache anything
350	// because it'll never be used again for building other tries
351	// => let's clear the cache
352	if !config.config.is_digest_build_enabled() {
353		return IncompleteCacheAction::Clear;
354	}
355
356	// when this is the last block where current configuration is active
357	// => let's clear the cache
358	if config.end.as_ref() == Some(&block) {
359		return IncompleteCacheAction::Clear;
360	}
361
362	// we do not need to cache anything when top-level digest trie is created, because
363	// it'll never be used again for building other tries
364	// => let's clear the cache
365	match config.config.digest_level_at_block(config.zero.clone(), block) {
366		Some((digest_level, _, _)) if digest_level == config.config.digest_levels => IncompleteCacheAction::Clear,
367		_ => IncompleteCacheAction::CacheBuildData(IncompleteCachedBuildData::new()),
368	}
369}
370
371#[cfg(test)]
372mod tests {
373	use super::*;
374
375	#[test]
376	fn cache_is_cleared_when_digests_are_disabled() {
377		let config = Configuration { digest_interval: 0, digest_levels: 0 };
378		let config_range = ConfigurationRange { zero: 0, end: None, config: &config };
379		assert_eq!(prepare_cached_build_data(config_range, 8u32), IncompleteCacheAction::Clear);
380	}
381
382	#[test]
383	fn build_data_is_cached_when_digests_are_enabled() {
384		let config = Configuration { digest_interval: 8, digest_levels: 2 };
385		let config_range = ConfigurationRange { zero: 0, end: None, config: &config };
386		assert!(prepare_cached_build_data(config_range.clone(), 4u32).collects_changed_keys());
387		assert!(prepare_cached_build_data(config_range.clone(), 7u32).collects_changed_keys());
388		assert!(prepare_cached_build_data(config_range, 8u32).collects_changed_keys());
389	}
390
391	#[test]
392	fn cache_is_cleared_when_digests_are_enabled_and_top_level_digest_is_built() {
393		let config = Configuration { digest_interval: 8, digest_levels: 2 };
394		let config_range = ConfigurationRange { zero: 0, end: None, config: &config };
395		assert_eq!(prepare_cached_build_data(config_range, 64u32), IncompleteCacheAction::Clear);
396	}
397
398	#[test]
399	fn cache_is_cleared_when_end_block_of_configuration_is_built() {
400		let config = Configuration { digest_interval: 8, digest_levels: 2 };
401		let config_range = ConfigurationRange { zero: 0, end: Some(4u32), config: &config };
402		assert_eq!(prepare_cached_build_data(config_range.clone(), 4u32), IncompleteCacheAction::Clear);
403	}
404}