referrerpolicy=no-referrer-when-downgrade

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