use log::warn;
use parking_lot::RwLock;
use sp_runtime::{
generic::BlockId,
traits::{Block as BlockT, Header as HeaderT, NumberFor, Saturating},
Justifications,
};
use std::collections::btree_set::BTreeSet;
use crate::header_metadata::HeaderMetadata;
use crate::error::{Error, Result};
pub trait HeaderBackend<Block: BlockT>: Send + Sync {
fn header(&self, hash: Block::Hash) -> Result<Option<Block::Header>>;
fn info(&self) -> Info<Block>;
fn status(&self, hash: Block::Hash) -> Result<BlockStatus>;
fn number(
&self,
hash: Block::Hash,
) -> Result<Option<<<Block as BlockT>::Header as HeaderT>::Number>>;
fn hash(&self, number: NumberFor<Block>) -> Result<Option<Block::Hash>>;
fn block_hash_from_id(&self, id: &BlockId<Block>) -> Result<Option<Block::Hash>> {
match *id {
BlockId::Hash(h) => Ok(Some(h)),
BlockId::Number(n) => self.hash(n),
}
}
fn block_number_from_id(&self, id: &BlockId<Block>) -> Result<Option<NumberFor<Block>>> {
match *id {
BlockId::Hash(h) => self.number(h),
BlockId::Number(n) => Ok(Some(n)),
}
}
fn expect_header(&self, hash: Block::Hash) -> Result<Block::Header> {
self.header(hash)?
.ok_or_else(|| Error::UnknownBlock(format!("Expect header: {}", hash)))
}
fn expect_block_number_from_id(&self, id: &BlockId<Block>) -> Result<NumberFor<Block>> {
self.block_number_from_id(id).and_then(|n| {
n.ok_or_else(|| Error::UnknownBlock(format!("Expect block number from id: {}", id)))
})
}
fn expect_block_hash_from_id(&self, id: &BlockId<Block>) -> Result<Block::Hash> {
self.block_hash_from_id(id).and_then(|h| {
h.ok_or_else(|| Error::UnknownBlock(format!("Expect block hash from id: {}", id)))
})
}
}
pub trait ForkBackend<Block: BlockT>:
HeaderMetadata<Block> + HeaderBackend<Block> + Send + Sync
{
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;
while expanded_forks.insert(route_head) {
match self.header_metadata(route_head) {
Ok(meta) => {
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,
{
}
pub trait Backend<Block: BlockT>:
HeaderBackend<Block> + HeaderMetadata<Block, Error = Error>
{
fn body(&self, hash: Block::Hash) -> Result<Option<Vec<<Block as BlockT>::Extrinsic>>>;
fn justifications(&self, hash: Block::Hash) -> Result<Option<Justifications>>;
fn last_finalized(&self) -> Result<Block::Hash>;
fn leaves(&self) -> Result<Vec<Block::Hash>>;
fn displaced_leaves_after_finalizing(
&self,
block_number: NumberFor<Block>,
) -> Result<Vec<Block::Hash>>;
fn children(&self, parent_hash: Block::Hash) -> Result<Vec<Block::Hash>>;
fn best_containing(
&self,
target_hash: Block::Hash,
maybe_max_number: Option<NumberFor<Block>>,
import_lock: &RwLock<()>,
) -> Result<Option<Block::Hash>> {
let target_header = {
match self.header(target_hash)? {
Some(x) => x,
None => return Ok(None),
}
};
if let Some(max_number) = maybe_max_number {
if target_header.number() > &max_number {
return Ok(None)
}
}
let leaves = {
let _import_guard = import_lock.read();
let info = self.info();
let maybe_canon_hash = self.hash(*target_header.number())?;
if maybe_canon_hash.as_ref() == Some(&target_hash) {
if let Some(max_number) = maybe_max_number {
if let Some(header) = self.hash(max_number)? {
return Ok(Some(header))
}
}
} else if info.finalized_number >= *target_header.number() {
return Ok(None)
}
self.leaves()?
};
for leaf_hash in leaves {
let mut current_hash = leaf_hash;
let mut best_hash = leaf_hash;
if let Some(max_number) = maybe_max_number {
loop {
let current_header = self
.header(current_hash)?
.ok_or_else(|| Error::MissingHeader(current_hash.to_string()))?;
if current_header.number() <= &max_number {
best_hash = current_header.hash();
break
}
current_hash = *current_header.parent_hash();
}
}
loop {
if current_hash == target_hash {
return Ok(Some(best_hash))
}
let current_header = self
.header(current_hash)?
.ok_or_else(|| Error::MissingHeader(current_hash.to_string()))?;
if current_header.number() < target_header.number() {
break
}
current_hash = *current_header.parent_hash();
}
}
warn!(
"Block {:?} exists in chain but not found when following all \
leaves backwards. Number limit = {:?}",
target_hash, maybe_max_number,
);
Ok(None)
}
fn indexed_transaction(&self, hash: Block::Hash) -> Result<Option<Vec<u8>>>;
fn has_indexed_transaction(&self, hash: Block::Hash) -> Result<bool> {
Ok(self.indexed_transaction(hash)?.is_some())
}
fn block_indexed_body(&self, hash: Block::Hash) -> Result<Option<Vec<Vec<u8>>>>;
}
#[derive(Debug, Eq, PartialEq)]
pub struct Info<Block: BlockT> {
pub best_hash: Block::Hash,
pub best_number: <<Block as BlockT>::Header as HeaderT>::Number,
pub genesis_hash: Block::Hash,
pub finalized_hash: Block::Hash,
pub finalized_number: <<Block as BlockT>::Header as HeaderT>::Number,
pub finalized_state: Option<(Block::Hash, <<Block as BlockT>::Header as HeaderT>::Number)>,
pub number_leaves: usize,
pub block_gap: Option<(NumberFor<Block>, NumberFor<Block>)>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BlockStatus {
InChain,
Unknown,
}
pub mod well_known_cache_keys {
pub type Id = sp_consensus::CacheKeyId;
pub const AUTHORITIES: Id = *b"auth";
pub const EPOCH: Id = *b"epch";
pub const CHANGES_TRIE_CONFIG: Id = *b"chtr";
}