1use 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#[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 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 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 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 pub fn blocks_count(&self) -> usize {
192 self.storage.read().blocks.len()
193 }
194
195 pub fn equals_to(&self, other: &Self) -> bool {
197 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 pub fn canon_equals_to(&self, other: &Self) -> bool {
206 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 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 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 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 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 }
396 fn remove_header_metadata(&self, _hash: Block::Hash) {
397 }
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
473pub 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 justifications: Option<Justifications>,
524 state: NewBlockState,
525 register_as_leaf: bool,
526 ) -> sp_blockchain::Result<()> {
527 assert!(self.pending_block.is_none(), "Only one block per operation is allowed");
528 self.pending_block = Some(PendingBlock {
529 block: StoredBlock::new(header, body, justifications),
530 state,
531 register_as_leaf,
532 });
533 Ok(())
534 }
535
536 fn update_db_storage(
537 &mut self,
538 update: BackendTransaction<HashingFor<Block>>,
539 ) -> sp_blockchain::Result<()> {
540 self.new_state = Some(update);
541 Ok(())
542 }
543
544 fn set_genesis_state(
545 &mut self,
546 storage: Storage,
547 commit: bool,
548 state_version: StateVersion,
549 ) -> sp_blockchain::Result<Block::Hash> {
550 self.apply_storage(storage, commit, state_version)
551 }
552
553 fn reset_storage(
554 &mut self,
555 storage: Storage,
556 state_version: StateVersion,
557 ) -> sp_blockchain::Result<Block::Hash> {
558 self.apply_storage(storage, true, state_version)
559 }
560
561 fn insert_aux<I>(&mut self, ops: I) -> sp_blockchain::Result<()>
562 where
563 I: IntoIterator<Item = (Vec<u8>, Option<Vec<u8>>)>,
564 {
565 self.aux.append(&mut ops.into_iter().collect());
566 Ok(())
567 }
568
569 fn update_storage(
570 &mut self,
571 _update: StorageCollection,
572 _child_update: ChildStorageCollection,
573 ) -> sp_blockchain::Result<()> {
574 Ok(())
575 }
576
577 fn mark_finalized(
578 &mut self,
579 hash: Block::Hash,
580 justification: Option<Justification>,
581 ) -> sp_blockchain::Result<()> {
582 self.finalized_blocks.push((hash, justification));
583 Ok(())
584 }
585
586 fn mark_head(&mut self, hash: Block::Hash) -> sp_blockchain::Result<()> {
587 assert!(self.pending_block.is_none(), "Only one set block per operation is allowed");
588 self.set_head = Some(hash);
589 Ok(())
590 }
591
592 fn update_transaction_index(
593 &mut self,
594 _index: Vec<IndexOperation>,
595 ) -> sp_blockchain::Result<()> {
596 Ok(())
597 }
598
599 fn set_renew_payloads(
600 &mut self,
601 _payloads: HashMap<H256, Vec<u8>>,
602 ) -> sp_blockchain::Result<()> {
603 Ok(())
604 }
605
606 fn set_create_gap(&mut self, _create_gap: bool) {}
607}
608
609pub struct Backend<Block: BlockT> {
614 states: RwLock<HashMap<Block::Hash, InMemoryBackend<HashingFor<Block>>>>,
615 blockchain: Blockchain<Block>,
616 import_lock: RwLock<()>,
617 pinned_blocks: RwLock<HashMap<Block::Hash, i64>>,
618}
619
620impl<Block: BlockT> Backend<Block> {
621 pub fn new() -> Self {
627 Backend {
628 states: RwLock::new(HashMap::new()),
629 blockchain: Blockchain::new(),
630 import_lock: Default::default(),
631 pinned_blocks: Default::default(),
632 }
633 }
634
635 pub fn pin_refs(&self, hash: &<Block as BlockT>::Hash) -> Option<i64> {
641 let blocks = self.pinned_blocks.read();
642 blocks.get(hash).map(|value| *value)
643 }
644}
645
646impl<Block: BlockT> backend::AuxStore for Backend<Block> {
647 fn insert_aux<
648 'a,
649 'b: 'a,
650 'c: 'a,
651 I: IntoIterator<Item = &'a (&'c [u8], &'c [u8])>,
652 D: IntoIterator<Item = &'a &'b [u8]>,
653 >(
654 &self,
655 insert: I,
656 delete: D,
657 ) -> sp_blockchain::Result<()> {
658 self.blockchain.insert_aux(insert, delete)
659 }
660
661 fn get_aux(&self, key: &[u8]) -> sp_blockchain::Result<Option<Vec<u8>>> {
662 self.blockchain.get_aux(key)
663 }
664}
665
666impl<Block: BlockT> backend::Backend<Block> for Backend<Block> {
667 type BlockImportOperation = BlockImportOperation<Block>;
668 type Blockchain = Blockchain<Block>;
669 type State = InMemoryBackend<HashingFor<Block>>;
670 type OffchainStorage = OffchainStorage;
671
672 fn begin_operation(&self) -> sp_blockchain::Result<Self::BlockImportOperation> {
673 let old_state = self.state_at(Default::default(), TrieCacheContext::Untrusted)?;
674 Ok(BlockImportOperation {
675 pending_block: None,
676 old_state,
677 new_state: None,
678 aux: Default::default(),
679 finalized_blocks: Default::default(),
680 set_head: None,
681 })
682 }
683
684 fn begin_state_operation(
685 &self,
686 operation: &mut Self::BlockImportOperation,
687 block: Block::Hash,
688 ) -> sp_blockchain::Result<()> {
689 operation.old_state = self.state_at(block, TrieCacheContext::Untrusted)?;
690 Ok(())
691 }
692
693 fn commit_operation(&self, operation: Self::BlockImportOperation) -> sp_blockchain::Result<()> {
694 if !operation.finalized_blocks.is_empty() {
695 for (block, justification) in operation.finalized_blocks {
696 self.blockchain.finalize_header(block, justification)?;
697 }
698 }
699
700 if let Some(pending_block) = operation.pending_block {
701 let old_state = &operation.old_state;
702 let (header, body, justification) = pending_block.block.into_inner();
703
704 let hash = header.hash();
705
706 let new_state = match operation.new_state {
707 Some(state) => old_state.update_backend(*header.state_root(), state),
708 None => old_state.clone(),
709 };
710
711 self.states.write().insert(hash, new_state);
712
713 self.blockchain.insert(
714 hash,
715 header,
716 justification,
717 body,
718 pending_block.state,
719 pending_block.register_as_leaf,
720 )?;
721 }
722
723 if !operation.aux.is_empty() {
724 self.blockchain.write_aux(operation.aux);
725 }
726
727 if let Some(set_head) = operation.set_head {
728 self.blockchain.set_head(set_head)?;
729 }
730
731 Ok(())
732 }
733
734 fn finalize_block(
735 &self,
736 hash: Block::Hash,
737 justification: Option<Justification>,
738 ) -> sp_blockchain::Result<()> {
739 self.blockchain.finalize_header(hash, justification)
740 }
741
742 fn append_justification(
743 &self,
744 hash: Block::Hash,
745 justification: Justification,
746 ) -> sp_blockchain::Result<()> {
747 self.blockchain.append_justification(hash, justification)
748 }
749
750 fn blockchain(&self) -> &Self::Blockchain {
751 &self.blockchain
752 }
753
754 fn usage_info(&self) -> Option<UsageInfo> {
755 None
756 }
757
758 fn offchain_storage(&self) -> Option<Self::OffchainStorage> {
759 None
760 }
761
762 fn state_at(
763 &self,
764 hash: Block::Hash,
765 _trie_cache_context: TrieCacheContext,
766 ) -> sp_blockchain::Result<Self::State> {
767 if hash == Default::default() {
768 return Ok(Self::State::default());
769 }
770
771 self.states
772 .read()
773 .get(&hash)
774 .cloned()
775 .ok_or_else(|| sp_blockchain::Error::UnknownBlock(format!("{}", hash)))
776 }
777
778 fn revert(
779 &self,
780 _n: NumberFor<Block>,
781 _revert_finalized: bool,
782 ) -> sp_blockchain::Result<(NumberFor<Block>, HashSet<Block::Hash>)> {
783 Ok((Zero::zero(), HashSet::new()))
784 }
785
786 fn remove_leaf_block(&self, _hash: Block::Hash) -> sp_blockchain::Result<()> {
787 Ok(())
788 }
789
790 fn get_import_lock(&self) -> &RwLock<()> {
791 &self.import_lock
792 }
793
794 fn requires_full_sync(&self) -> bool {
795 false
796 }
797
798 fn pin_block(&self, hash: <Block as BlockT>::Hash) -> blockchain::Result<()> {
799 let mut blocks = self.pinned_blocks.write();
800 *blocks.entry(hash).or_default() += 1;
801 Ok(())
802 }
803
804 fn unpin_block(&self, hash: <Block as BlockT>::Hash) {
805 let mut blocks = self.pinned_blocks.write();
806 blocks.entry(hash).and_modify(|counter| *counter -= 1).or_insert(-1);
807 }
808}
809
810impl<Block: BlockT> backend::LocalBackend<Block> for Backend<Block> {}
811
812pub fn check_genesis_storage(storage: &Storage) -> sp_blockchain::Result<()> {
814 if storage.top.iter().any(|(k, _)| well_known_keys::is_child_storage_key(k)) {
815 return Err(sp_blockchain::Error::InvalidState);
816 }
817
818 if storage
819 .children_default
820 .keys()
821 .any(|child_key| !well_known_keys::is_child_storage_key(child_key))
822 {
823 return Err(sp_blockchain::Error::InvalidState);
824 }
825
826 Ok(())
827}
828
829#[cfg(test)]
830mod tests {
831 use crate::{in_mem::Blockchain, NewBlockState};
832 use sp_blockchain::Backend;
833 use sp_runtime::{traits::Header as HeaderT, ConsensusEngineId, Justifications};
834 use substrate_test_runtime::{Block, Header, H256};
835
836 pub const ID1: ConsensusEngineId = *b"TST1";
837 pub const ID2: ConsensusEngineId = *b"TST2";
838
839 fn header(number: u64) -> Header {
840 let parent_hash = match number {
841 0 => Default::default(),
842 _ => header(number - 1).hash(),
843 };
844 Header::new(
845 number,
846 H256::from_low_u64_be(0),
847 H256::from_low_u64_be(0),
848 parent_hash,
849 Default::default(),
850 )
851 }
852
853 fn test_blockchain() -> Blockchain<Block> {
854 let blockchain = Blockchain::<Block>::new();
855 let just0 = Some(Justifications::from((ID1, vec![0])));
856 let just1 = Some(Justifications::from((ID1, vec![1])));
857 let just2 = None;
858 let just3 = Some(Justifications::from((ID1, vec![3])));
859 blockchain
860 .insert(header(0).hash(), header(0), just0, None, NewBlockState::Final, true)
861 .unwrap();
862 blockchain
863 .insert(header(1).hash(), header(1), just1, None, NewBlockState::Final, true)
864 .unwrap();
865 blockchain
866 .insert(header(2).hash(), header(2), just2, None, NewBlockState::Best, true)
867 .unwrap();
868 blockchain
869 .insert(header(3).hash(), header(3), just3, None, NewBlockState::Final, true)
870 .unwrap();
871 blockchain
872 }
873
874 #[test]
875 fn append_and_retrieve_justifications() {
876 let blockchain = test_blockchain();
877 let last_finalized = blockchain.last_finalized().unwrap();
878
879 blockchain.append_justification(last_finalized, (ID2, vec![4])).unwrap();
880 let justifications = {
881 let mut just = Justifications::from((ID1, vec![3]));
882 just.append((ID2, vec![4]));
883 just
884 };
885 assert_eq!(blockchain.justifications(last_finalized).unwrap(), Some(justifications));
886 }
887
888 #[test]
889 fn store_duplicate_justifications_is_forbidden() {
890 let blockchain = test_blockchain();
891 let last_finalized = blockchain.last_finalized().unwrap();
892
893 blockchain.append_justification(last_finalized, (ID2, vec![0])).unwrap();
894 assert!(matches!(
895 blockchain.append_justification(last_finalized, (ID2, vec![1])),
896 Err(sp_blockchain::Error::BadJustification(_)),
897 ));
898 }
899}