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 _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
603pub 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 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 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
806pub 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}