pezsc_client_api/
in_mem.rs

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