referrerpolicy=no-referrer-when-downgrade
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// Copyright (C) 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 <http://www.gnu.org/licenses/>.

//! All runtime calls, supported by `pallet-bridge-relayers` when it acts as a signed
//! extension.

use bp_header_chain::SubmitFinalityProofInfo;
use bp_messages::MessagesCallInfo;
use bp_parachains::SubmitParachainHeadsInfo;
use bp_runtime::StaticStrProvider;
use codec::{Decode, Encode};
use frame_support::{
	dispatch::CallableCallFor, traits::IsSubType, weights::Weight, RuntimeDebugNoBound,
};
use frame_system::Config as SystemConfig;
use pallet_utility::{Call as UtilityCall, Pallet as UtilityPallet};
use sp_runtime::{
	traits::Get,
	transaction_validity::{TransactionPriority, TransactionValidityError},
	RuntimeDebug,
};
use sp_std::{fmt::Debug, marker::PhantomData, vec, vec::Vec};

/// Type of the call that the signed extension recognizes.
#[derive(PartialEq, RuntimeDebugNoBound)]
pub enum ExtensionCallInfo<RemoteGrandpaChainBlockNumber: Debug, LaneId: Clone + Copy + Debug> {
	/// Relay chain finality + parachain finality + message delivery/confirmation calls.
	AllFinalityAndMsgs(
		SubmitFinalityProofInfo<RemoteGrandpaChainBlockNumber>,
		SubmitParachainHeadsInfo,
		MessagesCallInfo<LaneId>,
	),
	/// Relay chain finality + message delivery/confirmation calls.
	RelayFinalityAndMsgs(
		SubmitFinalityProofInfo<RemoteGrandpaChainBlockNumber>,
		MessagesCallInfo<LaneId>,
	),
	/// Parachain finality + message delivery/confirmation calls.
	///
	/// This variant is used only when bridging with parachain.
	ParachainFinalityAndMsgs(SubmitParachainHeadsInfo, MessagesCallInfo<LaneId>),
	/// Standalone message delivery/confirmation call.
	Msgs(MessagesCallInfo<LaneId>),
}

impl<RemoteGrandpaChainBlockNumber: Clone + Copy + Debug, LaneId: Clone + Copy + Debug>
	ExtensionCallInfo<RemoteGrandpaChainBlockNumber, LaneId>
{
	/// Returns true if call is a message delivery call (with optional finality calls).
	pub fn is_receive_messages_proof_call(&self) -> bool {
		match self.messages_call_info() {
			MessagesCallInfo::ReceiveMessagesProof(_) => true,
			MessagesCallInfo::ReceiveMessagesDeliveryProof(_) => false,
		}
	}

	/// Returns the pre-dispatch `finality_target` sent to the `SubmitFinalityProof` call.
	pub fn submit_finality_proof_info(
		&self,
	) -> Option<SubmitFinalityProofInfo<RemoteGrandpaChainBlockNumber>> {
		match *self {
			Self::AllFinalityAndMsgs(info, _, _) => Some(info),
			Self::RelayFinalityAndMsgs(info, _) => Some(info),
			_ => None,
		}
	}

	/// Returns the pre-dispatch `SubmitParachainHeadsInfo`.
	pub fn submit_parachain_heads_info(&self) -> Option<&SubmitParachainHeadsInfo> {
		match self {
			Self::AllFinalityAndMsgs(_, info, _) => Some(info),
			Self::ParachainFinalityAndMsgs(info, _) => Some(info),
			_ => None,
		}
	}

	/// Returns the pre-dispatch `ReceiveMessagesProofInfo`.
	pub fn messages_call_info(&self) -> &MessagesCallInfo<LaneId> {
		match self {
			Self::AllFinalityAndMsgs(_, _, info) => info,
			Self::RelayFinalityAndMsgs(_, info) => info,
			Self::ParachainFinalityAndMsgs(_, info) => info,
			Self::Msgs(info) => info,
		}
	}
}

/// Extra post-dispatch data, associated with the supported runtime call.
#[derive(Default, RuntimeDebug)]
pub struct ExtensionCallData {
	/// Extra weight, consumed by the call. We have some assumptions about normal weight
	/// that may be consumed by expected calls. If the actual weight is larger than that,
	/// we do not refund relayer for this extra weight.
	pub extra_weight: Weight,
	/// Extra size, consumed by the call. We have some assumptions about normal size
	/// of the encoded call. If the actual size is larger than that, we do not refund relayer
	/// for this extra size.
	pub extra_size: u32,
}

/// Signed extension configuration.
///
/// The single `pallet-bridge-relayers` instance may be shared by multiple messages
/// pallet instances, bridging with different remote networks. We expect every instance
/// of the messages pallet to add a separate signed extension to runtime. So it must
/// have a separate configuration.
pub trait ExtensionConfig {
	/// Unique identifier of the signed extension that will use this configuration.
	type IdProvider: StaticStrProvider;
	/// Runtime that optionally supports batched calls. We assume that batched call
	/// succeeds if and only if all of its nested calls succeed.
	type Runtime: frame_system::Config;
	/// Relayers pallet instance.
	type BridgeRelayersPalletInstance: 'static;
	/// Messages pallet instance.
	type BridgeMessagesPalletInstance: 'static;
	/// Additional priority that is added to base message delivery transaction priority
	/// for every additional bundled message.
	type PriorityBoostPerMessage: Get<TransactionPriority>;
	/// Block number for the remote **GRANDPA chain**. Mind that this chain is not
	/// necessarily the chain that we are bridging with. If we are bridging with
	/// parachain, it must be its parent relay chain. If we are bridging with the
	/// GRANDPA chain, it must be it.
	type RemoteGrandpaChainBlockNumber: Clone + Copy + Debug;
	/// Lane identifier type.
	type LaneId: Clone + Copy + Decode + Encode + Debug;

	/// Given runtime call, check if it is supported by the transaction extension. Additionally,
	/// check if call (or any of batched calls) are obsolete.
	fn parse_and_check_for_obsolete_call(
		call: &<Self::Runtime as SystemConfig>::RuntimeCall,
	) -> Result<
		Option<ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber, Self::LaneId>>,
		TransactionValidityError,
	>;

	/// Check if runtime call is already obsolete.
	fn check_obsolete_parsed_call(
		call: &<Self::Runtime as SystemConfig>::RuntimeCall,
	) -> Result<&<Self::Runtime as SystemConfig>::RuntimeCall, TransactionValidityError>;

	/// Given runtime call info, check that this call has been successful and has updated
	/// runtime storage accordingly.
	fn check_call_result(
		call_info: &ExtensionCallInfo<Self::RemoteGrandpaChainBlockNumber, Self::LaneId>,
		call_data: &mut ExtensionCallData,
		relayer: &<Self::Runtime as SystemConfig>::AccountId,
	) -> bool;
}

/// Something that can unpack batch calls (all-or-nothing flavor) of given size.
pub trait BatchCallUnpacker<Runtime: frame_system::Config> {
	/// Unpack batch call with no more than `max_packed_calls` calls.
	fn unpack(call: &Runtime::RuntimeCall, max_packed_calls: u32) -> Vec<&Runtime::RuntimeCall>;
}

/// An `BatchCallUnpacker` adapter for runtimes with utility pallet.
pub struct RuntimeWithUtilityPallet<Runtime>(PhantomData<Runtime>);

impl<Runtime> BatchCallUnpacker<Runtime> for RuntimeWithUtilityPallet<Runtime>
where
	Runtime: pallet_utility::Config<RuntimeCall = <Runtime as SystemConfig>::RuntimeCall>,
	<Runtime as SystemConfig>::RuntimeCall:
		IsSubType<CallableCallFor<UtilityPallet<Runtime>, Runtime>>,
{
	fn unpack(
		call: &<Runtime as frame_system::Config>::RuntimeCall,
		max_packed_calls: u32,
	) -> Vec<&<Runtime as frame_system::Config>::RuntimeCall> {
		match call.is_sub_type() {
			Some(UtilityCall::<Runtime>::batch_all { ref calls })
				if calls.len() <= max_packed_calls as usize =>
				calls.iter().collect(),
			Some(_) => vec![],
			None => vec![call],
		}
	}
}

impl<Runtime: frame_system::Config> BatchCallUnpacker<Runtime> for () {
	fn unpack(call: &Runtime::RuntimeCall, _max_packed_calls: u32) -> Vec<&Runtime::RuntimeCall> {
		vec![call]
	}
}