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