Skip to main content

sc_client_api/
in_mem.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//! In memory client backend
20
21use parking_lot::RwLock;
22use sp_blockchain::{CachedHeaderMetadata, HeaderMetadata};
23use sp_core::{
24	offchain::storage::InMemOffchainStorage as OffchainStorage, storage::well_known_keys,
25};
26use sp_runtime::{
27	generic::BlockId,
28	traits::{Block as BlockT, HashingFor, Header as HeaderT, NumberFor, Zero},
29	Justification, Justifications, StateVersion, Storage,
30};
31use sp_state_machine::{
32	Backend as StateBackend, BackendTransaction, ChildStorageCollection, InMemoryBackend,
33	IndexOperation, StorageCollection,
34};
35use std::{
36	collections::{HashMap, HashSet},
37	ptr,
38	sync::Arc,
39};
40
41use crate::{
42	backend::{self, NewBlockState},
43	blockchain::{self, BlockStatus, HeaderBackend},
44	leaves::LeafSet,
45	TrieCacheContext, UsageInfo,
46};
47
48struct PendingBlock<B: BlockT> {
49	block: StoredBlock<B>,
50	state: NewBlockState,
51	register_as_leaf: bool,
52}
53
54#[derive(PartialEq, Eq, Clone)]
55enum StoredBlock<B: BlockT> {
56	Header(B::Header, Option<Justifications>),
57	Full(B, Option<Justifications>),
58}
59
60impl<B: BlockT> StoredBlock<B> {
61	fn new(
62		header: B::Header,
63		body: Option<Vec<B::Extrinsic>>,
64		just: Option<Justifications>,
65	) -> Self {
66		match body {
67			Some(body) => StoredBlock::Full(B::new(header, body), just),
68			None => StoredBlock::Header(header, just),
69		}
70	}
71
72	fn header(&self) -> &B::Header {
73		match *self {
74			StoredBlock::Header(ref h, _) => h,
75			StoredBlock::Full(ref b, _) => b.header(),
76		}
77	}
78
79	fn justifications(&self) -> Option<&Justifications> {
80		match *self {
81			StoredBlock::Header(_, ref j) | StoredBlock::Full(_, ref j) => j.as_ref(),
82		}
83	}
84
85	fn extrinsics(&self) -> Option<&[B::Extrinsic]> {
86		match *self {
87			StoredBlock::Header(_, _) => None,
88			StoredBlock::Full(ref b, _) => Some(b.extrinsics()),
89		}
90	}
91
92	fn into_inner(self) -> (B::Header, Option<Vec<B::Extrinsic>>, Option<Justifications>) {
93		match self {
94			StoredBlock::Header(header, just) => (header, None, just),
95			StoredBlock::Full(block, just) => {
96				let (header, body) = block.deconstruct();
97				(header, Some(body), just)
98			},
99		}
100	}
101}
102
103#[derive(Clone)]
104struct BlockchainStorage<Block: BlockT> {
105	blocks: HashMap<Block::Hash, StoredBlock<Block>>,
106	hashes: HashMap<NumberFor<Block>, Block::Hash>,
107	best_hash: Block::Hash,
108	best_number: NumberFor<Block>,
109	finalized_hash: Block::Hash,
110	finalized_number: NumberFor<Block>,
111	genesis_hash: Block::Hash,
112	header_cht_roots: HashMap<NumberFor<Block>, Block::Hash>,
113	leaves: LeafSet<Block::Hash, NumberFor<Block>>,
114	aux: HashMap<Vec<u8>, Vec<u8>>,
115}
116
117/// In-memory blockchain. Supports concurrent reads.
118#[derive(Clone)]
119pub struct Blockchain<Block: BlockT> {
120	storage: Arc<RwLock<BlockchainStorage<Block>>>,
121}
122
123impl<Block: BlockT> Default for Blockchain<Block> {
124	fn default() -> Self {
125		Self::new()
126	}
127}
128
129impl<Block: BlockT> Blockchain<Block> {
130	/// Get header hash of given block.
131	pub fn id(&self, id: BlockId<Block>) -> Option<Block::Hash> {
132		match id {
133			BlockId::Hash(h) => Some(h),
134			BlockId::Number(n) => self.storage.read().hashes.get(&n).cloned(),
135		}
136	}
137
138	/// Create new in-memory blockchain storage.
139	pub fn new() -> Blockchain<Block> {
140		let storage = Arc::new(RwLock::new(BlockchainStorage {
141			blocks: HashMap::new(),
142			hashes: HashMap::new(),
143			best_hash: Default::default(),
144			best_number: Zero::zero(),
145			finalized_hash: Default::default(),
146			finalized_number: Zero::zero(),
147			genesis_hash: Default::default(),
148			header_cht_roots: HashMap::new(),
149			leaves: LeafSet::new(),
150			aux: HashMap::new(),
151		}));
152		Blockchain { storage }
153	}
154
155	/// Insert a block header and associated data.
156	pub fn insert(
157		&self,
158		hash: Block::Hash,
159		header: <Block as BlockT>::Header,
160		justifications: Option<Justifications>,
161		body: Option<Vec<<Block as BlockT>::Extrinsic>>,
162		new_state: NewBlockState,
163		register_as_leaf: bool,
164	) -> sp_blockchain::Result<()> {
165		let number = *header.number();
166		if new_state.is_best() {
167			self.apply_head(&header)?;
168		}
169
170		{
171			let mut storage = self.storage.write();
172			if register_as_leaf {
173				storage.leaves.import(hash, number, *header.parent_hash());
174			}
175			storage.blocks.insert(hash, StoredBlock::new(header, body, justifications));
176
177			if let NewBlockState::Final = new_state {
178				storage.finalized_hash = hash;
179				storage.finalized_number = number;
180			}
181
182			if number == Zero::zero() {
183				storage.genesis_hash = hash;
184			}
185		}
186
187		Ok(())
188	}
189
190	/// Get total number of blocks.
191	pub fn blocks_count(&self) -> usize {
192		self.storage.read().blocks.len()
193	}
194
195	/// Compare this blockchain with another in-mem blockchain
196	pub fn equals_to(&self, other: &Self) -> bool {
197		// Check ptr equality first to avoid double read locks.
198		if ptr::eq(self, other) {
199			return true;
200		}
201		self.canon_equals_to(other) && self.storage.read().blocks == other.storage.read().blocks
202	}
203
204	/// Compare canonical chain to other canonical chain.
205	pub fn canon_equals_to(&self, other: &Self) -> bool {
206		// Check ptr equality first to avoid double read locks.
207		if ptr::eq(self, other) {
208			return true;
209		}
210		let this = self.storage.read();
211		let other = other.storage.read();
212		this.hashes == other.hashes &&
213			this.best_hash == other.best_hash &&
214			this.best_number == other.best_number &&
215			this.genesis_hash == other.genesis_hash
216	}
217
218	/// Insert header CHT root.
219	pub fn insert_cht_root(&self, block: NumberFor<Block>, cht_root: Block::Hash) {
220		self.storage.write().header_cht_roots.insert(block, cht_root);
221	}
222
223	/// Set an existing block as head.
224	pub fn set_head(&self, hash: Block::Hash) -> sp_blockchain::Result<()> {
225		let header = self
226			.header(hash)?
227			.ok_or_else(|| sp_blockchain::Error::UnknownBlock(format!("{}", hash)))?;
228
229		self.apply_head(&header)
230	}
231
232	fn apply_head(&self, header: &<Block as BlockT>::Header) -> sp_blockchain::Result<()> {
233		let hash = header.hash();
234		let number = header.number();
235
236		// Note: this may lock storage, so it must happen before obtaining storage
237		// write lock.
238		let best_tree_route = {
239			let best_hash = self.storage.read().best_hash;
240			if &best_hash == header.parent_hash() {
241				None
242			} else {
243				let route = sp_blockchain::tree_route(self, best_hash, *header.parent_hash())?;
244				Some(route)
245			}
246		};
247
248		let mut storage = self.storage.write();
249
250		if let Some(tree_route) = best_tree_route {
251			// apply retraction and enaction when reorganizing up to parent hash
252			let enacted = tree_route.enacted();
253
254			for entry in enacted {
255				storage.hashes.insert(entry.number, entry.hash);
256			}
257
258			for entry in tree_route.retracted().iter().skip(enacted.len()) {
259				storage.hashes.remove(&entry.number);
260			}
261		}
262
263		storage.best_hash = hash;
264		storage.best_number = *number;
265		storage.hashes.insert(*number, hash);
266
267		Ok(())
268	}
269
270	fn finalize_header(
271		&self,
272		block: Block::Hash,
273		justification: Option<Justification>,
274	) -> sp_blockchain::Result<()> {
275		let mut storage = self.storage.write();
276		storage.finalized_hash = block;
277
278		if justification.is_some() {
279			let block = storage
280				.blocks
281				.get_mut(&block)
282				.expect("hash was fetched from a block in the db; qed");
283
284			let block_justifications = match block {
285				StoredBlock::Header(_, ref mut j) | StoredBlock::Full(_, ref mut j) => j,
286			};
287
288			*block_justifications = justification.map(Justifications::from);
289		}
290
291		Ok(())
292	}
293
294	fn append_justification(
295		&self,
296		hash: Block::Hash,
297		justification: Justification,
298	) -> sp_blockchain::Result<()> {
299		let mut storage = self.storage.write();
300
301		let block = storage
302			.blocks
303			.get_mut(&hash)
304			.expect("hash was fetched from a block in the db; qed");
305
306		let block_justifications = match block {
307			StoredBlock::Header(_, ref mut j) | StoredBlock::Full(_, ref mut j) => j,
308		};
309
310		if let Some(stored_justifications) = block_justifications {
311			if !stored_justifications.append(justification) {
312				return Err(sp_blockchain::Error::BadJustification(
313					"Duplicate consensus engine ID".into(),
314				));
315			}
316		} else {
317			*block_justifications = Some(Justifications::from(justification));
318		};
319
320		Ok(())
321	}
322
323	fn write_aux(&self, ops: Vec<(Vec<u8>, Option<Vec<u8>>)>) {
324		let mut storage = self.storage.write();
325		for (k, v) in ops {
326			match v {
327				Some(v) => storage.aux.insert(k, v),
328				None => storage.aux.remove(&k),
329			};
330		}
331	}
332}
333
334impl<Block: BlockT> HeaderBackend<Block> for Blockchain<Block> {
335	fn header(
336		&self,
337		hash: Block::Hash,
338	) -> sp_blockchain::Result<Option<<Block as BlockT>::Header>> {
339		Ok(self.storage.read().blocks.get(&hash).map(|b| b.header().clone()))
340	}
341
342	fn info(&self) -> blockchain::Info<Block> {
343		let storage = self.storage.read();
344		blockchain::Info {
345			best_hash: storage.best_hash,
346			best_number: storage.best_number,
347			genesis_hash: storage.genesis_hash,
348			finalized_hash: storage.finalized_hash,
349			finalized_number: storage.finalized_number,
350			finalized_state: if storage.finalized_hash != Default::default() {
351				Some((storage.finalized_hash, storage.finalized_number))
352			} else {
353				None
354			},
355			number_leaves: storage.leaves.count(),
356			block_gap: None,
357		}
358	}
359
360	fn status(&self, hash: Block::Hash) -> sp_blockchain::Result<BlockStatus> {
361		match self.storage.read().blocks.contains_key(&hash) {
362			true => Ok(BlockStatus::InChain),
363			false => Ok(BlockStatus::Unknown),
364		}
365	}
366
367	fn number(&self, hash: Block::Hash) -> sp_blockchain::Result<Option<NumberFor<Block>>> {
368		Ok(self.storage.read().blocks.get(&hash).map(|b| *b.header().number()))
369	}
370
371	fn hash(
372		&self,
373		number: <<Block as BlockT>::Header as HeaderT>::Number,
374	) -> sp_blockchain::Result<Option<Block::Hash>> {
375		Ok(self.id(BlockId::Number(number)))
376	}
377}
378
379impl<Block: BlockT> HeaderMetadata<Block> for Blockchain<Block> {
380	type Error = sp_blockchain::Error;
381
382	fn header_metadata(
383		&self,
384		hash: Block::Hash,
385	) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
386		self.header(hash)?
387			.map(|header| CachedHeaderMetadata::from(&header))
388			.ok_or_else(|| {
389				sp_blockchain::Error::UnknownBlock(format!("header not found: {}", hash))
390			})
391	}
392
393	fn insert_header_metadata(&self, _hash: Block::Hash, _metadata: CachedHeaderMetadata<Block>) {
394		// No need to implement.
395	}
396	fn remove_header_metadata(&self, _hash: Block::Hash) {
397		// No need to implement.
398	}
399}
400
401impl<Block: BlockT> blockchain::Backend<Block> for Blockchain<Block> {
402	fn body(
403		&self,
404		hash: Block::Hash,
405	) -> sp_blockchain::Result<Option<Vec<<Block as BlockT>::Extrinsic>>> {
406		Ok(self
407			.storage
408			.read()
409			.blocks
410			.get(&hash)
411			.and_then(|b| b.extrinsics().map(|x| x.to_vec())))
412	}
413
414	fn justifications(&self, hash: Block::Hash) -> sp_blockchain::Result<Option<Justifications>> {
415		Ok(self.storage.read().blocks.get(&hash).and_then(|b| b.justifications().cloned()))
416	}
417
418	fn last_finalized(&self) -> sp_blockchain::Result<Block::Hash> {
419		Ok(self.storage.read().finalized_hash)
420	}
421
422	fn leaves(&self) -> sp_blockchain::Result<Vec<Block::Hash>> {
423		Ok(self.storage.read().leaves.hashes())
424	}
425
426	fn children(&self, _parent_hash: Block::Hash) -> sp_blockchain::Result<Vec<Block::Hash>> {
427		unimplemented!()
428	}
429
430	fn indexed_transaction(&self, _hash: Block::Hash) -> sp_blockchain::Result<Option<Vec<u8>>> {
431		unimplemented!("Not supported by the in-mem backend.")
432	}
433
434	fn block_indexed_body(
435		&self,
436		_hash: Block::Hash,
437	) -> sp_blockchain::Result<Option<Vec<Vec<u8>>>> {
438		unimplemented!("Not supported by the in-mem backend.")
439	}
440}
441
442impl<Block: BlockT> backend::AuxStore for Blockchain<Block> {
443	fn insert_aux<
444		'a,
445		'b: 'a,
446		'c: 'a,
447		I: IntoIterator<Item = &'a (&'c [u8], &'c [u8])>,
448		D: IntoIterator<Item = &'a &'b [u8]>,
449	>(
450		&self,
451		insert: I,
452		delete: D,
453	) -> sp_blockchain::Result<()> {
454		let mut storage = self.storage.write();
455		for (k, v) in insert {
456			storage.aux.insert(k.to_vec(), v.to_vec());
457		}
458		for k in delete {
459			storage.aux.remove(*k);
460		}
461		Ok(())
462	}
463
464	fn get_aux(&self, key: &[u8]) -> sp_blockchain::Result<Option<Vec<u8>>> {
465		Ok(self.storage.read().aux.get(key).cloned())
466	}
467}
468
469/// In-memory operation.
470pub struct BlockImportOperation<Block: BlockT> {
471	pending_block: Option<PendingBlock<Block>>,
472	old_state: InMemoryBackend<HashingFor<Block>>,
473	new_state: Option<BackendTransaction<HashingFor<Block>>>,
474	aux: Vec<(Vec<u8>, Option<Vec<u8>>)>,
475	finalized_blocks: Vec<(Block::Hash, Option<Justification>)>,
476	set_head: Option<Block::Hash>,
477}
478
479impl<Block: BlockT> BlockImportOperation<Block> {
480	fn apply_storage(
481		&mut self,
482		storage: Storage,
483		commit: bool,
484		state_version: StateVersion,
485	) -> sp_blockchain::Result<Block::Hash> {
486		check_genesis_storage(&storage)?;
487
488		let child_delta = storage.children_default.values().map(|child_content| {
489			(
490				&child_content.child_info,
491				child_content.data.iter().map(|(k, v)| (k.as_ref(), Some(v.as_ref()))),
492			)
493		});
494
495		let (root, transaction) = self.old_state.full_storage_root(
496			storage.top.iter().map(|(k, v)| (k.as_ref(), Some(v.as_ref()))),
497			child_delta,
498			state_version,
499		);
500
501		if commit {
502			self.new_state = Some(transaction);
503		}
504		Ok(root)
505	}
506}
507
508impl<Block: BlockT> backend::BlockImportOperation<Block> for BlockImportOperation<Block> {
509	type State = InMemoryBackend<HashingFor<Block>>;
510
511	fn state(&self) -> sp_blockchain::Result<Option<&Self::State>> {
512		Ok(Some(&self.old_state))
513	}
514
515	fn set_block_data(
516		&mut self,
517		header: <Block as BlockT>::Header,
518		body: Option<Vec<<Block as BlockT>::Extrinsic>>,
519		_indexed_body: Option<Vec<Vec<u8>>>,
520		justifications: Option<Justifications>,
521		state: NewBlockState,
522		register_as_leaf: bool,
523	) -> sp_blockchain::Result<()> {
524		assert!(self.pending_block.is_none(), "Only one block per operation is allowed");
525		self.pending_block = Some(PendingBlock {
526			block: StoredBlock::new(header, body, justifications),
527			state,
528			register_as_leaf,
529		});
530		Ok(())
531	}
532
533	fn update_db_storage(
534		&mut self,
535		update: BackendTransaction<HashingFor<Block>>,
536	) -> sp_blockchain::Result<()> {
537		self.new_state = Some(update);
538		Ok(())
539	}
540
541	fn set_genesis_state(
542		&mut self,
543		storage: Storage,
544		commit: bool,
545		state_version: StateVersion,
546	) -> sp_blockchain::Result<Block::Hash> {
547		self.apply_storage(storage, commit, state_version)
548	}
549
550	fn reset_storage(
551		&mut self,
552		storage: Storage,
553		state_version: StateVersion,
554	) -> sp_blockchain::Result<Block::Hash> {
555		self.apply_storage(storage, true, state_version)
556	}
557
558	fn insert_aux<I>(&mut self, ops: I) -> sp_blockchain::Result<()>
559	where
560		I: IntoIterator<Item = (Vec<u8>, Option<Vec<u8>>)>,
561	{
562		self.aux.append(&mut ops.into_iter().collect());
563		Ok(())
564	}
565
566	fn update_storage(
567		&mut self,
568		_update: StorageCollection,
569		_child_update: ChildStorageCollection,
570	) -> sp_blockchain::Result<()> {
571		Ok(())
572	}
573
574	fn mark_finalized(
575		&mut self,
576		hash: Block::Hash,
577		justification: Option<Justification>,
578	) -> sp_blockchain::Result<()> {
579		self.finalized_blocks.push((hash, justification));
580		Ok(())
581	}
582
583	fn mark_head(&mut self, hash: Block::Hash) -> sp_blockchain::Result<()> {
584		assert!(self.pending_block.is_none(), "Only one set block per operation is allowed");
585		self.set_head = Some(hash);
586		Ok(())
587	}
588
589	fn update_transaction_index(
590		&mut self,
591		_index: Vec<IndexOperation>,
592	) -> sp_blockchain::Result<()> {
593		Ok(())
594	}
595
596	fn set_create_gap(&mut self, _create_gap: bool) {}
597}
598
599/// In-memory backend. Keeps all states and blocks in memory.
600///
601/// > **Warning**: Doesn't support all the features necessary for a proper database. Only use this
602/// > struct for testing purposes. Do **NOT** use in production.
603pub struct Backend<Block: BlockT> {
604	states: RwLock<HashMap<Block::Hash, InMemoryBackend<HashingFor<Block>>>>,
605	blockchain: Blockchain<Block>,
606	import_lock: RwLock<()>,
607	pinned_blocks: RwLock<HashMap<Block::Hash, i64>>,
608}
609
610impl<Block: BlockT> Backend<Block> {
611	/// Create a new instance of in-mem backend.
612	///
613	/// # Warning
614	///
615	/// For testing purposes only!
616	pub fn new() -> Self {
617		Backend {
618			states: RwLock::new(HashMap::new()),
619			blockchain: Blockchain::new(),
620			import_lock: Default::default(),
621			pinned_blocks: Default::default(),
622		}
623	}
624
625	/// Return the number of references active for a pinned block.
626	///
627	/// # Warning
628	///
629	/// For testing purposes only!
630	pub fn pin_refs(&self, hash: &<Block as BlockT>::Hash) -> Option<i64> {
631		let blocks = self.pinned_blocks.read();
632		blocks.get(hash).map(|value| *value)
633	}
634}
635
636impl<Block: BlockT> backend::AuxStore for Backend<Block> {
637	fn insert_aux<
638		'a,
639		'b: 'a,
640		'c: 'a,
641		I: IntoIterator<Item = &'a (&'c [u8], &'c [u8])>,
642		D: IntoIterator<Item = &'a &'b [u8]>,
643	>(
644		&self,
645		insert: I,
646		delete: D,
647	) -> sp_blockchain::Result<()> {
648		self.blockchain.insert_aux(insert, delete)
649	}
650
651	fn get_aux(&self, key: &[u8]) -> sp_blockchain::Result<Option<Vec<u8>>> {
652		self.blockchain.get_aux(key)
653	}
654}
655
656impl<Block: BlockT> backend::Backend<Block> for Backend<Block> {
657	type BlockImportOperation = BlockImportOperation<Block>;
658	type Blockchain = Blockchain<Block>;
659	type State = InMemoryBackend<HashingFor<Block>>;
660	type OffchainStorage = OffchainStorage;
661
662	fn begin_operation(&self) -> sp_blockchain::Result<Self::BlockImportOperation> {
663		let old_state = self.state_at(Default::default(), TrieCacheContext::Untrusted)?;
664		Ok(BlockImportOperation {
665			pending_block: None,
666			old_state,
667			new_state: None,
668			aux: Default::default(),
669			finalized_blocks: Default::default(),
670			set_head: None,
671		})
672	}
673
674	fn begin_state_operation(
675		&self,
676		operation: &mut Self::BlockImportOperation,
677		block: Block::Hash,
678	) -> sp_blockchain::Result<()> {
679		operation.old_state = self.state_at(block, TrieCacheContext::Untrusted)?;
680		Ok(())
681	}
682
683	fn commit_operation(&self, operation: Self::BlockImportOperation) -> sp_blockchain::Result<()> {
684		if !operation.finalized_blocks.is_empty() {
685			for (block, justification) in operation.finalized_blocks {
686				self.blockchain.finalize_header(block, justification)?;
687			}
688		}
689
690		if let Some(pending_block) = operation.pending_block {
691			let old_state = &operation.old_state;
692			let (header, body, justification) = pending_block.block.into_inner();
693
694			let hash = header.hash();
695
696			let new_state = match operation.new_state {
697				Some(state) => old_state.update_backend(*header.state_root(), state),
698				None => old_state.clone(),
699			};
700
701			self.states.write().insert(hash, new_state);
702
703			self.blockchain.insert(
704				hash,
705				header,
706				justification,
707				body,
708				pending_block.state,
709				pending_block.register_as_leaf,
710			)?;
711		}
712
713		if !operation.aux.is_empty() {
714			self.blockchain.write_aux(operation.aux);
715		}
716
717		if let Some(set_head) = operation.set_head {
718			self.blockchain.set_head(set_head)?;
719		}
720
721		Ok(())
722	}
723
724	fn finalize_block(
725		&self,
726		hash: Block::Hash,
727		justification: Option<Justification>,
728	) -> sp_blockchain::Result<()> {
729		self.blockchain.finalize_header(hash, justification)
730	}
731
732	fn append_justification(
733		&self,
734		hash: Block::Hash,
735		justification: Justification,
736	) -> sp_blockchain::Result<()> {
737		self.blockchain.append_justification(hash, justification)
738	}
739
740	fn blockchain(&self) -> &Self::Blockchain {
741		&self.blockchain
742	}
743
744	fn usage_info(&self) -> Option<UsageInfo> {
745		None
746	}
747
748	fn offchain_storage(&self) -> Option<Self::OffchainStorage> {
749		None
750	}
751
752	fn state_at(
753		&self,
754		hash: Block::Hash,
755		_trie_cache_context: TrieCacheContext,
756	) -> sp_blockchain::Result<Self::State> {
757		if hash == Default::default() {
758			return Ok(Self::State::default());
759		}
760
761		self.states
762			.read()
763			.get(&hash)
764			.cloned()
765			.ok_or_else(|| sp_blockchain::Error::UnknownBlock(format!("{}", hash)))
766	}
767
768	fn revert(
769		&self,
770		_n: NumberFor<Block>,
771		_revert_finalized: bool,
772	) -> sp_blockchain::Result<(NumberFor<Block>, HashSet<Block::Hash>)> {
773		Ok((Zero::zero(), HashSet::new()))
774	}
775
776	fn remove_leaf_block(&self, _hash: Block::Hash) -> sp_blockchain::Result<()> {
777		Ok(())
778	}
779
780	fn get_import_lock(&self) -> &RwLock<()> {
781		&self.import_lock
782	}
783
784	fn requires_full_sync(&self) -> bool {
785		false
786	}
787
788	fn pin_block(&self, hash: <Block as BlockT>::Hash) -> blockchain::Result<()> {
789		let mut blocks = self.pinned_blocks.write();
790		*blocks.entry(hash).or_default() += 1;
791		Ok(())
792	}
793
794	fn unpin_block(&self, hash: <Block as BlockT>::Hash) {
795		let mut blocks = self.pinned_blocks.write();
796		blocks.entry(hash).and_modify(|counter| *counter -= 1).or_insert(-1);
797	}
798}
799
800impl<Block: BlockT> backend::LocalBackend<Block> for Backend<Block> {}
801
802/// Check that genesis storage is valid.
803pub fn check_genesis_storage(storage: &Storage) -> sp_blockchain::Result<()> {
804	if storage.top.iter().any(|(k, _)| well_known_keys::is_child_storage_key(k)) {
805		return Err(sp_blockchain::Error::InvalidState);
806	}
807
808	if storage
809		.children_default
810		.keys()
811		.any(|child_key| !well_known_keys::is_child_storage_key(child_key))
812	{
813		return Err(sp_blockchain::Error::InvalidState);
814	}
815
816	Ok(())
817}
818
819#[cfg(test)]
820mod tests {
821	use crate::{in_mem::Blockchain, NewBlockState};
822	use sp_blockchain::Backend;
823	use sp_runtime::{traits::Header as HeaderT, ConsensusEngineId, Justifications};
824	use substrate_test_runtime::{Block, Header, H256};
825
826	pub const ID1: ConsensusEngineId = *b"TST1";
827	pub const ID2: ConsensusEngineId = *b"TST2";
828
829	fn header(number: u64) -> Header {
830		let parent_hash = match number {
831			0 => Default::default(),
832			_ => header(number - 1).hash(),
833		};
834		Header::new(
835			number,
836			H256::from_low_u64_be(0),
837			H256::from_low_u64_be(0),
838			parent_hash,
839			Default::default(),
840		)
841	}
842
843	fn test_blockchain() -> Blockchain<Block> {
844		let blockchain = Blockchain::<Block>::new();
845		let just0 = Some(Justifications::from((ID1, vec![0])));
846		let just1 = Some(Justifications::from((ID1, vec![1])));
847		let just2 = None;
848		let just3 = Some(Justifications::from((ID1, vec![3])));
849		blockchain
850			.insert(header(0).hash(), header(0), just0, None, NewBlockState::Final, true)
851			.unwrap();
852		blockchain
853			.insert(header(1).hash(), header(1), just1, None, NewBlockState::Final, true)
854			.unwrap();
855		blockchain
856			.insert(header(2).hash(), header(2), just2, None, NewBlockState::Best, true)
857			.unwrap();
858		blockchain
859			.insert(header(3).hash(), header(3), just3, None, NewBlockState::Final, true)
860			.unwrap();
861		blockchain
862	}
863
864	#[test]
865	fn append_and_retrieve_justifications() {
866		let blockchain = test_blockchain();
867		let last_finalized = blockchain.last_finalized().unwrap();
868
869		blockchain.append_justification(last_finalized, (ID2, vec![4])).unwrap();
870		let justifications = {
871			let mut just = Justifications::from((ID1, vec![3]));
872			just.append((ID2, vec![4]));
873			just
874		};
875		assert_eq!(blockchain.justifications(last_finalized).unwrap(), Some(justifications));
876	}
877
878	#[test]
879	fn store_duplicate_justifications_is_forbidden() {
880		let blockchain = test_blockchain();
881		let last_finalized = blockchain.last_finalized().unwrap();
882
883		blockchain.append_justification(last_finalized, (ID2, vec![0])).unwrap();
884		assert!(matches!(
885			blockchain.append_justification(last_finalized, (ID2, vec![1])),
886			Err(sp_blockchain::Error::BadJustification(_)),
887		));
888	}
889}