Skip to main content

soil_client/client_api/
in_mem.rs

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