tp_state_machine/
in_memory_backend.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//! State machine in memory backend.
19
20use crate::{
21	StorageKey, StorageValue, StorageCollection, trie_backend::TrieBackend, backend::Backend,
22};
23use std::collections::{BTreeMap, HashMap};
24use tetsy_hash_db::Hasher;
25use tp_trie::{MemoryDB, empty_trie_root, Layout};
26use codec::Codec;
27use tet_core::storage::{ChildInfo, Storage};
28
29/// Create a new empty instance of in-memory backend.
30pub fn new_in_mem<H: Hasher>() -> TrieBackend<MemoryDB<H>, H>
31where
32	H::Out: Codec + Ord,
33{
34	let db = MemoryDB::default();
35	TrieBackend::new(db, empty_trie_root::<Layout<H>>())
36}
37
38impl<H: Hasher> TrieBackend<MemoryDB<H>, H>
39where
40	H::Out: Codec + Ord,
41{
42	/// Copy the state, with applied updates
43	pub fn update<
44		T: IntoIterator<Item = (Option<ChildInfo>, StorageCollection)>
45	>(
46		&self,
47		changes: T,
48	) -> Self {
49		let mut clone = self.clone();
50		clone.insert(changes);
51		clone
52	}
53
54	/// Insert values into backend trie.
55	pub fn insert<
56		T: IntoIterator<Item = (Option<ChildInfo>, StorageCollection)>
57	>(
58		&mut self,
59		changes: T,
60	) {
61		let (top, child) = changes.into_iter().partition::<Vec<_>, _>(|v| v.0.is_none());
62		let (root, transaction) = self.full_storage_root(
63			top.iter().map(|(_, v)| v).flatten().map(|(k, v)| (&k[..], v.as_deref())),
64			child.iter()
65				.filter_map(|v|
66					v.0.as_ref().map(|c| (c, v.1.iter().map(|(k, v)| (&k[..], v.as_deref()))))
67				),
68		);
69
70		self.apply_transaction(root, transaction);
71	}
72
73	/// Merge trie nodes into this backend.
74	pub fn update_backend(&self, root: H::Out, changes: MemoryDB<H>) -> Self {
75		let mut clone = self.backend_storage().clone();
76		clone.consolidate(changes);
77		Self::new(clone, root)
78	}
79
80	/// Apply the given transaction to this backend and set the root to the given value.
81	pub fn apply_transaction(&mut self, root: H::Out, transaction: MemoryDB<H>) {
82		self.backend_storage_mut().consolidate(transaction);
83		self.essence.set_root(root);
84	}
85
86	/// Compare with another in-memory backend.
87	pub fn eq(&self, other: &Self) -> bool {
88		self.root() == other.root()
89	}
90}
91
92impl<H: Hasher> Clone for TrieBackend<MemoryDB<H>, H>
93where
94	H::Out: Codec + Ord,
95{
96	fn clone(&self) -> Self {
97		TrieBackend::new(self.backend_storage().clone(), self.root().clone())
98	}
99}
100
101impl<H: Hasher> Default for TrieBackend<MemoryDB<H>, H>
102where
103	H::Out: Codec + Ord,
104{
105	fn default() -> Self {
106		new_in_mem()
107	}
108}
109
110impl<H: Hasher> From<HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>>>
111	for TrieBackend<MemoryDB<H>, H>
112where
113	H::Out: Codec + Ord,
114{
115	fn from(inner: HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>>) -> Self {
116		let mut backend = new_in_mem();
117		backend.insert(
118			inner.into_iter().map(|(k, m)| (k, m.into_iter().map(|(k, v)| (k, Some(v))).collect())),
119		);
120		backend
121	}
122}
123
124impl<H: Hasher> From<Storage> for TrieBackend<MemoryDB<H>, H>
125where
126	H::Out: Codec + Ord,
127{
128	fn from(inners: Storage) -> Self {
129		let mut inner: HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>>
130			= inners.children_default.into_iter().map(|(_k, c)| (Some(c.child_info), c.data)).collect();
131		inner.insert(None, inners.top);
132		inner.into()
133	}
134}
135
136impl<H: Hasher> From<BTreeMap<StorageKey, StorageValue>> for TrieBackend<MemoryDB<H>, H>
137where
138	H::Out: Codec + Ord,
139{
140	fn from(inner: BTreeMap<StorageKey, StorageValue>) -> Self {
141		let mut expanded = HashMap::new();
142		expanded.insert(None, inner);
143		expanded.into()
144	}
145}
146
147impl<H: Hasher> From<Vec<(Option<ChildInfo>, StorageCollection)>>
148	for TrieBackend<MemoryDB<H>, H>
149where
150	H::Out: Codec + Ord,
151{
152	fn from(
153		inner: Vec<(Option<ChildInfo>, StorageCollection)>,
154	) -> Self {
155		let mut expanded: HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>>
156			= HashMap::new();
157		for (child_info, key_values) in inner {
158			let entry = expanded.entry(child_info).or_default();
159			for (key, value) in key_values {
160				if let Some(value) = value {
161					entry.insert(key, value);
162				}
163			}
164		}
165		expanded.into()
166	}
167}
168
169#[cfg(test)]
170mod tests {
171	use super::*;
172	use tp_runtime::traits::BlakeTwo256;
173	use crate::backend::Backend;
174
175	/// Assert in memory backend with only child trie keys works as trie backend.
176	#[test]
177	fn in_memory_with_child_trie_only() {
178		let storage = new_in_mem::<BlakeTwo256>();
179		let child_info = ChildInfo::new_default(b"1");
180		let child_info = &child_info;
181		let mut storage = storage.update(
182			vec![(
183				Some(child_info.clone()),
184				vec![(b"2".to_vec(), Some(b"3".to_vec()))]
185			)]
186		);
187		let trie_backend = storage.as_trie_backend().unwrap();
188		assert_eq!(trie_backend.child_storage(child_info, b"2").unwrap(),
189			Some(b"3".to_vec()));
190		let storage_key = child_info.prefixed_storage_key();
191		assert!(trie_backend.storage(storage_key.as_slice()).unwrap().is_some());
192	}
193
194	#[test]
195	fn insert_multiple_times_child_data_works() {
196		let mut storage = new_in_mem::<BlakeTwo256>();
197		let child_info = ChildInfo::new_default(b"1");
198
199		storage.insert(vec![(Some(child_info.clone()), vec![(b"2".to_vec(), Some(b"3".to_vec()))])]);
200		storage.insert(vec![(Some(child_info.clone()), vec![(b"1".to_vec(), Some(b"3".to_vec()))])]);
201
202		assert_eq!(storage.child_storage(&child_info, &b"2"[..]), Ok(Some(b"3".to_vec())));
203		assert_eq!(storage.child_storage(&child_info, &b"1"[..]), Ok(Some(b"3".to_vec())));
204	}
205}