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