pallet-mmr: move offchain logic to client-side gadget (#12753)

* Move MMR utils methods from pallet to primitives

Signed-off-by: Serban Iorga <serban@parity.io>

* Add method to MmrApi

* Move forks expanding logic from babe to primitives

* Implement MMR gadget

* Remove prunning logic from the MMR pallet

* Code review changes: 1st iteration

* Replace MaybeCanonEngine with CanonEngineBuilder

* fix mmr_leaves_count() for kitchen sink demo

* Update client/merkle-mountain-range/src/canon_engine.rs

Co-authored-by: Adrian Catangiu <adrian@parity.io>

* Code review changes: 2nd iteration

* fix INDEXING_PREFIX

* impl review comments

* add documentation and minor rename

Signed-off-by: Serban Iorga <serban@parity.io>
Co-authored-by: Adrian Catangiu <adrian@parity.io>
This commit is contained in:
Serban Iorga
2022-11-29 16:39:52 +02:00
committed by GitHub
parent d56214c21f
commit ff439ee335
21 changed files with 1161 additions and 561 deletions
+73 -1
View File
@@ -21,9 +21,10 @@ use log::warn;
use parking_lot::RwLock;
use sp_runtime::{
generic::BlockId,
traits::{Block as BlockT, Header as HeaderT, NumberFor},
traits::{Block as BlockT, Header as HeaderT, NumberFor, Saturating},
Justifications,
};
use std::collections::btree_set::BTreeSet;
use crate::header_metadata::HeaderMetadata;
@@ -84,6 +85,77 @@ pub trait HeaderBackend<Block: BlockT>: Send + Sync {
}
}
/// Handles stale forks.
pub trait ForkBackend<Block: BlockT>:
HeaderMetadata<Block> + HeaderBackend<Block> + Send + Sync
{
/// Best effort to get all the header hashes that are part of the provided forks
/// starting only from the fork heads.
///
/// The function tries to reconstruct the route from the fork head to the canonical chain.
/// If any of the hashes on the route can't be found in the db, the function won't be able
/// to reconstruct the route anymore. In this case it will give up expanding the current fork,
/// move on to the next ones and at the end it will return an error that also contains
/// the partially expanded forks.
fn expand_forks(
&self,
fork_heads: &[Block::Hash],
) -> std::result::Result<BTreeSet<Block::Hash>, (BTreeSet<Block::Hash>, Error)> {
let mut missing_blocks = vec![];
let mut expanded_forks = BTreeSet::new();
for fork_head in fork_heads {
let mut route_head = *fork_head;
// Insert stale blocks hashes until canonical chain is reached.
// If we reach a block that is already part of the `expanded_forks` we can stop
// processing the fork.
while expanded_forks.insert(route_head) {
match self.header_metadata(route_head) {
Ok(meta) => {
// If the parent is part of the canonical chain or there doesn't exist a
// block hash for the parent number (bug?!), we can abort adding blocks.
let parent_number = meta.number.saturating_sub(1u32.into());
match self.hash(parent_number) {
Ok(Some(parent_hash)) =>
if parent_hash == meta.parent {
break
},
Ok(None) | Err(_) => {
missing_blocks.push(BlockId::<Block>::Number(parent_number));
break
},
}
route_head = meta.parent;
},
Err(_e) => {
missing_blocks.push(BlockId::<Block>::Hash(route_head));
break
},
}
}
}
if !missing_blocks.is_empty() {
return Err((
expanded_forks,
Error::UnknownBlocks(format!(
"Missing stale headers {:?} while expanding forks {:?}.",
fork_heads, missing_blocks
)),
))
}
Ok(expanded_forks)
}
}
impl<Block, T> ForkBackend<Block> for T
where
Block: BlockT,
T: HeaderMetadata<Block> + HeaderBackend<Block> + Send + Sync,
{
}
/// Blockchain database backend. Does not perform any validation.
pub trait Backend<Block: BlockT>:
HeaderBackend<Block> + HeaderMetadata<Block, Error = Error>
@@ -59,6 +59,9 @@ pub enum Error {
#[error("UnknownBlock: {0}")]
UnknownBlock(String),
#[error("UnknownBlocks: {0}")]
UnknownBlocks(String),
#[error(transparent)]
ApplyExtrinsicFailed(#[from] ApplyExtrinsicFailed),