referrerpolicy=no-referrer-when-downgrade

pallet_bridge_relayers/extension/
grandpa_adapter.rs

1// Copyright (C) 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//! Adapter that allows using `pallet-bridge-relayers` as a signed extension in the
18//! bridge with remote GRANDPA chain.
19
20use crate::{
21	extension::verify_messages_call_succeeded, Config as BridgeRelayersConfig, LOG_TARGET,
22};
23
24use bp_relayers::{BatchCallUnpacker, ExtensionCallData, ExtensionCallInfo, ExtensionConfig};
25use bp_runtime::{Chain, StaticStrProvider};
26use core::marker::PhantomData;
27use frame_support::dispatch::{DispatchInfo, PostDispatchInfo};
28use frame_system::Config as SystemConfig;
29use pallet_bridge_grandpa::{
30	CallSubType as BridgeGrandpaCallSubtype, Config as BridgeGrandpaConfig,
31	SubmitFinalityProofHelper,
32};
33use pallet_bridge_messages::{
34	CallSubType as BridgeMessagesCallSubType, Config as BridgeMessagesConfig, LaneIdOf,
35};
36use sp_runtime::{
37	traits::{Dispatchable, Get},
38	transaction_validity::{TransactionPriority, TransactionValidityError},
39	Saturating,
40};
41
42/// Adapter to be used in signed extension configuration, when bridging with remote
43/// chains that are using GRANDPA finality.
44pub struct WithGrandpaChainExtensionConfig<
45	// signed extension identifier
46	IdProvider,
47	// runtime that implements `BridgeMessagesConfig<BridgeMessagesPalletInstance>`, which
48	// uses `BridgeGrandpaConfig<BridgeGrandpaPalletInstance>` to receive messages and
49	// confirmations from the remote chain.
50	Runtime,
51	// batch call unpacker
52	BatchCallUnpacker,
53	// instance of the `pallet-bridge-grandpa`, tracked by this extension
54	BridgeGrandpaPalletInstance,
55	// instance of BridgedChain `pallet-bridge-messages`, tracked by this extension
56	BridgeMessagesPalletInstance,
57	// instance of `pallet-bridge-relayers`, tracked by this extension
58	BridgeRelayersPalletInstance,
59	// message delivery transaction priority boost for every additional message
60	PriorityBoostPerMessage,
61>(
62	PhantomData<(
63		IdProvider,
64		Runtime,
65		BatchCallUnpacker,
66		BridgeGrandpaPalletInstance,
67		BridgeMessagesPalletInstance,
68		BridgeRelayersPalletInstance,
69		PriorityBoostPerMessage,
70	)>,
71);
72
73impl<ID, R, BCU, GI, MI, RI, P> ExtensionConfig
74	for WithGrandpaChainExtensionConfig<ID, R, BCU, GI, MI, RI, P>
75where
76	ID: StaticStrProvider,
77	R: BridgeRelayersConfig<RI>
78		+ BridgeMessagesConfig<MI, BridgedChain = pallet_bridge_grandpa::BridgedChain<R, GI>>
79		+ BridgeGrandpaConfig<GI>,
80	BCU: BatchCallUnpacker<R>,
81	GI: 'static,
82	MI: 'static,
83	RI: 'static,
84	P: Get<TransactionPriority>,
85	R::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>
86		+ BridgeGrandpaCallSubtype<R, GI>
87		+ BridgeMessagesCallSubType<R, MI>,
88{
89	type IdProvider = ID;
90	type Runtime = R;
91	type BridgeMessagesPalletInstance = MI;
92	type BridgeRelayersPalletInstance = RI;
93	type PriorityBoostPerMessage = P;
94	type RemoteGrandpaChainBlockNumber = pallet_bridge_grandpa::BridgedBlockNumber<R, GI>;
95	type LaneId = LaneIdOf<R, Self::BridgeMessagesPalletInstance>;
96
97	fn parse_and_check_for_obsolete_call(
98		call: &R::RuntimeCall,
99	) -> Result<
100		Option<ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber, Self::LaneId>>,
101		TransactionValidityError,
102	> {
103		let calls = BCU::unpack(call, 2);
104		let total_calls = calls.len();
105		let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev();
106
107		let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info());
108		let relay_finality_call =
109			calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info());
110
111		Ok(match (total_calls, relay_finality_call, msgs_call) {
112			(2, Some(relay_finality_call), Some(msgs_call)) =>
113				Some(ExtensionCallInfo::RelayFinalityAndMsgs(relay_finality_call, msgs_call)),
114			(1, None, Some(msgs_call)) => Some(ExtensionCallInfo::Msgs(msgs_call)),
115			_ => None,
116		})
117	}
118
119	fn check_obsolete_parsed_call(
120		call: &R::RuntimeCall,
121	) -> Result<&R::RuntimeCall, TransactionValidityError> {
122		call.check_obsolete_submit_finality_proof()?;
123		call.check_obsolete_call()?;
124		Ok(call)
125	}
126
127	fn check_call_result(
128		call_info: &ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber, Self::LaneId>,
129		call_data: &mut ExtensionCallData,
130		relayer: &R::AccountId,
131	) -> bool {
132		verify_submit_finality_proof_succeeded::<Self, GI>(call_info, call_data, relayer) &&
133			verify_messages_call_succeeded::<Self>(call_info, call_data, relayer)
134	}
135}
136
137/// If the batch call contains the GRANDPA chain state update call, verify that it
138/// has been successful.
139///
140/// Only returns false when GRANDPA chain state update call has failed.
141pub(crate) fn verify_submit_finality_proof_succeeded<C, GI>(
142	call_info: &ExtensionCallInfo<C::RemoteGrandpaChainBlockNumber, C::LaneId>,
143	call_data: &mut ExtensionCallData,
144	relayer: &<C::Runtime as SystemConfig>::AccountId,
145) -> bool
146where
147	C: ExtensionConfig,
148	GI: 'static,
149	C::Runtime: BridgeGrandpaConfig<GI>,
150	<C::Runtime as BridgeGrandpaConfig<GI>>::BridgedChain:
151		Chain<BlockNumber = C::RemoteGrandpaChainBlockNumber>,
152{
153	let Some(finality_proof_info) = call_info.submit_finality_proof_info() else { return true };
154
155	if !SubmitFinalityProofHelper::<C::Runtime, GI>::was_successful(
156		finality_proof_info.block_number,
157	) {
158		// we only refund relayer if all calls have updated chain state
159		tracing::trace!(
160			target: LOG_TARGET,
161			id_provider=%C::IdProvider::STR,
162			lane_id=?call_info.messages_call_info().lane_id(),
163			?relayer,
164			"Relayer has submitted invalid GRANDPA chain finality proof"
165		);
166		return false
167	}
168
169	// there's a conflict between how bridge GRANDPA pallet works and a `utility.batchAll`
170	// transaction. If relay chain header is mandatory, the GRANDPA pallet returns
171	// `Pays::No`, because such transaction is mandatory for operating the bridge. But
172	// `utility.batchAll` transaction always requires payment. But in both cases we'll
173	// refund relayer - either explicitly here, or using `Pays::No` if he's choosing
174	// to submit dedicated transaction.
175
176	// submitter has means to include extra weight/bytes in the `submit_finality_proof`
177	// call, so let's subtract extra weight/size to avoid refunding for this extra stuff
178	call_data.extra_weight.saturating_accrue(finality_proof_info.extra_weight);
179	call_data.extra_size.saturating_accrue(finality_proof_info.extra_size);
180
181	true
182}