mod source;
mod target;
use crate::{
equivocation::{source::SubstrateEquivocationSource, target::SubstrateEquivocationTarget},
finality_base::{engine::Engine, SubstrateFinalityPipeline, SubstrateFinalityProof},
TransactionParams,
};
use async_trait::async_trait;
use bp_runtime::{AccountIdOf, BlockNumberOf, HashOf};
use equivocation_detector::EquivocationDetectionPipeline;
use finality_relay::FinalityPipeline;
use pallet_grandpa::{Call as GrandpaCall, Config as GrandpaConfig};
use relay_substrate_client::{AccountKeyPairOf, CallOf, Chain, ChainWithTransactions, Client};
use relay_utils::metrics::MetricsParams;
use sp_core::Pair;
use sp_runtime::traits::{Block, Header};
use std::marker::PhantomData;
pub trait BaseSubstrateEquivocationDetectionPipeline:
SubstrateFinalityPipeline<SourceChain = Self::BoundedSourceChain>
{
type BoundedSourceChain: ChainWithTransactions<AccountId = Self::BoundedSourceChainAccountId>;
type BoundedSourceChainAccountId: From<<AccountKeyPairOf<Self::BoundedSourceChain> as Pair>::Public>
+ Send;
}
impl<T> BaseSubstrateEquivocationDetectionPipeline for T
where
T: SubstrateFinalityPipeline,
T::SourceChain: ChainWithTransactions,
AccountIdOf<T::SourceChain>: From<<AccountKeyPairOf<Self::SourceChain> as Pair>::Public>,
{
type BoundedSourceChain = T::SourceChain;
type BoundedSourceChainAccountId = AccountIdOf<T::SourceChain>;
}
#[async_trait]
pub trait SubstrateEquivocationDetectionPipeline:
BaseSubstrateEquivocationDetectionPipeline
{
type ReportEquivocationCallBuilder: ReportEquivocationCallBuilder<Self>;
async fn start_relay_guards(
source_client: &impl Client<Self::SourceChain>,
enable_version_guard: bool,
) -> relay_substrate_client::Result<()> {
if enable_version_guard {
relay_substrate_client::guard::abort_on_spec_version_change(
source_client.clone(),
source_client.simple_runtime_version().await?.spec_version,
);
}
Ok(())
}
}
type FinalityProoffOf<P> = <<P as SubstrateFinalityPipeline>::FinalityEngine as Engine<
<P as SubstrateFinalityPipeline>::SourceChain,
>>::FinalityProof;
type FinalityVerificationContextfOf<P> =
<<P as SubstrateFinalityPipeline>::FinalityEngine as Engine<
<P as SubstrateFinalityPipeline>::SourceChain,
>>::FinalityVerificationContext;
pub type EquivocationProofOf<P> = <<P as SubstrateFinalityPipeline>::FinalityEngine as Engine<
<P as SubstrateFinalityPipeline>::SourceChain,
>>::EquivocationProof;
type EquivocationsFinderOf<P> = <<P as SubstrateFinalityPipeline>::FinalityEngine as Engine<
<P as SubstrateFinalityPipeline>::SourceChain,
>>::EquivocationsFinder;
pub type KeyOwnerProofOf<P> = <<P as SubstrateFinalityPipeline>::FinalityEngine as Engine<
<P as SubstrateFinalityPipeline>::SourceChain,
>>::KeyOwnerProof;
#[derive(Clone, Debug)]
pub struct EquivocationDetectionPipelineAdapter<P: SubstrateEquivocationDetectionPipeline> {
_phantom: PhantomData<P>,
}
impl<P: SubstrateEquivocationDetectionPipeline> FinalityPipeline
for EquivocationDetectionPipelineAdapter<P>
{
const SOURCE_NAME: &'static str = P::SourceChain::NAME;
const TARGET_NAME: &'static str = P::TargetChain::NAME;
type Hash = HashOf<P::SourceChain>;
type Number = BlockNumberOf<P::SourceChain>;
type FinalityProof = SubstrateFinalityProof<P>;
}
impl<P: SubstrateEquivocationDetectionPipeline> EquivocationDetectionPipeline
for EquivocationDetectionPipelineAdapter<P>
{
type TargetNumber = BlockNumberOf<P::TargetChain>;
type FinalityVerificationContext = FinalityVerificationContextfOf<P>;
type EquivocationProof = EquivocationProofOf<P>;
type EquivocationsFinder = EquivocationsFinderOf<P>;
}
pub trait ReportEquivocationCallBuilder<P: SubstrateEquivocationDetectionPipeline> {
fn build_report_equivocation_call(
equivocation_proof: EquivocationProofOf<P>,
key_owner_proof: KeyOwnerProofOf<P>,
) -> CallOf<P::SourceChain>;
}
pub struct DirectReportGrandpaEquivocationCallBuilder<P, R> {
_phantom: PhantomData<(P, R)>,
}
impl<P, R> ReportEquivocationCallBuilder<P> for DirectReportGrandpaEquivocationCallBuilder<P, R>
where
P: SubstrateEquivocationDetectionPipeline,
P::FinalityEngine: Engine<
P::SourceChain,
EquivocationProof = sp_consensus_grandpa::EquivocationProof<
HashOf<P::SourceChain>,
BlockNumberOf<P::SourceChain>,
>,
>,
R: frame_system::Config<Hash = HashOf<P::SourceChain>>
+ GrandpaConfig<KeyOwnerProof = KeyOwnerProofOf<P>>,
<R::Block as Block>::Header: Header<Number = BlockNumberOf<P::SourceChain>>,
CallOf<P::SourceChain>: From<GrandpaCall<R>>,
{
fn build_report_equivocation_call(
equivocation_proof: EquivocationProofOf<P>,
key_owner_proof: KeyOwnerProofOf<P>,
) -> CallOf<P::SourceChain> {
GrandpaCall::<R>::report_equivocation {
equivocation_proof: Box::new(equivocation_proof),
key_owner_proof,
}
.into()
}
}
#[rustfmt::skip]
#[macro_export]
macro_rules! generate_report_equivocation_call_builder {
($pipeline:ident, $mocked_builder:ident, $grandpa:path, $report_equivocation:path) => {
pub struct $mocked_builder;
impl $crate::equivocation::ReportEquivocationCallBuilder<$pipeline>
for $mocked_builder
{
fn build_report_equivocation_call(
equivocation_proof: $crate::equivocation::EquivocationProofOf<$pipeline>,
key_owner_proof: $crate::equivocation::KeyOwnerProofOf<$pipeline>,
) -> relay_substrate_client::CallOf<
<$pipeline as $crate::finality_base::SubstrateFinalityPipeline>::SourceChain
> {
bp_runtime::paste::item! {
$grandpa($report_equivocation {
equivocation_proof: Box::new(equivocation_proof),
key_owner_proof: key_owner_proof
})
}
}
}
};
}
pub async fn run<P: SubstrateEquivocationDetectionPipeline>(
source_client: impl Client<P::SourceChain>,
target_client: impl Client<P::TargetChain>,
source_transaction_params: TransactionParams<AccountKeyPairOf<P::SourceChain>>,
metrics_params: MetricsParams,
) -> anyhow::Result<()> {
log::info!(
target: "bridge",
"Starting {} -> {} equivocations detection loop",
P::SourceChain::NAME,
P::TargetChain::NAME,
);
equivocation_detector::run(
SubstrateEquivocationSource::<P, _>::new(source_client, source_transaction_params),
SubstrateEquivocationTarget::<P, _>::new(target_client),
P::TargetChain::AVERAGE_BLOCK_INTERVAL,
metrics_params,
futures::future::pending(),
)
.await
.map_err(|e| anyhow::format_err!("{}", e))
}