Backport: Implement basic equivocations detection loop (#2375)

* Implement basic equivocations detection loop (#2367)

* FinalityProofsBuf adjustments

- store a Vec<FinalityProof>
- transform prune `buf_limit` to Option

* FinalityProof: add target_header_hash()

* Target client: implement best_synced_header_hash()

* Implement first version of the equivocations detection loop

* Address code review comments

* Leftover

* polkadot-staging adjustments
This commit is contained in:
Serban Iorga
2023-08-23 19:31:17 +03:00
committed by Bastian Köcher
parent cc3bbc690b
commit 588508acd4
18 changed files with 572 additions and 79 deletions
@@ -17,14 +17,16 @@
//! Default generic implementation of equivocation source for basic Substrate client.
use crate::{
equivocation::{EquivocationDetectionPipelineAdapter, SubstrateEquivocationDetectionPipeline},
finality_base::engine::Engine,
equivocation::{
EquivocationDetectionPipelineAdapter, FinalityProoffOf, FinalityVerificationContextfOf,
SubstrateEquivocationDetectionPipeline,
},
finality_base::{best_synced_header_id, engine::Engine},
};
use crate::equivocation::{FinalityProoffOf, FinalityVerificationContextfOf};
use async_trait::async_trait;
use bp_header_chain::HeaderFinalityInfo;
use bp_runtime::BlockNumberOf;
use bp_runtime::{BlockNumberOf, HashOf};
use equivocation_detector::TargetClient;
use relay_substrate_client::{Client, Error};
use relay_utils::relay_loop::Client as RelayClient;
@@ -59,6 +61,24 @@ impl<P: SubstrateEquivocationDetectionPipeline> RelayClient for SubstrateEquivoc
impl<P: SubstrateEquivocationDetectionPipeline>
TargetClient<EquivocationDetectionPipelineAdapter<P>> for SubstrateEquivocationTarget<P>
{
async fn best_finalized_header_number(
&self,
) -> Result<BlockNumberOf<P::TargetChain>, Self::Error> {
self.client.best_finalized_header_number().await
}
async fn best_synced_header_hash(
&self,
at: BlockNumberOf<P::TargetChain>,
) -> Result<Option<HashOf<P::SourceChain>>, Self::Error> {
Ok(best_synced_header_id::<P::SourceChain, P::TargetChain>(
&self.client,
self.client.header_by_number(at).await?.hash(),
)
.await?
.map(|id| id.hash()))
}
async fn finality_verification_context(
&self,
at: BlockNumberOf<P::TargetChain>,
@@ -20,7 +20,7 @@ use crate::{
finality::{
FinalitySyncPipelineAdapter, SubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline,
},
finality_base::{engine::Engine, SubstrateFinalityProof},
finality_base::{best_synced_header_id, engine::Engine, SubstrateFinalityProof},
TransactionParams,
};
@@ -31,6 +31,7 @@ use relay_substrate_client::{
TransactionTracker, UnsignedTransaction,
};
use relay_utils::relay_loop::Client as RelayClient;
use sp_runtime::traits::Header;
/// Substrate client as Substrate finality target.
pub struct SubstrateFinalityTarget<P: SubstrateFinalitySyncPipeline> {
@@ -94,12 +95,11 @@ impl<P: SubstrateFinalitySyncPipeline> TargetClient<FinalitySyncPipelineAdapter<
// we can't relay finality if bridge pallet at target chain is halted
self.ensure_pallet_active().await?;
Ok(crate::messages_source::read_client_state::<P::TargetChain, P::SourceChain>(
Ok(best_synced_header_id::<P::SourceChain, P::TargetChain>(
&self.client,
None,
self.client.best_header().await?.hash(),
)
.await?
.best_finalized_peer_at_best_self
.ok_or(Error::BridgePalletIsNotInitialized)?)
}
@@ -36,7 +36,7 @@ use relay_substrate_client::{
use sp_consensus_grandpa::{AuthorityList as GrandpaAuthoritiesSet, GRANDPA_ENGINE_ID};
use sp_core::{storage::StorageKey, Bytes};
use sp_runtime::{scale_info::TypeInfo, traits::Header, ConsensusEngineId};
use std::marker::PhantomData;
use std::{fmt::Debug, marker::PhantomData};
/// Finality engine, used by the Substrate chain.
#[async_trait]
@@ -48,11 +48,11 @@ pub trait Engine<C: Chain>: Send {
/// Type of Finality RPC client used by this engine.
type FinalityClient: SubstrateFinalityClient<C>;
/// Type of finality proofs, used by consensus engine.
type FinalityProof: FinalityProof<BlockNumberOf<C>> + Decode + Encode;
type FinalityProof: FinalityProof<HashOf<C>, BlockNumberOf<C>> + Decode + Encode;
/// The context needed for verifying finality proofs.
type FinalityVerificationContext;
type FinalityVerificationContext: Send;
/// The type of the equivocation proof used by the consensus engine.
type EquivocationProof: Send + Sync;
type EquivocationProof: Clone + Debug + Send + Sync;
/// The equivocations finder.
type EquivocationsFinder: FindEquivocations<
Self::FinalityProof,
@@ -62,7 +62,7 @@ pub trait Engine<C: Chain>: Send {
/// The type of the key owner proof used by the consensus engine.
type KeyOwnerProof: Send;
/// Type of bridge pallet initialization data.
type InitializationData: std::fmt::Debug + Send + Sync + 'static;
type InitializationData: Debug + Send + Sync + 'static;
/// Type of bridge pallet operating mode.
type OperatingMode: OperatingMode + 'static;
@@ -20,7 +20,9 @@
pub mod engine;
use crate::finality_base::engine::Engine;
use async_trait::async_trait;
use bp_runtime::{HashOf, HeaderIdOf};
use codec::Decode;
use futures::{stream::unfold, Stream, StreamExt};
use relay_substrate_client::{Chain, Client, Error};
@@ -85,3 +87,21 @@ pub async fn finality_proofs<P: SubstrateFinalityPipeline>(
)
.boxed())
}
/// Get the id of the best `SourceChain` header known to the `TargetChain` at the provided
/// target block using the exposed runtime API method.
///
/// The runtime API method should be `<TargetChain>FinalityApi::best_finalized()`.
pub async fn best_synced_header_id<SourceChain, TargetChain>(
target_client: &Client<TargetChain>,
at: HashOf<TargetChain>,
) -> Result<Option<HeaderIdOf<SourceChain>>, Error>
where
SourceChain: Chain,
TargetChain: Chain,
{
// now let's read id of best finalized peer header at our best finalized block
target_client
.typed_state_call(SourceChain::BEST_FINALIZED_HEADER_ID_METHOD.into(), (), Some(at))
.await
}
@@ -19,6 +19,7 @@
//! `<BridgedName>` chain.
use crate::{
finality_base::best_synced_header_id,
messages_lane::{
BatchProofTransaction, MessageLaneAdapter, ReceiveMessagesDeliveryProofCallBuilder,
SubstrateMessageLane,
@@ -428,11 +429,7 @@ where
// now let's read id of best finalized peer header at our best finalized block
let peer_on_self_best_finalized_id =
best_finalized_peer_header_at_self::<SelfChain, PeerChain>(
self_client,
self_best_id.hash(),
)
.await?;
best_synced_header_id::<PeerChain, SelfChain>(self_client, self_best_id.hash()).await?;
// read actual header, matching the `peer_on_self_best_finalized_id` from the peer chain
let actual_peer_on_self_best_finalized_id =