tp_state_machine/changes_trie/
storage.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 storage utilities.
19
20use std::collections::{BTreeMap, HashSet, HashMap};
21use tetsy_hash_db::{Hasher, Prefix, EMPTY_PREFIX};
22use tet_core::storage::PrefixedStorageKey;
23use tp_trie::DBValue;
24use tp_trie::MemoryDB;
25use parking_lot::RwLock;
26use crate::{
27	StorageKey,
28	trie_backend_essence::TrieBackendStorage,
29	changes_trie::{BuildCache, RootsStorage, Storage, AnchorBlockId, BlockNumber},
30};
31
32#[cfg(test)]
33use crate::backend::insert_into_memory_db;
34#[cfg(test)]
35use crate::changes_trie::input::{InputPair, ChildIndex};
36
37/// In-memory implementation of changes trie storage.
38pub struct InMemoryStorage<H: Hasher, Number: BlockNumber> {
39	data: RwLock<InMemoryStorageData<H, Number>>,
40	cache: BuildCache<H::Out, Number>,
41}
42
43/// Adapter for using changes trie storage as a TrieBackendEssence' storage.
44pub struct TrieBackendAdapter<'a, H: Hasher, Number: BlockNumber> {
45	storage: &'a dyn Storage<H, Number>,
46	_hasher: std::marker::PhantomData<(H, Number)>,
47}
48
49struct InMemoryStorageData<H: Hasher, Number: BlockNumber> {
50	roots: BTreeMap<Number, H::Out>,
51	mdb: MemoryDB<H>,
52}
53
54impl<H: Hasher, Number: BlockNumber> InMemoryStorage<H, Number> {
55	/// Creates storage from given in-memory database.
56	pub fn with_db(mdb: MemoryDB<H>) -> Self {
57		Self {
58			data: RwLock::new(InMemoryStorageData {
59				roots: BTreeMap::new(),
60				mdb,
61			}),
62			cache: BuildCache::new(),
63		}
64	}
65
66	/// Creates storage with empty database.
67	pub fn new() -> Self {
68		Self::with_db(Default::default())
69	}
70
71	/// Creates storage with given proof.
72	pub fn with_proof(proof: Vec<Vec<u8>>) -> Self {
73		use tetsy_hash_db::HashDB;
74
75 		let mut proof_db = MemoryDB::<H>::default();
76		for item in proof {
77			proof_db.insert(EMPTY_PREFIX, &item);
78		}
79		Self::with_db(proof_db)
80	}
81
82	/// Get mutable cache reference.
83	pub fn cache_mut(&mut self) -> &mut BuildCache<H::Out, Number> {
84		&mut self.cache
85	}
86
87	/// Create the storage with given blocks.
88	pub fn with_blocks(blocks: Vec<(Number, H::Out)>) -> Self {
89		Self {
90			data: RwLock::new(InMemoryStorageData {
91				roots: blocks.into_iter().collect(),
92				mdb: MemoryDB::default(),
93			}),
94			cache: BuildCache::new(),
95		}
96	}
97
98	#[cfg(test)]
99	pub fn with_inputs(
100		mut top_inputs: Vec<(Number, Vec<InputPair<Number>>)>,
101		children_inputs: Vec<(PrefixedStorageKey, Vec<(Number, Vec<InputPair<Number>>)>)>,
102	) -> Self {
103		let mut mdb = MemoryDB::default();
104		let mut roots = BTreeMap::new();
105		for (storage_key, child_input) in children_inputs {
106			for (block, pairs) in child_input {
107				let root = insert_into_memory_db::<H, _>(&mut mdb, pairs.into_iter().map(Into::into));
108
109				if let Some(root) = root {
110					let ix = if let Some(ix) = top_inputs.iter().position(|v| v.0 == block) {
111						ix
112					} else {
113						top_inputs.push((block.clone(), Default::default()));
114						top_inputs.len() - 1
115					};
116					top_inputs[ix].1.push(InputPair::ChildIndex(
117						ChildIndex { block: block.clone(), storage_key: storage_key.clone() },
118						root.as_ref().to_vec(),
119					));
120				}
121			}
122		}
123
124		for (block, pairs) in top_inputs {
125			let root = insert_into_memory_db::<H, _>(&mut mdb, pairs.into_iter().map(Into::into));
126			if let Some(root) = root {
127				roots.insert(block, root);
128			}
129		}
130
131		InMemoryStorage {
132			data: RwLock::new(InMemoryStorageData {
133				roots,
134				mdb,
135			}),
136			cache: BuildCache::new(),
137		}
138	}
139
140	#[cfg(test)]
141	pub fn clear_storage(&self) {
142		self.data.write().mdb = MemoryDB::default();	// use new to be more correct
143	}
144
145	#[cfg(test)]
146	pub fn remove_from_storage(&self, keys: &HashSet<H::Out>) {
147		let mut data = self.data.write();
148		for key in keys {
149			data.mdb.remove_and_purge(key, tetsy_hash_db::EMPTY_PREFIX);
150		}
151	}
152
153	#[cfg(test)]
154	pub fn into_mdb(self) -> MemoryDB<H> {
155		self.data.into_inner().mdb
156	}
157
158	/// Insert changes trie for given block.
159	pub fn insert(&self, block: Number, changes_trie_root: H::Out, trie: MemoryDB<H>) {
160		let mut data = self.data.write();
161		data.roots.insert(block, changes_trie_root);
162		data.mdb.consolidate(trie);
163	}
164}
165
166impl<H: Hasher, Number: BlockNumber> RootsStorage<H, Number> for InMemoryStorage<H, Number> {
167	fn build_anchor(&self, parent_hash: H::Out) -> Result<AnchorBlockId<H::Out, Number>, String> {
168		self.data.read().roots.iter()
169			.find(|(_, v)| **v == parent_hash)
170			.map(|(k, _)| AnchorBlockId { hash: parent_hash, number: k.clone() })
171			.ok_or_else(|| format!("Can't find associated number for block {:?}", parent_hash))
172	}
173
174	fn root(&self, _anchor_block: &AnchorBlockId<H::Out, Number>, block: Number) -> Result<Option<H::Out>, String> {
175		Ok(self.data.read().roots.get(&block).cloned())
176	}
177}
178
179impl<H: Hasher, Number: BlockNumber> Storage<H, Number> for InMemoryStorage<H, Number> {
180	fn as_roots_storage(&self) -> &dyn RootsStorage<H, Number> {
181		self
182	}
183
184	fn with_cached_changed_keys(
185		&self,
186		root: &H::Out,
187		functor: &mut dyn FnMut(&HashMap<Option<PrefixedStorageKey>, HashSet<StorageKey>>),
188	) -> bool {
189		self.cache.with_changed_keys(root, functor)
190	}
191
192	fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>, String> {
193		MemoryDB::<H>::get(&self.data.read().mdb, key, prefix)
194	}
195}
196
197impl<'a, H: Hasher, Number: BlockNumber> TrieBackendAdapter<'a, H, Number> {
198	pub fn new(storage: &'a dyn Storage<H, Number>) -> Self {
199		Self { storage, _hasher: Default::default() }
200	}
201}
202
203impl<'a, H, Number> TrieBackendStorage<H> for TrieBackendAdapter<'a, H, Number>
204	where
205		Number: BlockNumber,
206		H: Hasher,
207{
208	type Overlay = MemoryDB<H>;
209
210	fn get(&self, key: &H::Out, prefix: Prefix) -> Result<Option<DBValue>, String> {
211		self.storage.get(key, prefix)
212	}
213}