diff --git a/finality-aleph/src/sync/mod.rs b/finality-aleph/src/sync/mod.rs index 801e0b7902..23e66bd53e 100644 --- a/finality-aleph/src/sync/mod.rs +++ b/finality-aleph/src/sync/mod.rs @@ -91,12 +91,17 @@ pub enum BlockStatus { /// The knowledge about the chain status. pub trait ChainStatus { + type Error: Display; + /// The status of the block. - fn status_of(&self, id: ::Identifier) -> BlockStatus; + fn status_of( + &self, + id: ::Identifier, + ) -> Result, Self::Error>; /// The header of the best block. - fn best_block(&self) -> J::Header; + fn best_block(&self) -> Result; /// The justification of the top finalized block. - fn top_finalized(&self) -> J; + fn top_finalized(&self) -> Result; } diff --git a/finality-aleph/src/sync/substrate/chain_status.rs b/finality-aleph/src/sync/substrate/chain_status.rs new file mode 100644 index 0000000000..74958dba6f --- /dev/null +++ b/finality-aleph/src/sync/substrate/chain_status.rs @@ -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 { + MissingHash(B::Hash), + MissingJustification(B::Hash), + Client(ClientError), +} + +impl Display for Error { + 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 From for Error { + fn from(value: ClientError) -> Self { + Error::Client(value) + } +} + +/// Substrate implementation of ChainStatus trait +pub struct SubstrateChainStatus +where + BE: Backend, + B: BlockT, + B::Header: SubstrateHeader, +{ + client: BE, + _phantom: PhantomData, +} + +impl SubstrateChainStatus +where + BE: Backend, + B: BlockT, + B::Header: SubstrateHeader, +{ + fn header(&self, hash: B::Hash) -> Result, ClientError> { + let id = SubstrateBlockId::::Hash(hash); + self.client.header(id) + } + + fn justification(&self, hash: B::Hash) -> Result, ClientError> { + let id = SubstrateBlockId::::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 ChainStatus> for SubstrateChainStatus +where + BE: Backend, + B: BlockT, + B::Header: SubstrateHeader, +{ + type Error = Error; + + fn status_of( + &self, + id: ::Identifier, + ) -> Result>, 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 { + let best_hash = self.best_hash(); + + self.header(best_hash)?.ok_or(Error::MissingHash(best_hash)) + } + + fn top_finalized(&self) -> Result, 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, + }) + } +} diff --git a/finality-aleph/src/sync/substrate/mod.rs b/finality-aleph/src/sync/substrate/mod.rs index 56cd111be1..049ad41c44 100644 --- a/finality-aleph/src/sync/substrate/mod.rs +++ b/finality-aleph/src/sync/substrate/mod.rs @@ -8,6 +8,7 @@ use crate::{ AlephJustification, }; +mod chain_status; mod status_notifier; #[derive(Clone, Debug, PartialEq, Eq)]