referrerpolicy=no-referrer-when-downgrade

substrate_relay_helper/equivocation/
mod.rs

1// Copyright 2019-2023 Parity Technologies (UK) Ltd.
2// This file is part of Parity Bridges Common.
3
4// Parity Bridges Common is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity Bridges Common is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity Bridges Common.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Types and functions intended to ease adding of new Substrate -> Substrate
18//! equivocation detection pipelines.
19
20mod 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
40/// Convenience trait that adds bounds to `SubstrateEquivocationDetectionPipeline`.
41pub trait BaseSubstrateEquivocationDetectionPipeline:
42	SubstrateFinalityPipeline<SourceChain = Self::BoundedSourceChain>
43{
44	/// Bounded `SubstrateFinalityPipeline::SourceChain`.
45	type BoundedSourceChain: ChainWithTransactions<AccountId = Self::BoundedSourceChainAccountId>;
46
47	/// Bounded `AccountIdOf<SubstrateFinalityPipeline::SourceChain>`.
48	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/// Substrate -> Substrate equivocation detection pipeline.
63#[async_trait]
64pub trait SubstrateEquivocationDetectionPipeline:
65	BaseSubstrateEquivocationDetectionPipeline
66{
67	/// How the `report_equivocation` call is built ?
68	type ReportEquivocationCallBuilder: ReportEquivocationCallBuilder<Self>;
69
70	/// Add relay guards if required.
71	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;
92/// The type of the equivocation proof used by the `SubstrateEquivocationDetectionPipeline`
93pub 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;
99/// The type of the key owner proof used by the `SubstrateEquivocationDetectionPipeline`
100pub type KeyOwnerProofOf<P> = <<P as SubstrateFinalityPipeline>::FinalityEngine as Engine<
101	<P as SubstrateFinalityPipeline>::SourceChain,
102>>::KeyOwnerProof;
103
104/// Adapter that allows a `SubstrateEquivocationDetectionPipeline` to act as an
105/// `EquivocationDetectionPipeline`.
106#[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
131/// Different ways of building `report_equivocation` calls.
132pub trait ReportEquivocationCallBuilder<P: SubstrateEquivocationDetectionPipeline> {
133	/// Build a `report_equivocation` call to be executed on the source chain.
134	fn build_report_equivocation_call(
135		equivocation_proof: EquivocationProofOf<P>,
136		key_owner_proof: KeyOwnerProofOf<P>,
137	) -> CallOf<P::SourceChain>;
138}
139
140/// Building the `report_equivocation` call when having direct access to the target chain runtime.
141pub 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/// Macro that generates `ReportEquivocationCallBuilder` implementation for the case where
173/// we only have access to the mocked version of the source chain runtime.
174#[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
200/// Run Substrate-to-Substrate equivocations detection loop.
201pub 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}