sp_state_machine/
in_memory_backend.rs

1// This file is part of Substrate.
2
3// Copyright (C) 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	backend::Backend, trie_backend::TrieBackend, StorageCollection, StorageKey, StorageValue,
22	TrieBackendBuilder,
23};
24use codec::Codec;
25use hash_db::Hasher;
26use sp_core::storage::{ChildInfo, StateVersion, Storage};
27use sp_trie::{empty_trie_root, LayoutV1, PrefixedMemoryDB};
28use std::collections::{BTreeMap, HashMap};
29
30/// Create a new empty instance of in-memory backend.
31pub fn new_in_mem<H>() -> TrieBackend<PrefixedMemoryDB<H>, H>
32where
33	H: Hasher,
34	H::Out: Codec + Ord,
35{
36	// V1 is same as V0 for an empty trie.
37	TrieBackendBuilder::new(Default::default(), empty_trie_root::<LayoutV1<H>>()).build()
38}
39
40impl<H: Hasher> TrieBackend<PrefixedMemoryDB<H>, H>
41where
42	H::Out: Codec + Ord,
43{
44	/// Copy the state, with applied updates
45	pub fn update<T: IntoIterator<Item = (Option<ChildInfo>, StorageCollection)>>(
46		&self,
47		changes: T,
48		state_version: StateVersion,
49	) -> Self {
50		let mut clone = self.clone();
51		clone.insert(changes, state_version);
52		clone
53	}
54
55	/// Insert values into backend trie.
56	pub fn insert<T: IntoIterator<Item = (Option<ChildInfo>, StorageCollection)>>(
57		&mut self,
58		changes: T,
59		state_version: StateVersion,
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().flat_map(|(_, v)| v).map(|(k, v)| (&k[..], v.as_deref())),
64			child.iter().filter_map(|v| {
65				v.0.as_ref().map(|c| (c, v.1.iter().map(|(k, v)| (&k[..], v.as_deref()))))
66			}),
67			state_version,
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: PrefixedMemoryDB<H>) -> Self {
75		let mut clone = self.backend_storage().clone();
76		clone.consolidate(changes);
77		TrieBackendBuilder::new(clone, root).build()
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: PrefixedMemoryDB<H>) {
82		let mut storage = core::mem::take(self).into_storage();
83
84		storage.consolidate(transaction);
85		*self = TrieBackendBuilder::new(storage, root).build();
86	}
87
88	/// Compare with another in-memory backend.
89	pub fn eq(&self, other: &Self) -> bool {
90		self.root() == other.root()
91	}
92}
93
94impl<H: Hasher> Clone for TrieBackend<PrefixedMemoryDB<H>, H>
95where
96	H::Out: Codec + Ord,
97{
98	fn clone(&self) -> Self {
99		TrieBackendBuilder::new(self.backend_storage().clone(), *self.root()).build()
100	}
101}
102
103impl<H> Default for TrieBackend<PrefixedMemoryDB<H>, H>
104where
105	H: Hasher,
106	H::Out: Codec + Ord,
107{
108	fn default() -> Self {
109		new_in_mem()
110	}
111}
112
113impl<H: Hasher> From<(HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>>, StateVersion)>
114	for TrieBackend<PrefixedMemoryDB<H>, H>
115where
116	H::Out: Codec + Ord,
117{
118	fn from(
119		(inner, state_version): (
120			HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>>,
121			StateVersion,
122		),
123	) -> Self {
124		let mut backend = new_in_mem();
125		backend.insert(
126			inner
127				.into_iter()
128				.map(|(k, m)| (k, m.into_iter().map(|(k, v)| (k, Some(v))).collect())),
129			state_version,
130		);
131		backend
132	}
133}
134
135#[cfg(feature = "std")]
136impl<H: Hasher> From<(Storage, StateVersion)> for TrieBackend<PrefixedMemoryDB<H>, H>
137where
138	H::Out: Codec + Ord,
139{
140	fn from((inners, state_version): (Storage, StateVersion)) -> Self {
141		let mut inner: HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>> = inners
142			.children_default
143			.into_values()
144			.map(|c| (Some(c.child_info), c.data))
145			.collect();
146		inner.insert(None, inners.top);
147		(inner, state_version).into()
148	}
149}
150
151impl<H: Hasher> From<(BTreeMap<StorageKey, StorageValue>, StateVersion)>
152	for TrieBackend<PrefixedMemoryDB<H>, H>
153where
154	H::Out: Codec + Ord,
155{
156	fn from((inner, state_version): (BTreeMap<StorageKey, StorageValue>, StateVersion)) -> Self {
157		let mut expanded = HashMap::new();
158		expanded.insert(None, inner);
159		(expanded, state_version).into()
160	}
161}
162
163impl<H: Hasher> From<(Vec<(Option<ChildInfo>, StorageCollection)>, StateVersion)>
164	for TrieBackend<PrefixedMemoryDB<H>, H>
165where
166	H::Out: Codec + Ord,
167{
168	fn from(
169		(inner, state_version): (Vec<(Option<ChildInfo>, StorageCollection)>, StateVersion),
170	) -> Self {
171		let mut expanded: HashMap<Option<ChildInfo>, BTreeMap<StorageKey, StorageValue>> =
172			HashMap::new();
173		for (child_info, key_values) in inner {
174			let entry = expanded.entry(child_info).or_default();
175			for (key, value) in key_values {
176				if let Some(value) = value {
177					entry.insert(key, value);
178				}
179			}
180		}
181		(expanded, state_version).into()
182	}
183}
184
185#[cfg(test)]
186mod tests {
187	use super::*;
188	use crate::backend::{AsTrieBackend, Backend};
189	use sp_core::storage::StateVersion;
190	use sp_runtime::traits::BlakeTwo256;
191
192	/// Assert in memory backend with only child trie keys works as trie backend.
193	#[test]
194	fn in_memory_with_child_trie_only() {
195		let state_version = StateVersion::default();
196		let storage = new_in_mem::<BlakeTwo256>();
197		let child_info = ChildInfo::new_default(b"1");
198		let child_info = &child_info;
199		let storage = storage.update(
200			vec![(Some(child_info.clone()), vec![(b"2".to_vec(), Some(b"3".to_vec()))])],
201			state_version,
202		);
203		let trie_backend = storage.as_trie_backend();
204		assert_eq!(trie_backend.child_storage(child_info, b"2").unwrap(), Some(b"3".to_vec()));
205		let storage_key = child_info.prefixed_storage_key();
206		assert!(trie_backend.storage(storage_key.as_slice()).unwrap().is_some());
207	}
208
209	#[test]
210	fn insert_multiple_times_child_data_works() {
211		let state_version = StateVersion::default();
212		let mut storage = new_in_mem::<BlakeTwo256>();
213		let child_info = ChildInfo::new_default(b"1");
214
215		storage.insert(
216			vec![(Some(child_info.clone()), vec![(b"2".to_vec(), Some(b"3".to_vec()))])],
217			state_version,
218		);
219		storage.insert(
220			vec![(Some(child_info.clone()), vec![(b"1".to_vec(), Some(b"3".to_vec()))])],
221			state_version,
222		);
223
224		assert_eq!(storage.child_storage(&child_info, &b"2"[..]), Ok(Some(b"3".to_vec())));
225		assert_eq!(storage.child_storage(&child_info, &b"1"[..]), Ok(Some(b"3".to_vec())));
226	}
227}