Skip to content

Commit

Permalink
A0-1823: add substrate specific chain status (#844)
Browse files Browse the repository at this point in the history
* add substrate chain status
  • Loading branch information
maciejnems authored Jan 9, 2023
1 parent 0095c94 commit 969dce4
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 3 deletions.
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

0 comments on commit 969dce4

Please sign in to comment.