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, H256,
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: H256) -> sp_blockchain::Result<Option<Vec<u8>>> {
431		unimplemented!("Not supported by the in-mem backend.")
432	}
433
434	fn block_indexed_hashes(&self, _hash: Block::Hash) -> sp_blockchain::Result<Option<Vec<H256>>> {
435		unimplemented!("Not supported by the in-mem backend.")
436	}
437
438	fn block_indexed_body(
439		&self,
440		_hash: Block::Hash,
441	) -> sp_blockchain::Result<Option<Vec<Vec<u8>>>> {
442		unimplemented!("Not supported by the in-mem backend.")
443	}
444}
445
446impl<Block: BlockT> backend::AuxStore for Blockchain<Block> {
447	fn insert_aux<
448		'a,
449		'b: 'a,
450		'c: 'a,
451		I: IntoIterator<Item = &'a (&'c [u8], &'c [u8])>,
452		D: IntoIterator<Item = &'a &'b [u8]>,
453	>(
454		&self,
455		insert: I,
456		delete: D,
457	) -> sp_blockchain::Result<()> {
458		let mut storage = self.storage.write();
459		for (k, v) in insert {
460			storage.aux.insert(k.to_vec(), v.to_vec());
461		}
462		for k in delete {
463			storage.aux.remove(*k);
464		}
465		Ok(())
466	}
467
468	fn get_aux(&self, key: &[u8]) -> sp_blockchain::Result<Option<Vec<u8>>> {
469		Ok(self.storage.read().aux.get(key).cloned())
470	}
471}
472
473/// In-memory operation.
474pub struct BlockImportOperation<Block: BlockT> {
475	pending_block: Option<PendingBlock<Block>>,
476	old_state: InMemoryBackend<HashingFor<Block>>,
477	new_state: Option<BackendTransaction<HashingFor<Block>>>,
478	aux: Vec<(Vec<u8>, Option<Vec<u8>>)>,
479	finalized_blocks: Vec<(Block::Hash, Option<Justification>)>,
480	set_head: Option<Block::Hash>,
481}
482
483impl<Block: BlockT> BlockImportOperation<Block> {
484	fn apply_storage(
485		&mut self,
486		storage: Storage,
487		commit: bool,
488		state_version: StateVersion,
489	) -> sp_blockchain::Result<Block::Hash> {
490		check_genesis_storage(&storage)?;
491
492		let child_delta = storage.children_default.values().map(|child_content| {
493			(
494				&child_content.child_info,
495				child_content.data.iter().map(|(k, v)| (k.as_ref(), Some(v.as_ref()))),
496			)
497		});
498
499		let (root, transaction) = self.old_state.full_storage_root(
500			storage.top.iter().map(|(k, v)| (k.as_ref(), Some(v.as_ref()))),
501			child_delta,
502			state_version,
503		);
504
505		if commit {
506			self.new_state = Some(transaction);
507		}
508		Ok(root)
509	}
510}
511
512impl<Block: BlockT> backend::BlockImportOperation<Block> for BlockImportOperation<Block> {
513	type State = InMemoryBackend<HashingFor<Block>>;
514
515	fn state(&self) -> sp_blockchain::Result<Option<&Self::State>> {
516		Ok(Some(&self.old_state))
517	}
518
519	fn set_block_data(
520		&mut self,
521		header: <Block as BlockT>::Header,
522		body: Option<Vec<<Block as BlockT>::Extrinsic>>,
523		_indexed_body: Option<Vec<Vec<u8>>>,
524		justifications: Option<Justifications>,
525		state: NewBlockState,
526		register_as_leaf: bool,
527	) -> sp_blockchain::Result<()> {
528		assert!(self.pending_block.is_none(), "Only one block per operation is allowed");
529		self.pending_block = Some(PendingBlock {
530			block: StoredBlock::new(header, body, justifications),
531			state,
532			register_as_leaf,
533		});
534		Ok(())
535	}
536
537	fn update_db_storage(
538		&mut self,
539		update: BackendTransaction<HashingFor<Block>>,
540	) -> sp_blockchain::Result<()> {
541		self.new_state = Some(update);
542		Ok(())
543	}
544
545	fn set_genesis_state(
546		&mut self,
547		storage: Storage,
548		commit: bool,
549		state_version: StateVersion,
550	) -> sp_blockchain::Result<Block::Hash> {
551		self.apply_storage(storage, commit, state_version)
552	}
553
554	fn reset_storage(
555		&mut self,
556		storage: Storage,
557		state_version: StateVersion,
558	) -> sp_blockchain::Result<Block::Hash> {
559		self.apply_storage(storage, true, state_version)
560	}
561
562	fn insert_aux<I>(&mut self, ops: I) -> sp_blockchain::Result<()>
563	where
564		I: IntoIterator<Item = (Vec<u8>, Option<Vec<u8>>)>,
565	{
566		self.aux.append(&mut ops.into_iter().collect());
567		Ok(())
568	}
569
570	fn update_storage(
571		&mut self,
572		_update: StorageCollection,
573		_child_update: ChildStorageCollection,
574	) -> sp_blockchain::Result<()> {
575		Ok(())
576	}
577
578	fn mark_finalized(
579		&mut self,
580		hash: Block::Hash,
581		justification: Option<Justification>,
582	) -> sp_blockchain::Result<()> {
583		self.finalized_blocks.push((hash, justification));
584		Ok(())
585	}
586
587	fn mark_head(&mut self, hash: Block::Hash) -> sp_blockchain::Result<()> {
588		assert!(self.pending_block.is_none(), "Only one set block per operation is allowed");
589		self.set_head = Some(hash);
590		Ok(())
591	}
592
593	fn update_transaction_index(
594		&mut self,
595		_index: Vec<IndexOperation>,
596	) -> sp_blockchain::Result<()> {
597		Ok(())
598	}
599
600	fn set_create_gap(&mut self, _create_gap: bool) {}
601}
602
603/// In-memory backend. Keeps all states and blocks in memory.
604///
605/// > **Warning**: Doesn't support all the features necessary for a proper database. Only use this
606/// > struct for testing purposes. Do **NOT** use in production.
607pub struct Backend<Block: BlockT> {
608	states: RwLock<HashMap<Block::Hash, InMemoryBackend<HashingFor<Block>>>>,
609	blockchain: Blockchain<Block>,
610	import_lock: RwLock<()>,
611	pinned_blocks: RwLock<HashMap<Block::Hash, i64>>,
612}
613
614impl<Block: BlockT> Backend<Block> {
615	/// Create a new instance of in-mem backend.
616	///
617	/// # Warning
618	///
619	/// For testing purposes only!
620	pub fn new() -> Self {
621		Backend {
622			states: RwLock::new(HashMap::new()),
623			blockchain: Blockchain::new(),
624			import_lock: Default::default(),
625			pinned_blocks: Default::default(),
626		}
627	}
628
629	/// Return the number of references active for a pinned block.
630	///
631	/// # Warning
632	///
633	/// For testing purposes only!
634	pub fn pin_refs(&self, hash: &<Block as BlockT>::Hash) -> Option<i64> {
635		let blocks = self.pinned_blocks.read();
636		blocks.get(hash).map(|value| *value)
637	}
638}
639
640impl<Block: BlockT> backend::AuxStore for Backend<Block> {
641	fn insert_aux<
642		'a,
643		'b: 'a,
644		'c: 'a,
645		I: IntoIterator<Item = &'a (&'c [u8], &'c [u8])>,
646		D: IntoIterator<Item = &'a &'b [u8]>,
647	>(
648		&self,
649		insert: I,
650		delete: D,
651	) -> sp_blockchain::Result<()> {
652		self.blockchain.insert_aux(insert, delete)
653	}
654
655	fn get_aux(&self, key: &[u8]) -> sp_blockchain::Result<Option<Vec<u8>>> {
656		self.blockchain.get_aux(key)
657	}
658}
659
660impl<Block: BlockT> backend::Backend<Block> for Backend<Block> {
661	type BlockImportOperation = BlockImportOperation<Block>;
662	type Blockchain = Blockchain<Block>;
663	type State = InMemoryBackend<HashingFor<Block>>;
664	type OffchainStorage = OffchainStorage;
665
666	fn begin_operation(&self) -> sp_blockchain::Result<Self::BlockImportOperation> {
667		let old_state = self.state_at(Default::default(), TrieCacheContext::Untrusted)?;
668		Ok(BlockImportOperation {
669			pending_block: None,
670			old_state,
671			new_state: None,
672			aux: Default::default(),
673			finalized_blocks: Default::default(),
674			set_head: None,
675		})
676	}
677
678	fn begin_state_operation(
679		&self,
680		operation: &mut Self::BlockImportOperation,
681		block: Block::Hash,
682	) -> sp_blockchain::Result<()> {
683		operation.old_state = self.state_at(block, TrieCacheContext::Untrusted)?;
684		Ok(())
685	}
686
687	fn commit_operation(&self, operation: Self::BlockImportOperation) -> sp_blockchain::Result<()> {
688		if !operation.finalized_blocks.is_empty() {
689			for (block, justification) in operation.finalized_blocks {
690				self.blockchain.finalize_header(block, justification)?;
691			}
692		}
693
694		if let Some(pending_block) = operation.pending_block {
695			let old_state = &operation.old_state;
696			let (header, body, justification) = pending_block.block.into_inner();
697
698			let hash = header.hash();
699
700			let new_state = match operation.new_state {
701				Some(state) => old_state.update_backend(*header.state_root(), state),
702				None => old_state.clone(),
703			};
704
705			self.states.write().insert(hash, new_state);
706
707			self.blockchain.insert(
708				hash,
709				header,
710				justification,
711				body,
712				pending_block.state,
713				pending_block.register_as_leaf,
714			)?;
715		}
716
717		if !operation.aux.is_empty() {
718			self.blockchain.write_aux(operation.aux);
719		}
720
721		if let Some(set_head) = operation.set_head {
722			self.blockchain.set_head(set_head)?;
723		}
724
725		Ok(())
726	}
727
728	fn finalize_block(
729		&self,
730		hash: Block::Hash,
731		justification: Option<Justification>,
732	) -> sp_blockchain::Result<()> {
733		self.blockchain.finalize_header(hash, justification)
734	}
735
736	fn append_justification(
737		&self,
738		hash: Block::Hash,
739		justification: Justification,
740	) -> sp_blockchain::Result<()> {
741		self.blockchain.append_justification(hash, justification)
742	}
743
744	fn blockchain(&self) -> &Self::Blockchain {
745		&self.blockchain
746	}
747
748	fn usage_info(&self) -> Option<UsageInfo> {
749		None
750	}
751
752	fn offchain_storage(&self) -> Option<Self::OffchainStorage> {
753		None
754	}
755
756	fn state_at(
757		&self,
758		hash: Block::Hash,
759		_trie_cache_context: TrieCacheContext,
760	) -> sp_blockchain::Result<Self::State> {
761		if hash == Default::default() {
762			return Ok(Self::State::default());
763		}
764
765		self.states
766			.read()
767			.get(&hash)
768			.cloned()
769			.ok_or_else(|| sp_blockchain::Error::UnknownBlock(format!("{}", hash)))
770	}
771
772	fn revert(
773		&self,
774		_n: NumberFor<Block>,
775		_revert_finalized: bool,
776	) -> sp_blockchain::Result<(NumberFor<Block>, HashSet<Block::Hash>)> {
777		Ok((Zero::zero(), HashSet::new()))
778	}
779
780	fn remove_leaf_block(&self, _hash: Block::Hash) -> sp_blockchain::Result<()> {
781		Ok(())
782	}
783
784	fn get_import_lock(&self) -> &RwLock<()> {
785		&self.import_lock
786	}
787
788	fn requires_full_sync(&self) -> bool {
789		false
790	}
791
792	fn pin_block(&self, hash: <Block as BlockT>::Hash) -> blockchain::Result<()> {
793		let mut blocks = self.pinned_blocks.write();
794		*blocks.entry(hash).or_default() += 1;
795		Ok(())
796	}
797
798	fn unpin_block(&self, hash: <Block as BlockT>::Hash) {
799		let mut blocks = self.pinned_blocks.write();
800		blocks.entry(hash).and_modify(|counter| *counter -= 1).or_insert(-1);
801	}
802}
803
804impl<Block: BlockT> backend::LocalBackend<Block> for Backend<Block> {}
805
806/// Check that genesis storage is valid.
807pub fn check_genesis_storage(storage: &Storage) -> sp_blockchain::Result<()> {
808	if storage.top.iter().any(|(k, _)| well_known_keys::is_child_storage_key(k)) {
809		return Err(sp_blockchain::Error::InvalidState);
810	}
811
812	if storage
813		.children_default
814		.keys()
815		.any(|child_key| !well_known_keys::is_child_storage_key(child_key))
816	{
817		return Err(sp_blockchain::Error::InvalidState);
818	}
819
820	Ok(())
821}
822
823#[cfg(test)]
824mod tests {
825	use crate::{in_mem::Blockchain, NewBlockState};
826	use sp_blockchain::Backend;
827	use sp_runtime::{traits::Header as HeaderT, ConsensusEngineId, Justifications};
828	use substrate_test_runtime::{Block, Header, H256};
829
830	pub const ID1: ConsensusEngineId = *b"TST1";
831	pub const ID2: ConsensusEngineId = *b"TST2";
832
833	fn header(number: u64) -> Header {
834		let parent_hash = match number {
835			0 => Default::default(),
836			_ => header(number - 1).hash(),
837		};
838		Header::new(
839			number,
840			H256::from_low_u64_be(0),
841			H256::from_low_u64_be(0),
842			parent_hash,
843			Default::default(),
844		)
845	}
846
847	fn test_blockchain() -> Blockchain<Block> {
848		let blockchain = Blockchain::<Block>::new();
849		let just0 = Some(Justifications::from((ID1, vec![0])));
850		let just1 = Some(Justifications::from((ID1, vec![1])));
851		let just2 = None;
852		let just3 = Some(Justifications::from((ID1, vec![3])));
853		blockchain
854			.insert(header(0).hash(), header(0), just0, None, NewBlockState::Final, true)
855			.unwrap();
856		blockchain
857			.insert(header(1).hash(), header(1), just1, None, NewBlockState::Final, true)
858			.unwrap();
859		blockchain
860			.insert(header(2).hash(), header(2), just2, None, NewBlockState::Best, true)
861			.unwrap();
862		blockchain
863			.insert(header(3).hash(), header(3), just3, None, NewBlockState::Final, true)
864			.unwrap();
865		blockchain
866	}
867
868	#[test]
869	fn append_and_retrieve_justifications() {
870		let blockchain = test_blockchain();
871		let last_finalized = blockchain.last_finalized().unwrap();
872
873		blockchain.append_justification(last_finalized, (ID2, vec![4])).unwrap();
874		let justifications = {
875			let mut just = Justifications::from((ID1, vec![3]));
876			just.append((ID2, vec![4]));
877			just
878		};
879		assert_eq!(blockchain.justifications(last_finalized).unwrap(), Some(justifications));
880	}
881
882	#[test]
883	fn store_duplicate_justifications_is_forbidden() {
884		let blockchain = test_blockchain();
885		let last_finalized = blockchain.last_finalized().unwrap();
886
887		blockchain.append_justification(last_finalized, (ID2, vec![0])).unwrap();
888		assert!(matches!(
889			blockchain.append_justification(last_finalized, (ID2, vec![1])),
890			Err(sp_blockchain::Error::BadJustification(_)),
891		));
892	}
893}