substrate_relay_helper/equivocation/
mod.rs1mod source;
21mod target;
22
23use crate::{
24 equivocation::{source::SubstrateEquivocationSource, target::SubstrateEquivocationTarget},
25 finality_base::{engine::Engine, SubstrateFinalityPipeline, SubstrateFinalityProof},
26 TransactionParams,
27};
28
29use async_trait::async_trait;
30use bp_runtime::{AccountIdOf, BlockNumberOf, HashOf};
31use equivocation_detector::EquivocationDetectionPipeline;
32use finality_relay::FinalityPipeline;
33use pallet_grandpa::{Call as GrandpaCall, Config as GrandpaConfig};
34use relay_substrate_client::{AccountKeyPairOf, CallOf, Chain, ChainWithTransactions, Client};
35use relay_utils::metrics::MetricsParams;
36use sp_core::Pair;
37use sp_runtime::traits::{Block, Header};
38use std::marker::PhantomData;
39
40pub trait BaseSubstrateEquivocationDetectionPipeline:
42 SubstrateFinalityPipeline<SourceChain = Self::BoundedSourceChain>
43{
44 type BoundedSourceChain: ChainWithTransactions<AccountId = Self::BoundedSourceChainAccountId>;
46
47 type BoundedSourceChainAccountId: From<<AccountKeyPairOf<Self::BoundedSourceChain> as Pair>::Public>
49 + Send;
50}
51
52impl<T> BaseSubstrateEquivocationDetectionPipeline for T
53where
54 T: SubstrateFinalityPipeline,
55 T::SourceChain: ChainWithTransactions,
56 AccountIdOf<T::SourceChain>: From<<AccountKeyPairOf<Self::SourceChain> as Pair>::Public>,
57{
58 type BoundedSourceChain = T::SourceChain;
59 type BoundedSourceChainAccountId = AccountIdOf<T::SourceChain>;
60}
61
62#[async_trait]
64pub trait SubstrateEquivocationDetectionPipeline:
65 BaseSubstrateEquivocationDetectionPipeline
66{
67 type ReportEquivocationCallBuilder: ReportEquivocationCallBuilder<Self>;
69
70 async fn start_relay_guards(
72 source_client: &impl Client<Self::SourceChain>,
73 enable_version_guard: bool,
74 ) -> relay_substrate_client::Result<()> {
75 if enable_version_guard {
76 relay_substrate_client::guard::abort_on_spec_version_change(
77 source_client.clone(),
78 source_client.simple_runtime_version().await?.spec_version,
79 );
80 }
81 Ok(())
82 }
83}
84
85type FinalityProoffOf<P> = <<P as SubstrateFinalityPipeline>::FinalityEngine as Engine<
86 <P as SubstrateFinalityPipeline>::SourceChain,
87>>::FinalityProof;
88type FinalityVerificationContextfOf<P> =
89 <<P as SubstrateFinalityPipeline>::FinalityEngine as Engine<
90 <P as SubstrateFinalityPipeline>::SourceChain,
91 >>::FinalityVerificationContext;
92pub type EquivocationProofOf<P> = <<P as SubstrateFinalityPipeline>::FinalityEngine as Engine<
94 <P as SubstrateFinalityPipeline>::SourceChain,
95>>::EquivocationProof;
96type EquivocationsFinderOf<P> = <<P as SubstrateFinalityPipeline>::FinalityEngine as Engine<
97 <P as SubstrateFinalityPipeline>::SourceChain,
98>>::EquivocationsFinder;
99pub type KeyOwnerProofOf<P> = <<P as SubstrateFinalityPipeline>::FinalityEngine as Engine<
101 <P as SubstrateFinalityPipeline>::SourceChain,
102>>::KeyOwnerProof;
103
104#[derive(Clone, Debug)]
107pub struct EquivocationDetectionPipelineAdapter<P: SubstrateEquivocationDetectionPipeline> {
108 _phantom: PhantomData<P>,
109}
110
111impl<P: SubstrateEquivocationDetectionPipeline> FinalityPipeline
112 for EquivocationDetectionPipelineAdapter<P>
113{
114 const SOURCE_NAME: &'static str = P::SourceChain::NAME;
115 const TARGET_NAME: &'static str = P::TargetChain::NAME;
116
117 type Hash = HashOf<P::SourceChain>;
118 type Number = BlockNumberOf<P::SourceChain>;
119 type FinalityProof = SubstrateFinalityProof<P>;
120}
121
122impl<P: SubstrateEquivocationDetectionPipeline> EquivocationDetectionPipeline
123 for EquivocationDetectionPipelineAdapter<P>
124{
125 type TargetNumber = BlockNumberOf<P::TargetChain>;
126 type FinalityVerificationContext = FinalityVerificationContextfOf<P>;
127 type EquivocationProof = EquivocationProofOf<P>;
128 type EquivocationsFinder = EquivocationsFinderOf<P>;
129}
130
131pub trait ReportEquivocationCallBuilder<P: SubstrateEquivocationDetectionPipeline> {
133 fn build_report_equivocation_call(
135 equivocation_proof: EquivocationProofOf<P>,
136 key_owner_proof: KeyOwnerProofOf<P>,
137 ) -> CallOf<P::SourceChain>;
138}
139
140pub struct DirectReportGrandpaEquivocationCallBuilder<P, R> {
142 _phantom: PhantomData<(P, R)>,
143}
144
145impl<P, R> ReportEquivocationCallBuilder<P> for DirectReportGrandpaEquivocationCallBuilder<P, R>
146where
147 P: SubstrateEquivocationDetectionPipeline,
148 P::FinalityEngine: Engine<
149 P::SourceChain,
150 EquivocationProof = sp_consensus_grandpa::EquivocationProof<
151 HashOf<P::SourceChain>,
152 BlockNumberOf<P::SourceChain>,
153 >,
154 >,
155 R: frame_system::Config<Hash = HashOf<P::SourceChain>>
156 + GrandpaConfig<KeyOwnerProof = KeyOwnerProofOf<P>>,
157 <R::Block as Block>::Header: Header<Number = BlockNumberOf<P::SourceChain>>,
158 CallOf<P::SourceChain>: From<GrandpaCall<R>>,
159{
160 fn build_report_equivocation_call(
161 equivocation_proof: EquivocationProofOf<P>,
162 key_owner_proof: KeyOwnerProofOf<P>,
163 ) -> CallOf<P::SourceChain> {
164 GrandpaCall::<R>::report_equivocation {
165 equivocation_proof: Box::new(equivocation_proof),
166 key_owner_proof,
167 }
168 .into()
169 }
170}
171
172#[rustfmt::skip]
175#[macro_export]
176macro_rules! generate_report_equivocation_call_builder {
177 ($pipeline:ident, $mocked_builder:ident, $grandpa:path, $report_equivocation:path) => {
178 pub struct $mocked_builder;
179
180 impl $crate::equivocation::ReportEquivocationCallBuilder<$pipeline>
181 for $mocked_builder
182 {
183 fn build_report_equivocation_call(
184 equivocation_proof: $crate::equivocation::EquivocationProofOf<$pipeline>,
185 key_owner_proof: $crate::equivocation::KeyOwnerProofOf<$pipeline>,
186 ) -> relay_substrate_client::CallOf<
187 <$pipeline as $crate::finality_base::SubstrateFinalityPipeline>::SourceChain
188 > {
189 bp_runtime::paste::item! {
190 $grandpa($report_equivocation {
191 equivocation_proof: Box::new(equivocation_proof),
192 key_owner_proof: key_owner_proof
193 })
194 }
195 }
196 }
197 };
198}
199
200pub async fn run<P: SubstrateEquivocationDetectionPipeline>(
202 source_client: impl Client<P::SourceChain>,
203 target_client: impl Client<P::TargetChain>,
204 source_transaction_params: TransactionParams<AccountKeyPairOf<P::SourceChain>>,
205 metrics_params: MetricsParams,
206) -> anyhow::Result<()> {
207 log::info!(
208 target: "bridge",
209 "Starting {} -> {} equivocations detection loop",
210 P::SourceChain::NAME,
211 P::TargetChain::NAME,
212 );
213
214 equivocation_detector::run(
215 SubstrateEquivocationSource::<P, _>::new(source_client, source_transaction_params),
216 SubstrateEquivocationTarget::<P, _>::new(target_client),
217 P::TargetChain::AVERAGE_BLOCK_INTERVAL,
218 metrics_params,
219 futures::future::pending(),
220 )
221 .await
222 .map_err(|e| anyhow::format_err!("{}", e))
223}