// Copyright 2019-2023 Parity Technologies (UK) Ltd. // This file is part of Parity Bridges Common. // Parity Bridges Common is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity Bridges Common is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . //! Types and functions intended to ease adding of new Substrate -> Substrate //! equivocation detection pipelines. 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; /// Convenience trait that adds bounds to `SubstrateEquivocationDetectionPipeline`. pub trait BaseSubstrateEquivocationDetectionPipeline: SubstrateFinalityPipeline { /// Bounded `SubstrateFinalityPipeline::SourceChain`. type BoundedSourceChain: ChainWithTransactions; /// Bounded `AccountIdOf`. type BoundedSourceChainAccountId: From< as Pair>::Public> + Send; } impl BaseSubstrateEquivocationDetectionPipeline for T where T: SubstrateFinalityPipeline, T::SourceChain: ChainWithTransactions, AccountIdOf: From< as Pair>::Public>, { type BoundedSourceChain = T::SourceChain; type BoundedSourceChainAccountId = AccountIdOf; } /// Substrate -> Substrate equivocation detection pipeline. #[async_trait] pub trait SubstrateEquivocationDetectionPipeline: BaseSubstrateEquivocationDetectionPipeline { /// How the `report_equivocation` call is built ? type ReportEquivocationCallBuilder: ReportEquivocationCallBuilder; /// Add relay guards if required. async fn start_relay_guards( source_client: &Client, 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

= <

::FinalityEngine as Engine<

::SourceChain, >>::FinalityProof; type FinalityVerificationContextfOf

= <

::FinalityEngine as Engine<

::SourceChain, >>::FinalityVerificationContext; /// The type of the equivocation proof used by the `SubstrateEquivocationDetectionPipeline` pub type EquivocationProofOf

= <

::FinalityEngine as Engine<

::SourceChain, >>::EquivocationProof; type EquivocationsFinderOf

= <

::FinalityEngine as Engine<

::SourceChain, >>::EquivocationsFinder; /// The type of the key owner proof used by the `SubstrateEquivocationDetectionPipeline` pub type KeyOwnerProofOf

= <

::FinalityEngine as Engine<

::SourceChain, >>::KeyOwnerProof; /// Adapter that allows a `SubstrateEquivocationDetectionPipeline` to act as an /// `EquivocationDetectionPipeline`. #[derive(Clone, Debug)] pub struct EquivocationDetectionPipelineAdapter { _phantom: PhantomData

, } impl FinalityPipeline for EquivocationDetectionPipelineAdapter

{ const SOURCE_NAME: &'static str = P::SourceChain::NAME; const TARGET_NAME: &'static str = P::TargetChain::NAME; type Hash = HashOf; type Number = BlockNumberOf; type FinalityProof = SubstrateFinalityProof

; } impl EquivocationDetectionPipeline for EquivocationDetectionPipelineAdapter

{ type TargetNumber = BlockNumberOf; type FinalityVerificationContext = FinalityVerificationContextfOf

; type EquivocationProof = EquivocationProofOf

; type EquivocationsFinder = EquivocationsFinderOf

; } /// Different ways of building `report_equivocation` calls. pub trait ReportEquivocationCallBuilder { /// Build a `report_equivocation` call to be executed on the source chain. fn build_report_equivocation_call( equivocation_proof: EquivocationProofOf

, key_owner_proof: KeyOwnerProofOf

, ) -> CallOf; } /// Building the `report_equivocation` call when having direct access to the target chain runtime. pub struct DirectReportGrandpaEquivocationCallBuilder { _phantom: PhantomData<(P, R)>, } impl ReportEquivocationCallBuilder

for DirectReportGrandpaEquivocationCallBuilder where P: SubstrateEquivocationDetectionPipeline, P::FinalityEngine: Engine< P::SourceChain, EquivocationProof = sp_consensus_grandpa::EquivocationProof< HashOf, BlockNumberOf, >, >, R: frame_system::Config> + GrandpaConfig>, ::Header: Header>, CallOf: From>, { fn build_report_equivocation_call( equivocation_proof: EquivocationProofOf

, key_owner_proof: KeyOwnerProofOf

, ) -> CallOf { GrandpaCall::::report_equivocation { equivocation_proof: Box::new(equivocation_proof), key_owner_proof, } .into() } } /// Macro that generates `ReportEquivocationCallBuilder` implementation for the case where /// we only have access to the mocked version of the source chain runtime. #[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 }) } } } }; } /// Run Substrate-to-Substrate equivocations detection loop. pub async fn run( source_client: Client, target_client: Client, source_transaction_params: TransactionParams>, metrics_params: MetricsParams, ) -> anyhow::Result<()> { log::info!( target: "bridge", "Starting {} -> {} equivocations detection loop", P::SourceChain::NAME, P::TargetChain::NAME, ); equivocation_detector::run( SubstrateEquivocationSource::

::new(source_client, source_transaction_params), SubstrateEquivocationTarget::

::new(target_client), P::TargetChain::AVERAGE_BLOCK_INTERVAL, metrics_params, futures::future::pending(), ) .await .map_err(|e| anyhow::format_err!("{}", e)) }