Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A0-1823: add substrate specific chain status #844

Merged
merged 10 commits into from
Jan 9, 2023
11 changes: 8 additions & 3 deletions finality-aleph/src/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,17 @@ pub enum BlockStatus<J: Justification> {

/// The knowledge about the chain status.
pub trait ChainStatus<J: Justification> {
type Error: Display;

/// The status of the block.
fn status_of(&self, id: <J::Header as Header>::Identifier) -> BlockStatus<J>;
fn status_of(
&self,
id: <J::Header as Header>::Identifier,
) -> Result<BlockStatus<J>, Self::Error>;

/// The header of the best block.
fn best_block(&self) -> J::Header;
fn best_block(&self) -> Result<J::Header, Self::Error>;

/// The justification of the top finalized block.
fn top_finalized(&self) -> J;
fn top_finalized(&self) -> Result<J, Self::Error>;
}
162 changes: 162 additions & 0 deletions finality-aleph/src/sync/substrate/chain_status.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
use std::{
fmt::{Display, Error as FmtError, Formatter},
marker::PhantomData,
};

use aleph_primitives::{BlockNumber, ALEPH_ENGINE_ID};
use log::warn;
use sp_blockchain::{Backend, Error as ClientError};
use sp_runtime::{
generic::BlockId as SubstrateBlockId,
traits::{Block as BlockT, Header as SubstrateHeader},
};

use crate::{
justification::backwards_compatible_decode,
sync::{substrate::Justification, BlockStatus, ChainStatus, Header, LOG_TARGET},
AlephJustification,
};

/// What can go wrong when checking chain status
#[derive(Debug)]
pub enum Error<B: BlockT> {
MissingHash(B::Hash),
MissingJustification(B::Hash),
Client(ClientError),
}

impl<B: BlockT> Display for Error<B> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
use Error::*;
match self {
MissingHash(hash) => {
write!(
f,
"data availability problem: no block for existing hash {:?}",
hash
)
}
MissingJustification(hash) => {
write!(
f,
"data availability problem: no justification for finalized block with hash {:?}",
hash
)
}
Client(e) => {
write!(f, "substrate client error {}", e)
}
}
}
}

impl<B: BlockT> From<ClientError> for Error<B> {
fn from(value: ClientError) -> Self {
Error::Client(value)
}
}

/// Substrate implementation of ChainStatus trait
pub struct SubstrateChainStatus<B, BE>
where
BE: Backend<B>,
B: BlockT,
B::Header: SubstrateHeader<Number = BlockNumber>,
{
client: BE,
_phantom: PhantomData<B>,
}

impl<B, BE> SubstrateChainStatus<B, BE>
where
BE: Backend<B>,
B: BlockT,
B::Header: SubstrateHeader<Number = BlockNumber>,
{
fn header(&self, hash: B::Hash) -> Result<Option<B::Header>, ClientError> {
let id = SubstrateBlockId::<B>::Hash(hash);
self.client.header(id)
}

fn justification(&self, hash: B::Hash) -> Result<Option<AlephJustification>, ClientError> {
let id = SubstrateBlockId::<B>::Hash(hash);
let justification = match self
.client
.justifications(id)?
.and_then(|j| j.into_justification(ALEPH_ENGINE_ID))
{
Some(justification) => justification,
None => return Ok(None),
};

match backwards_compatible_decode(justification) {
Ok(justification) => Ok(Some(justification)),
// This should not happen, as we only import correctly encoded justification.
Err(e) => {
warn!(
target: LOG_TARGET,
"Could not decode stored justification for block {:?}: {}", hash, e
);
Ok(None)
}
}
}

fn best_hash(&self) -> B::Hash {
self.client.info().best_hash
}

fn finalized_hash(&self) -> B::Hash {
self.client.info().finalized_hash
}
}

impl<B, BE> ChainStatus<Justification<B::Header>> for SubstrateChainStatus<B, BE>
where
BE: Backend<B>,
B: BlockT,
B::Header: SubstrateHeader<Number = BlockNumber>,
{
type Error = Error<B>;

fn status_of(
&self,
id: <B::Header as Header>::Identifier,
) -> Result<BlockStatus<Justification<B::Header>>, Self::Error> {
let header = match self.header(id.hash)? {
Some(header) => header,
None => return Ok(BlockStatus::Unknown),
};

if let Some(raw_justification) = self.justification(id.hash)? {
Ok(BlockStatus::Justified(Justification {
header,
raw_justification,
}))
} else {
Ok(BlockStatus::Present(header))
}
}

fn best_block(&self) -> Result<B::Header, Self::Error> {
let best_hash = self.best_hash();

self.header(best_hash)?.ok_or(Error::MissingHash(best_hash))
}

fn top_finalized(&self) -> Result<Justification<B::Header>, Self::Error> {
let finalized_hash = self.finalized_hash();

let header = self
.header(finalized_hash)?
.ok_or(Error::MissingHash(finalized_hash))?;
let raw_justification = self
.justification(finalized_hash)?
.ok_or(Error::MissingJustification(finalized_hash))?;

Ok(Justification {
header,
raw_justification,
})
}
}
1 change: 1 addition & 0 deletions finality-aleph/src/sync/substrate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::{
AlephJustification,
};

mod chain_status;
mod status_notifier;

#[derive(Clone, Debug, PartialEq, Eq)]
Expand Down