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