referrerpolicy=no-referrer-when-downgrade

sc_client_db/
bench.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! State backend that's useful for benchmarking
20
21use crate::{DbState, DbStateBuilder};
22use hash_db::{Hasher as DbHasher, Prefix};
23use kvdb::{DBTransaction, KeyValueDB};
24use linked_hash_map::LinkedHashMap;
25use parking_lot::Mutex;
26use sp_core::{
27	hexdisplay::HexDisplay,
28	storage::{ChildInfo, TrackedStorageKey},
29};
30use sp_runtime::{traits::Hash, StateVersion, Storage};
31use sp_state_machine::{
32	backend::Backend as StateBackend, BackendTransaction, ChildStorageCollection, DBValue,
33	IterArgs, StorageCollection, StorageIterator, StorageKey, StorageValue,
34};
35use sp_trie::{
36	cache::{CacheSize, SharedTrieCache},
37	prefixed_key, MemoryDB, MerkleValue,
38};
39use std::{
40	cell::{Cell, RefCell},
41	collections::HashMap,
42	sync::Arc,
43};
44
45type State<H> = DbState<H>;
46
47struct StorageDb<Hasher> {
48	db: Arc<dyn KeyValueDB>,
49	_phantom: std::marker::PhantomData<Hasher>,
50}
51
52impl<Hasher: Hash> sp_state_machine::Storage<Hasher> for StorageDb<Hasher> {
53	fn get(&self, key: &Hasher::Output, prefix: Prefix) -> Result<Option<DBValue>, String> {
54		let prefixed_key = prefixed_key::<Hasher>(key, prefix);
55		self.db
56			.get(0, &prefixed_key)
57			.map_err(|e| format!("Database backend error: {:?}", e))
58	}
59}
60
61struct KeyTracker {
62	enable_tracking: bool,
63	/// Key tracker for keys in the main trie.
64	/// We track the total number of reads and writes to these keys,
65	/// not de-duplicated for repeats.
66	main_keys: LinkedHashMap<Vec<u8>, TrackedStorageKey>,
67	/// Key tracker for keys in a child trie.
68	/// Child trie are identified by their storage key (i.e. `ChildInfo::storage_key()`)
69	/// We track the total number of reads and writes to these keys,
70	/// not de-duplicated for repeats.
71	child_keys: LinkedHashMap<Vec<u8>, LinkedHashMap<Vec<u8>, TrackedStorageKey>>,
72}
73
74/// State that manages the backend database reference. Allows runtime to control the database.
75pub struct BenchmarkingState<Hasher: Hash> {
76	root: Cell<Hasher::Output>,
77	genesis_root: Hasher::Output,
78	state: RefCell<Option<State<Hasher>>>,
79	db: Cell<Option<Arc<dyn KeyValueDB>>>,
80	genesis: HashMap<Vec<u8>, (Vec<u8>, i32)>,
81	record: Cell<Vec<Vec<u8>>>,
82	key_tracker: Arc<Mutex<KeyTracker>>,
83	whitelist: RefCell<Vec<TrackedStorageKey>>,
84	proof_recorder: Option<sp_trie::recorder::Recorder<Hasher>>,
85	proof_recorder_root: Cell<Hasher::Output>,
86	shared_trie_cache: SharedTrieCache<Hasher>,
87}
88
89/// A raw iterator over the `BenchmarkingState`.
90pub struct RawIter<Hasher: Hash> {
91	inner: <DbState<Hasher> as StateBackend<Hasher>>::RawIter,
92	child_trie: Option<Vec<u8>>,
93	key_tracker: Arc<Mutex<KeyTracker>>,
94}
95
96impl<Hasher: Hash> StorageIterator<Hasher> for RawIter<Hasher> {
97	type Backend = BenchmarkingState<Hasher>;
98	type Error = String;
99
100	fn next_key(&mut self, backend: &Self::Backend) -> Option<Result<StorageKey, Self::Error>> {
101		match self.inner.next_key(backend.state.borrow().as_ref()?) {
102			Some(Ok(key)) => {
103				self.key_tracker.lock().add_read_key(self.child_trie.as_deref(), &key);
104				Some(Ok(key))
105			},
106			result => result,
107		}
108	}
109
110	fn next_pair(
111		&mut self,
112		backend: &Self::Backend,
113	) -> Option<Result<(StorageKey, StorageValue), Self::Error>> {
114		match self.inner.next_pair(backend.state.borrow().as_ref()?) {
115			Some(Ok((key, value))) => {
116				self.key_tracker.lock().add_read_key(self.child_trie.as_deref(), &key);
117				Some(Ok((key, value)))
118			},
119			result => result,
120		}
121	}
122
123	fn was_complete(&self) -> bool {
124		self.inner.was_complete()
125	}
126}
127
128impl<Hasher: Hash> BenchmarkingState<Hasher> {
129	/// Create a new instance that creates a database in a temporary dir.
130	pub fn new(
131		genesis: Storage,
132		_cache_size_mb: Option<usize>,
133		record_proof: bool,
134		enable_tracking: bool,
135	) -> Result<Self, String> {
136		let state_version = sp_runtime::StateVersion::default();
137		let mut root = Default::default();
138		let mut mdb = MemoryDB::<Hasher>::default();
139		sp_trie::trie_types::TrieDBMutBuilderV1::<Hasher>::new(&mut mdb, &mut root).build();
140
141		let mut state = BenchmarkingState {
142			state: RefCell::new(None),
143			db: Cell::new(None),
144			root: Cell::new(root),
145			genesis: Default::default(),
146			genesis_root: Default::default(),
147			record: Default::default(),
148			key_tracker: Arc::new(Mutex::new(KeyTracker {
149				main_keys: Default::default(),
150				child_keys: Default::default(),
151				enable_tracking,
152			})),
153			whitelist: Default::default(),
154			proof_recorder: record_proof.then(Default::default),
155			proof_recorder_root: Cell::new(root),
156			// Enable the cache, but do not sync anything to the shared state.
157			shared_trie_cache: SharedTrieCache::new(CacheSize::new(0), None),
158		};
159
160		state.add_whitelist_to_tracker();
161
162		state.reopen()?;
163		let child_delta = genesis.children_default.values().map(|child_content| {
164			(
165				&child_content.child_info,
166				child_content.data.iter().map(|(k, v)| (k.as_ref(), Some(v.as_ref()))),
167			)
168		});
169		let (root, transaction): (Hasher::Output, _) =
170			state.state.borrow().as_ref().unwrap().full_storage_root(
171				genesis.top.iter().map(|(k, v)| (k.as_ref(), Some(v.as_ref()))),
172				child_delta,
173				state_version,
174			);
175		state.genesis = transaction.clone().drain();
176		state.genesis_root = root;
177		state.commit(root, transaction, Vec::new(), Vec::new())?;
178		state.record.take();
179		Ok(state)
180	}
181
182	/// Get the proof recorder for this state
183	pub fn recorder(&self) -> Option<sp_trie::recorder::Recorder<Hasher>> {
184		self.proof_recorder.clone()
185	}
186
187	fn reopen(&self) -> Result<(), String> {
188		*self.state.borrow_mut() = None;
189		let db = match self.db.take() {
190			Some(db) => db,
191			None => Arc::new(kvdb_memorydb::create(1)),
192		};
193		self.db.set(Some(db.clone()));
194		if let Some(recorder) = &self.proof_recorder {
195			recorder.reset();
196			self.proof_recorder_root.set(self.root.get());
197		}
198		let storage_db = Arc::new(StorageDb::<Hasher> { db, _phantom: Default::default() });
199		*self.state.borrow_mut() = Some(
200			DbStateBuilder::<Hasher>::new(storage_db, self.root.get())
201				.with_optional_recorder(self.proof_recorder.clone())
202				.with_cache(self.shared_trie_cache.local_cache_trusted())
203				.build(),
204		);
205		Ok(())
206	}
207
208	fn add_whitelist_to_tracker(&self) {
209		self.key_tracker.lock().add_whitelist(&self.whitelist.borrow());
210	}
211
212	fn wipe_tracker(&self) {
213		let mut key_tracker = self.key_tracker.lock();
214		key_tracker.main_keys = LinkedHashMap::new();
215		key_tracker.child_keys = LinkedHashMap::new();
216		key_tracker.add_whitelist(&self.whitelist.borrow());
217	}
218
219	fn add_read_key(&self, childtrie: Option<&[u8]>, key: &[u8]) {
220		self.key_tracker.lock().add_read_key(childtrie, key);
221	}
222
223	fn add_write_key(&self, childtrie: Option<&[u8]>, key: &[u8]) {
224		self.key_tracker.lock().add_write_key(childtrie, key);
225	}
226
227	fn all_trackers(&self) -> Vec<TrackedStorageKey> {
228		self.key_tracker.lock().all_trackers()
229	}
230}
231
232impl KeyTracker {
233	fn add_whitelist(&mut self, whitelist: &[TrackedStorageKey]) {
234		whitelist.iter().for_each(|key| {
235			let mut whitelisted = TrackedStorageKey::new(key.key.clone());
236			whitelisted.whitelist();
237			if let Some(child_trie_key) = &key.child_trie_key {
238				self.child_keys
239					.entry(child_trie_key.clone())
240					.or_default()
241					.insert(key.key.clone(), whitelisted);
242			} else {
243				self.main_keys.insert(key.key.clone(), whitelisted);
244			}
245		});
246	}
247
248	// Childtrie is identified by its storage key (i.e. `ChildInfo::storage_key`)
249	fn add_read_key(&mut self, childtrie: Option<&[u8]>, key: &[u8]) {
250		if !self.enable_tracking {
251			return;
252		}
253
254		let child_key_tracker = &mut self.child_keys;
255		let main_key_tracker = &mut self.main_keys;
256
257		let key_tracker = if let Some(childtrie) = childtrie {
258			child_key_tracker.entry(childtrie.to_vec()).or_insert_with(LinkedHashMap::new)
259		} else {
260			main_key_tracker
261		};
262
263		let should_log = match key_tracker.get_mut(key) {
264			None => {
265				let mut has_been_read = TrackedStorageKey::new(key.to_vec());
266				has_been_read.add_read();
267				key_tracker.insert(key.to_vec(), has_been_read);
268				true
269			},
270			Some(tracker) => {
271				let should_log = !tracker.has_been_read();
272				tracker.add_read();
273				should_log
274			},
275		};
276
277		if should_log {
278			if let Some(childtrie) = childtrie {
279				log::trace!(
280					target: "benchmark",
281					"Childtrie Read: {} {}", HexDisplay::from(&childtrie), HexDisplay::from(&key)
282				);
283			} else {
284				log::trace!(target: "benchmark", "Read: {}", HexDisplay::from(&key));
285			}
286		}
287	}
288
289	// Childtrie is identified by its storage key (i.e. `ChildInfo::storage_key`)
290	fn add_write_key(&mut self, childtrie: Option<&[u8]>, key: &[u8]) {
291		if !self.enable_tracking {
292			return;
293		}
294
295		let child_key_tracker = &mut self.child_keys;
296		let main_key_tracker = &mut self.main_keys;
297
298		let key_tracker = if let Some(childtrie) = childtrie {
299			child_key_tracker.entry(childtrie.to_vec()).or_insert_with(LinkedHashMap::new)
300		} else {
301			main_key_tracker
302		};
303
304		// If we have written to the key, we also consider that we have read from it.
305		let should_log = match key_tracker.get_mut(key) {
306			None => {
307				let mut has_been_written = TrackedStorageKey::new(key.to_vec());
308				has_been_written.add_write();
309				key_tracker.insert(key.to_vec(), has_been_written);
310				true
311			},
312			Some(tracker) => {
313				let should_log = !tracker.has_been_written();
314				tracker.add_write();
315				should_log
316			},
317		};
318
319		if should_log {
320			if let Some(childtrie) = childtrie {
321				log::trace!(
322					target: "benchmark",
323					"Childtrie Write: {} {}", HexDisplay::from(&childtrie), HexDisplay::from(&key)
324				);
325			} else {
326				log::trace!(target: "benchmark", "Write: {}", HexDisplay::from(&key));
327			}
328		}
329	}
330
331	// Return all the tracked storage keys among main and child trie.
332	fn all_trackers(&self) -> Vec<TrackedStorageKey> {
333		let mut all_trackers = Vec::new();
334
335		self.main_keys.iter().for_each(|(_, tracker)| {
336			all_trackers.push(tracker.clone());
337		});
338
339		self.child_keys.iter().for_each(|(_, child_tracker)| {
340			child_tracker.iter().for_each(|(_, tracker)| {
341				all_trackers.push(tracker.clone());
342			});
343		});
344
345		all_trackers
346	}
347}
348
349fn state_err() -> String {
350	"State is not open".into()
351}
352
353impl<Hasher: Hash> StateBackend<Hasher> for BenchmarkingState<Hasher> {
354	type Error = <DbState<Hasher> as StateBackend<Hasher>>::Error;
355	type TrieBackendStorage = <DbState<Hasher> as StateBackend<Hasher>>::TrieBackendStorage;
356	type RawIter = RawIter<Hasher>;
357
358	fn storage(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
359		self.add_read_key(None, key);
360		self.state.borrow().as_ref().ok_or_else(state_err)?.storage(key)
361	}
362
363	fn storage_hash(&self, key: &[u8]) -> Result<Option<Hasher::Output>, Self::Error> {
364		self.add_read_key(None, key);
365		self.state.borrow().as_ref().ok_or_else(state_err)?.storage_hash(key)
366	}
367
368	fn child_storage(
369		&self,
370		child_info: &ChildInfo,
371		key: &[u8],
372	) -> Result<Option<Vec<u8>>, Self::Error> {
373		self.add_read_key(Some(child_info.storage_key()), key);
374		self.state
375			.borrow()
376			.as_ref()
377			.ok_or_else(state_err)?
378			.child_storage(child_info, key)
379	}
380
381	fn child_storage_hash(
382		&self,
383		child_info: &ChildInfo,
384		key: &[u8],
385	) -> Result<Option<Hasher::Output>, Self::Error> {
386		self.add_read_key(Some(child_info.storage_key()), key);
387		self.state
388			.borrow()
389			.as_ref()
390			.ok_or_else(state_err)?
391			.child_storage_hash(child_info, key)
392	}
393
394	fn closest_merkle_value(
395		&self,
396		key: &[u8],
397	) -> Result<Option<MerkleValue<Hasher::Output>>, Self::Error> {
398		self.add_read_key(None, key);
399		self.state.borrow().as_ref().ok_or_else(state_err)?.closest_merkle_value(key)
400	}
401
402	fn child_closest_merkle_value(
403		&self,
404		child_info: &ChildInfo,
405		key: &[u8],
406	) -> Result<Option<MerkleValue<Hasher::Output>>, Self::Error> {
407		self.add_read_key(Some(child_info.storage_key()), key);
408		self.state
409			.borrow()
410			.as_ref()
411			.ok_or_else(state_err)?
412			.child_closest_merkle_value(child_info, key)
413	}
414
415	fn exists_storage(&self, key: &[u8]) -> Result<bool, Self::Error> {
416		self.add_read_key(None, key);
417		self.state.borrow().as_ref().ok_or_else(state_err)?.exists_storage(key)
418	}
419
420	fn exists_child_storage(
421		&self,
422		child_info: &ChildInfo,
423		key: &[u8],
424	) -> Result<bool, Self::Error> {
425		self.add_read_key(Some(child_info.storage_key()), key);
426		self.state
427			.borrow()
428			.as_ref()
429			.ok_or_else(state_err)?
430			.exists_child_storage(child_info, key)
431	}
432
433	fn next_storage_key(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
434		self.add_read_key(None, key);
435		self.state.borrow().as_ref().ok_or_else(state_err)?.next_storage_key(key)
436	}
437
438	fn next_child_storage_key(
439		&self,
440		child_info: &ChildInfo,
441		key: &[u8],
442	) -> Result<Option<Vec<u8>>, Self::Error> {
443		self.add_read_key(Some(child_info.storage_key()), key);
444		self.state
445			.borrow()
446			.as_ref()
447			.ok_or_else(state_err)?
448			.next_child_storage_key(child_info, key)
449	}
450
451	fn storage_root<'a>(
452		&self,
453		delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
454		state_version: StateVersion,
455	) -> (Hasher::Output, BackendTransaction<Hasher>) {
456		self.state
457			.borrow()
458			.as_ref()
459			.map_or(Default::default(), |s| s.storage_root(delta, state_version))
460	}
461
462	fn child_storage_root<'a>(
463		&self,
464		child_info: &ChildInfo,
465		delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
466		state_version: StateVersion,
467	) -> (Hasher::Output, bool, BackendTransaction<Hasher>) {
468		self.state
469			.borrow()
470			.as_ref()
471			.map_or(Default::default(), |s| s.child_storage_root(child_info, delta, state_version))
472	}
473
474	fn raw_iter(&self, args: IterArgs) -> Result<Self::RawIter, Self::Error> {
475		let child_trie =
476			args.child_info.as_ref().map(|child_info| child_info.storage_key().to_vec());
477		self.state
478			.borrow()
479			.as_ref()
480			.map(|s| s.raw_iter(args))
481			.unwrap_or(Ok(Default::default()))
482			.map(|raw_iter| RawIter {
483				inner: raw_iter,
484				key_tracker: self.key_tracker.clone(),
485				child_trie,
486			})
487	}
488
489	fn commit(
490		&self,
491		storage_root: <Hasher as DbHasher>::Out,
492		mut transaction: BackendTransaction<Hasher>,
493		main_storage_changes: StorageCollection,
494		child_storage_changes: ChildStorageCollection,
495	) -> Result<(), Self::Error> {
496		if let Some(db) = self.db.take() {
497			let mut db_transaction = DBTransaction::new();
498			let changes = transaction.drain();
499			let mut keys = Vec::with_capacity(changes.len());
500			for (key, (val, rc)) in changes {
501				if rc > 0 {
502					db_transaction.put(0, &key, &val);
503				} else if rc < 0 {
504					db_transaction.delete(0, &key);
505				}
506				keys.push(key);
507			}
508			let mut record = self.record.take();
509			record.extend(keys);
510			self.record.set(record);
511			db.write(db_transaction)
512				.map_err(|_| String::from("Error committing transaction"))?;
513			self.root.set(storage_root);
514			self.db.set(Some(db));
515
516			// Track DB Writes
517			main_storage_changes.iter().for_each(|(key, _)| {
518				self.add_write_key(None, key);
519			});
520			child_storage_changes.iter().for_each(|(child_storage_key, storage_changes)| {
521				storage_changes.iter().for_each(|(key, _)| {
522					self.add_write_key(Some(child_storage_key), key);
523				})
524			});
525		} else {
526			return Err("Trying to commit to a closed db".into());
527		}
528		self.reopen()
529	}
530
531	fn wipe(&self) -> Result<(), Self::Error> {
532		// Restore to genesis
533		let record = self.record.take();
534		if let Some(db) = self.db.take() {
535			let mut db_transaction = DBTransaction::new();
536			for key in record {
537				match self.genesis.get(&key) {
538					Some((v, _)) => db_transaction.put(0, &key, v),
539					None => db_transaction.delete(0, &key),
540				}
541			}
542			db.write(db_transaction)
543				.map_err(|_| String::from("Error committing transaction"))?;
544			self.db.set(Some(db));
545		}
546
547		self.root.set(self.genesis_root);
548		self.reopen()?;
549		self.wipe_tracker();
550		Ok(())
551	}
552
553	/// Get the key tracking information for the state db.
554	/// 1. `reads` - Total number of DB reads.
555	/// 2. `repeat_reads` - Total number of in-memory reads.
556	/// 3. `writes` - Total number of DB writes.
557	/// 4. `repeat_writes` - Total number of in-memory writes.
558	fn read_write_count(&self) -> (u32, u32, u32, u32) {
559		let mut reads = 0;
560		let mut repeat_reads = 0;
561		let mut writes = 0;
562		let mut repeat_writes = 0;
563
564		self.all_trackers().iter().for_each(|tracker| {
565			if !tracker.whitelisted {
566				if tracker.reads > 0 {
567					reads += 1;
568					repeat_reads += tracker.reads - 1;
569				}
570
571				if tracker.writes > 0 {
572					writes += 1;
573					repeat_writes += tracker.writes - 1;
574				}
575			}
576		});
577		(reads, repeat_reads, writes, repeat_writes)
578	}
579
580	/// Reset the key tracking information for the state db.
581	fn reset_read_write_count(&self) {
582		self.wipe_tracker()
583	}
584
585	fn get_whitelist(&self) -> Vec<TrackedStorageKey> {
586		self.whitelist.borrow().to_vec()
587	}
588
589	fn set_whitelist(&self, new: Vec<TrackedStorageKey>) {
590		*self.whitelist.borrow_mut() = new;
591	}
592
593	fn get_read_and_written_keys(&self) -> Vec<(Vec<u8>, u32, u32, bool)> {
594		// We only track at the level of a key-prefix and not whitelisted for now for memory size.
595		// TODO: Refactor to enable full storage key transparency, where we can remove the
596		// `prefix_key_tracker`.
597		let mut prefix_key_tracker = LinkedHashMap::<Vec<u8>, (u32, u32, bool)>::new();
598		self.all_trackers().iter().for_each(|tracker| {
599			if !tracker.whitelisted {
600				let prefix_length = tracker.key.len().min(32);
601				let prefix = tracker.key[0..prefix_length].to_vec();
602				// each read / write of a specific key is counted at most one time, since
603				// additional reads / writes happen in the memory overlay.
604				let reads = tracker.reads.min(1);
605				let writes = tracker.writes.min(1);
606				if let Some(prefix_tracker) = prefix_key_tracker.get_mut(&prefix) {
607					prefix_tracker.0 += reads;
608					prefix_tracker.1 += writes;
609				} else {
610					prefix_key_tracker.insert(prefix, (reads, writes, tracker.whitelisted));
611				}
612			}
613		});
614
615		prefix_key_tracker
616			.iter()
617			.map(|(key, tracker)| -> (Vec<u8>, u32, u32, bool) {
618				(key.to_vec(), tracker.0, tracker.1, tracker.2)
619			})
620			.collect::<Vec<_>>()
621	}
622
623	fn register_overlay_stats(&self, stats: &sp_state_machine::StateMachineStats) {
624		self.state.borrow().as_ref().map(|s| s.register_overlay_stats(stats));
625	}
626
627	fn usage_info(&self) -> sp_state_machine::UsageInfo {
628		self.state
629			.borrow()
630			.as_ref()
631			.map_or(sp_state_machine::UsageInfo::empty(), |s| s.usage_info())
632	}
633
634	fn proof_size(&self) -> Option<u32> {
635		self.proof_recorder.as_ref().map(|recorder| {
636			let proof_size = recorder.estimate_encoded_size() as u32;
637
638			let proof = recorder.to_storage_proof();
639
640			let proof_recorder_root = self.proof_recorder_root.get();
641			if proof_recorder_root == Default::default() || proof_size == 1 {
642				// empty trie
643				log::debug!(target: "benchmark", "Some proof size: {}", &proof_size);
644				proof_size
645			} else {
646				if let Some(size) = proof.encoded_compact_size::<Hasher>(proof_recorder_root) {
647					size as u32
648				} else if proof_recorder_root == self.root.get() {
649					log::debug!(target: "benchmark", "No changes - no proof");
650					0
651				} else {
652					panic!(
653						"proof rec root {:?}, root {:?}, genesis {:?}, rec_len {:?}",
654						self.proof_recorder_root.get(),
655						self.root.get(),
656						self.genesis_root,
657						proof_size,
658					);
659				}
660			}
661		})
662	}
663}
664
665impl<Hasher: Hash> std::fmt::Debug for BenchmarkingState<Hasher> {
666	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
667		write!(f, "Bench DB")
668	}
669}
670
671#[cfg(test)]
672mod test {
673	use crate::bench::BenchmarkingState;
674	use sp_runtime::traits::HashingFor;
675	use sp_state_machine::backend::Backend as _;
676
677	fn hex(hex: &str) -> Vec<u8> {
678		array_bytes::hex2bytes(hex).unwrap()
679	}
680
681	#[test]
682	fn iteration_is_also_counted_in_rw_counts() {
683		let storage = sp_runtime::Storage {
684			top: vec![(
685				hex("ce6e1397e668c7fcf47744350dc59688455a2c2dbd2e2a649df4e55d93cd7158"),
686				hex("0102030405060708"),
687			)]
688			.into_iter()
689			.collect(),
690			..sp_runtime::Storage::default()
691		};
692		let bench_state =
693			BenchmarkingState::<HashingFor<crate::tests::Block>>::new(storage, None, false, true)
694				.unwrap();
695
696		assert_eq!(bench_state.read_write_count(), (0, 0, 0, 0));
697		assert_eq!(bench_state.keys(Default::default()).unwrap().count(), 1);
698		assert_eq!(bench_state.read_write_count(), (1, 0, 0, 0));
699	}
700
701	#[test]
702	fn read_to_main_and_child_tries() {
703		let bench_state = BenchmarkingState::<HashingFor<crate::tests::Block>>::new(
704			Default::default(),
705			None,
706			false,
707			true,
708		)
709		.unwrap();
710
711		for _ in 0..2 {
712			let child1 = sp_core::storage::ChildInfo::new_default(b"child1");
713			let child2 = sp_core::storage::ChildInfo::new_default(b"child2");
714
715			bench_state.storage(b"foo").unwrap();
716			bench_state.child_storage(&child1, b"foo").unwrap();
717			bench_state.child_storage(&child2, b"foo").unwrap();
718
719			bench_state.storage(b"bar").unwrap();
720			bench_state.child_storage(&child1, b"bar").unwrap();
721			bench_state.child_storage(&child2, b"bar").unwrap();
722
723			bench_state
724				.commit(
725					Default::default(),
726					Default::default(),
727					vec![("foo".as_bytes().to_vec(), None)],
728					vec![("child1".as_bytes().to_vec(), vec![("foo".as_bytes().to_vec(), None)])],
729				)
730				.unwrap();
731
732			let rw_tracker = bench_state.read_write_count();
733			assert_eq!(rw_tracker.0, 6);
734			assert_eq!(rw_tracker.1, 0);
735			assert_eq!(rw_tracker.2, 2);
736			assert_eq!(rw_tracker.3, 0);
737			bench_state.wipe().unwrap();
738		}
739	}
740}