pallet_bridge_messages/
lib.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//! Runtime module that allows sending and receiving messages using lane concept:
18//!
19//! 1) the message is sent using `send_message()` call;
20//! 2) every outbound message is assigned nonce;
21//! 3) the messages are stored in the storage;
22//! 4) external component (relay) delivers messages to bridged chain;
23//! 5) messages are processed in order (ordered by assigned nonce);
24//! 6) relay may send proof-of-delivery back to this chain.
25//!
26//! Once message is sent, its progress can be tracked by looking at module events.
27//! The assigned nonce is reported using `MessageAccepted` event. When message is
28//! delivered to the the bridged chain, it is reported using `MessagesDelivered` event.
29//!
30//! **IMPORTANT NOTE**: after generating weights (custom `WeighInfo` implementation) for
31//! your runtime (where this module is plugged to), please add test for these weights.
32//! The test should call the `ensure_weights_are_correct` function from this module.
33//! If this test fails with your weights, then either weights are computed incorrectly,
34//! or some benchmarks assumptions are broken for your runtime.
35
36#![warn(missing_docs)]
37#![cfg_attr(not(feature = "std"), no_std)]
38
39pub use inbound_lane::{InboundLane, InboundLaneStorage, StoredInboundLaneData};
40pub use lanes_manager::{
41	LanesManager, LanesManagerError, RuntimeInboundLaneStorage, RuntimeOutboundLaneStorage,
42};
43pub use outbound_lane::{
44	OutboundLane, OutboundLaneStorage, ReceptionConfirmationError, StoredMessagePayload,
45};
46pub use weights::WeightInfo;
47pub use weights_ext::{
48	ensure_able_to_receive_confirmation, ensure_able_to_receive_message,
49	ensure_maximal_message_dispatch, ensure_weights_are_correct, WeightInfoExt,
50	EXPECTED_DEFAULT_MESSAGE_LENGTH, EXTRA_STORAGE_PROOF_SIZE,
51};
52
53use bp_header_chain::HeaderChain;
54use bp_messages::{
55	source_chain::{
56		DeliveryConfirmationPayments, FromBridgedChainMessagesDeliveryProof, OnMessagesDelivered,
57		SendMessageArtifacts,
58	},
59	target_chain::{
60		DeliveryPayments, DispatchMessage, FromBridgedChainMessagesProof, MessageDispatch,
61		ProvedLaneMessages, ProvedMessages,
62	},
63	ChainWithMessages, DeliveredMessages, InboundLaneData, InboundMessageDetails, MessageKey,
64	MessageNonce, MessagePayload, MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails,
65	UnrewardedRelayersState, VerificationError,
66};
67use bp_runtime::{
68	AccountIdOf, BasicOperatingMode, HashOf, OwnedBridgeModule, PreComputedSize, RangeInclusiveExt,
69	Size,
70};
71use codec::{Decode, Encode};
72use frame_support::{dispatch::PostDispatchInfo, ensure, fail, traits::Get, DefaultNoBound};
73use sp_std::{marker::PhantomData, prelude::*};
74
75mod call_ext;
76mod inbound_lane;
77mod lanes_manager;
78mod outbound_lane;
79mod proofs;
80mod tests;
81mod weights_ext;
82
83pub mod weights;
84
85#[cfg(feature = "runtime-benchmarks")]
86pub mod benchmarking;
87pub mod migration;
88
89pub use call_ext::*;
90pub use pallet::*;
91#[cfg(feature = "test-helpers")]
92pub use tests::*;
93
94/// The target that will be used when publishing logs related to this pallet.
95pub const LOG_TARGET: &str = "runtime::bridge-messages";
96
97#[frame_support::pallet]
98pub mod pallet {
99	use super::*;
100	use bp_messages::{LaneIdType, ReceivedMessages, ReceptionResult};
101	use bp_runtime::RangeInclusiveExt;
102	use frame_support::pallet_prelude::*;
103	use frame_system::pallet_prelude::*;
104
105	#[pallet::config]
106	pub trait Config<I: 'static = ()>: frame_system::Config {
107		// General types
108
109		/// The overarching event type.
110		type RuntimeEvent: From<Event<Self, I>>
111			+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
112		/// Benchmarks results from runtime we're plugged into.
113		type WeightInfo: WeightInfoExt;
114
115		/// This chain type.
116		type ThisChain: ChainWithMessages;
117		/// Bridged chain type.
118		type BridgedChain: ChainWithMessages;
119		/// Bridged chain headers provider.
120		type BridgedHeaderChain: HeaderChain<Self::BridgedChain>;
121
122		/// Payload type of outbound messages. This payload is dispatched on the bridged chain.
123		type OutboundPayload: Parameter + Size;
124		/// Payload type of inbound messages. This payload is dispatched on this chain.
125		type InboundPayload: Decode;
126		/// Lane identifier type.
127		type LaneId: LaneIdType;
128
129		/// Handler for relayer payments that happen during message delivery transaction.
130		type DeliveryPayments: DeliveryPayments<Self::AccountId>;
131		/// Handler for relayer payments that happen during message delivery confirmation
132		/// transaction.
133		type DeliveryConfirmationPayments: DeliveryConfirmationPayments<
134			Self::AccountId,
135			Self::LaneId,
136		>;
137		/// Delivery confirmation callback.
138		type OnMessagesDelivered: OnMessagesDelivered<Self::LaneId>;
139
140		/// Message dispatch handler.
141		type MessageDispatch: MessageDispatch<
142			DispatchPayload = Self::InboundPayload,
143			LaneId = Self::LaneId,
144		>;
145	}
146
147	/// Shortcut to this chain type for Config.
148	pub type ThisChainOf<T, I> = <T as Config<I>>::ThisChain;
149	/// Shortcut to bridged chain type for Config.
150	pub type BridgedChainOf<T, I> = <T as Config<I>>::BridgedChain;
151	/// Shortcut to bridged header chain type for Config.
152	pub type BridgedHeaderChainOf<T, I> = <T as Config<I>>::BridgedHeaderChain;
153	/// Shortcut to lane identifier type for Config.
154	pub type LaneIdOf<T, I> = <T as Config<I>>::LaneId;
155
156	#[pallet::pallet]
157	#[pallet::storage_version(migration::STORAGE_VERSION)]
158	pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
159
160	impl<T: Config<I>, I: 'static> OwnedBridgeModule<T> for Pallet<T, I> {
161		const LOG_TARGET: &'static str = LOG_TARGET;
162		type OwnerStorage = PalletOwner<T, I>;
163		type OperatingMode = MessagesOperatingMode;
164		type OperatingModeStorage = PalletOperatingMode<T, I>;
165	}
166
167	#[pallet::call]
168	impl<T: Config<I>, I: 'static> Pallet<T, I> {
169		/// Change `PalletOwner`.
170		///
171		/// May only be called either by root, or by `PalletOwner`.
172		#[pallet::call_index(0)]
173		#[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))]
174		pub fn set_owner(origin: OriginFor<T>, new_owner: Option<T::AccountId>) -> DispatchResult {
175			<Self as OwnedBridgeModule<_>>::set_owner(origin, new_owner)
176		}
177
178		/// Halt or resume all/some pallet operations.
179		///
180		/// May only be called either by root, or by `PalletOwner`.
181		#[pallet::call_index(1)]
182		#[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))]
183		pub fn set_operating_mode(
184			origin: OriginFor<T>,
185			operating_mode: MessagesOperatingMode,
186		) -> DispatchResult {
187			<Self as OwnedBridgeModule<_>>::set_operating_mode(origin, operating_mode)
188		}
189
190		/// Receive messages proof from bridged chain.
191		///
192		/// The weight of the call assumes that the transaction always brings outbound lane
193		/// state update. Because of that, the submitter (relayer) has no benefit of not including
194		/// this data in the transaction, so reward confirmations lags should be minimal.
195		///
196		/// The call fails if:
197		///
198		/// - the pallet is halted;
199		///
200		/// - the call origin is not `Signed(_)`;
201		///
202		/// - there are too many messages in the proof;
203		///
204		/// - the proof verification procedure returns an error - e.g. because header used to craft
205		///   proof is not imported by the associated finality pallet;
206		///
207		/// - the `dispatch_weight` argument is not sufficient to dispatch all bundled messages.
208		///
209		/// The call may succeed, but some messages may not be delivered e.g. if they are not fit
210		/// into the unrewarded relayers vector.
211		#[pallet::call_index(2)]
212		#[pallet::weight(T::WeightInfo::receive_messages_proof_weight(&**proof, *messages_count, *dispatch_weight))]
213		pub fn receive_messages_proof(
214			origin: OriginFor<T>,
215			relayer_id_at_bridged_chain: AccountIdOf<BridgedChainOf<T, I>>,
216			proof: Box<FromBridgedChainMessagesProof<HashOf<BridgedChainOf<T, I>>, T::LaneId>>,
217			messages_count: u32,
218			dispatch_weight: Weight,
219		) -> DispatchResultWithPostInfo {
220			Self::ensure_not_halted().map_err(Error::<T, I>::BridgeModule)?;
221			let relayer_id_at_this_chain = ensure_signed(origin)?;
222
223			// reject transactions that are declaring too many messages
224			ensure!(
225				MessageNonce::from(messages_count) <=
226					BridgedChainOf::<T, I>::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX,
227				Error::<T, I>::TooManyMessagesInTheProof
228			);
229
230			// why do we need to know the weight of this (`receive_messages_proof`) call? Because
231			// we may want to return some funds for not-dispatching (or partially dispatching) some
232			// messages to the call origin (relayer). And this is done by returning actual weight
233			// from the call. But we only know dispatch weight of every messages. So to refund
234			// relayer because we have not dispatched Message, we need to:
235			//
236			// ActualWeight = DeclaredWeight - Message.DispatchWeight
237			//
238			// The DeclaredWeight is exactly what's computed here. Unfortunately it is impossible
239			// to get pre-computed value (and it has been already computed by the executive).
240			let declared_weight = T::WeightInfo::receive_messages_proof_weight(
241				&*proof,
242				messages_count,
243				dispatch_weight,
244			);
245			let mut actual_weight = declared_weight;
246
247			// verify messages proof && convert proof into messages
248			let (lane_id, lane_data) =
249				verify_and_decode_messages_proof::<T, I>(*proof, messages_count).map_err(
250					|err| {
251						log::trace!(target: LOG_TARGET, "Rejecting invalid messages proof: {:?}", err,);
252
253						Error::<T, I>::InvalidMessagesProof
254					},
255				)?;
256
257			// dispatch messages and (optionally) update lane(s) state(s)
258			let mut total_messages = 0;
259			let mut valid_messages = 0;
260			let mut dispatch_weight_left = dispatch_weight;
261			let mut lane = active_inbound_lane::<T, I>(lane_id)?;
262
263			// subtract extra storage proof bytes from the actual PoV size - there may be
264			// less unrewarded relayers than the maximal configured value
265			let lane_extra_proof_size_bytes = lane.storage().extra_proof_size_bytes();
266			actual_weight = actual_weight.set_proof_size(
267				actual_weight.proof_size().saturating_sub(lane_extra_proof_size_bytes),
268			);
269
270			if let Some(lane_state) = lane_data.lane_state {
271				let updated_latest_confirmed_nonce = lane.receive_state_update(lane_state);
272				if let Some(updated_latest_confirmed_nonce) = updated_latest_confirmed_nonce {
273					log::trace!(
274						target: LOG_TARGET,
275						"Received lane {:?} state update: latest_confirmed_nonce={}. Unrewarded relayers: {:?}",
276						lane_id,
277						updated_latest_confirmed_nonce,
278						UnrewardedRelayersState::from(&lane.storage().data()),
279					);
280				}
281			}
282
283			let mut messages_received_status =
284				ReceivedMessages::new(lane_id, Vec::with_capacity(lane_data.messages.len()));
285			for mut message in lane_data.messages {
286				debug_assert_eq!(message.key.lane_id, lane_id);
287				total_messages += 1;
288
289				// ensure that relayer has declared enough weight for dispatching next message
290				// on this lane. We can't dispatch lane messages out-of-order, so if declared
291				// weight is not enough, let's move to next lane
292				let message_dispatch_weight = T::MessageDispatch::dispatch_weight(&mut message);
293				if message_dispatch_weight.any_gt(dispatch_weight_left) {
294					log::trace!(
295						target: LOG_TARGET,
296						"Cannot dispatch any more messages on lane {:?}. Weight: declared={}, left={}",
297						lane_id,
298						message_dispatch_weight,
299						dispatch_weight_left,
300					);
301
302					fail!(Error::<T, I>::InsufficientDispatchWeight);
303				}
304
305				let receival_result = lane.receive_message::<T::MessageDispatch>(
306					&relayer_id_at_bridged_chain,
307					message.key.nonce,
308					message.data,
309				);
310
311				// note that we're returning unspent weight to relayer even if message has been
312				// rejected by the lane. This allows relayers to submit spam transactions with
313				// e.g. the same set of already delivered messages over and over again, without
314				// losing funds for messages dispatch. But keep in mind that relayer pays base
315				// delivery transaction cost anyway. And base cost covers everything except
316				// dispatch, so we have a balance here.
317				let unspent_weight = match &receival_result {
318					ReceptionResult::Dispatched(dispatch_result) => {
319						valid_messages += 1;
320						dispatch_result.unspent_weight
321					},
322					ReceptionResult::InvalidNonce |
323					ReceptionResult::TooManyUnrewardedRelayers |
324					ReceptionResult::TooManyUnconfirmedMessages => message_dispatch_weight,
325				};
326				messages_received_status.push(message.key.nonce, receival_result);
327
328				let unspent_weight = unspent_weight.min(message_dispatch_weight);
329				dispatch_weight_left -= message_dispatch_weight - unspent_weight;
330				actual_weight = actual_weight.saturating_sub(unspent_weight);
331			}
332
333			// let's now deal with relayer payments
334			T::DeliveryPayments::pay_reward(
335				relayer_id_at_this_chain,
336				total_messages,
337				valid_messages,
338				actual_weight,
339			);
340
341			log::debug!(
342				target: LOG_TARGET,
343				"Received messages: total={}, valid={}. Weight used: {}/{}.",
344				total_messages,
345				valid_messages,
346				actual_weight,
347				declared_weight,
348			);
349
350			Self::deposit_event(Event::MessagesReceived(messages_received_status));
351
352			Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes })
353		}
354
355		/// Receive messages delivery proof from bridged chain.
356		#[pallet::call_index(3)]
357		#[pallet::weight(T::WeightInfo::receive_messages_delivery_proof_weight(
358			proof,
359			relayers_state,
360		))]
361		pub fn receive_messages_delivery_proof(
362			origin: OriginFor<T>,
363			proof: FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChainOf<T, I>>, T::LaneId>,
364			mut relayers_state: UnrewardedRelayersState,
365		) -> DispatchResultWithPostInfo {
366			Self::ensure_not_halted().map_err(Error::<T, I>::BridgeModule)?;
367
368			let proof_size = proof.size();
369			let confirmation_relayer = ensure_signed(origin)?;
370			let (lane_id, lane_data) = proofs::verify_messages_delivery_proof::<T, I>(proof)
371				.map_err(|err| {
372					log::trace!(
373						target: LOG_TARGET,
374						"Rejecting invalid messages delivery proof: {:?}",
375						err,
376					);
377
378					Error::<T, I>::InvalidMessagesDeliveryProof
379				})?;
380			ensure!(
381				relayers_state.is_valid(&lane_data),
382				Error::<T, I>::InvalidUnrewardedRelayersState
383			);
384
385			// mark messages as delivered
386			let mut lane = any_state_outbound_lane::<T, I>(lane_id)?;
387			let last_delivered_nonce = lane_data.last_delivered_nonce();
388			let confirmed_messages = lane
389				.confirm_delivery(
390					relayers_state.total_messages,
391					last_delivered_nonce,
392					&lane_data.relayers,
393				)
394				.map_err(Error::<T, I>::ReceptionConfirmation)?;
395
396			if let Some(confirmed_messages) = confirmed_messages {
397				// emit 'delivered' event
398				let received_range = confirmed_messages.begin..=confirmed_messages.end;
399				Self::deposit_event(Event::MessagesDelivered {
400					lane_id: lane_id.into(),
401					messages: confirmed_messages,
402				});
403
404				// if some new messages have been confirmed, reward relayers
405				let actually_rewarded_relayers = T::DeliveryConfirmationPayments::pay_reward(
406					lane_id,
407					lane_data.relayers,
408					&confirmation_relayer,
409					&received_range,
410				);
411
412				// update relayers state with actual numbers to compute actual weight below
413				relayers_state.unrewarded_relayer_entries = sp_std::cmp::min(
414					relayers_state.unrewarded_relayer_entries,
415					actually_rewarded_relayers,
416				);
417				relayers_state.total_messages = sp_std::cmp::min(
418					relayers_state.total_messages,
419					received_range.checked_len().unwrap_or(MessageNonce::MAX),
420				);
421			};
422
423			log::trace!(
424				target: LOG_TARGET,
425				"Received messages delivery proof up to (and including) {} at lane {:?}",
426				last_delivered_nonce,
427				lane_id,
428			);
429
430			// notify others about messages delivery
431			T::OnMessagesDelivered::on_messages_delivered(
432				lane_id,
433				lane.data().queued_messages().saturating_len(),
434			);
435
436			// because of lags, the inbound lane state (`lane_data`) may have entries for
437			// already rewarded relayers and messages (if all entries are duplicated, then
438			// this transaction must be filtered out by our signed extension)
439			let actual_weight = T::WeightInfo::receive_messages_delivery_proof_weight(
440				&PreComputedSize(proof_size as usize),
441				&relayers_state,
442			);
443
444			Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes })
445		}
446	}
447
448	#[pallet::event]
449	#[pallet::generate_deposit(pub(super) fn deposit_event)]
450	pub enum Event<T: Config<I>, I: 'static = ()> {
451		/// Message has been accepted and is waiting to be delivered.
452		MessageAccepted {
453			/// Lane, which has accepted the message.
454			lane_id: T::LaneId,
455			/// Nonce of accepted message.
456			nonce: MessageNonce,
457		},
458		/// Messages have been received from the bridged chain.
459		MessagesReceived(
460			/// Result of received messages dispatch.
461			ReceivedMessages<
462				<T::MessageDispatch as MessageDispatch>::DispatchLevelResult,
463				T::LaneId,
464			>,
465		),
466		/// Messages in the inclusive range have been delivered to the bridged chain.
467		MessagesDelivered {
468			/// Lane for which the delivery has been confirmed.
469			lane_id: T::LaneId,
470			/// Delivered messages.
471			messages: DeliveredMessages,
472		},
473	}
474
475	#[pallet::error]
476	#[derive(PartialEq, Eq)]
477	pub enum Error<T, I = ()> {
478		/// Pallet is not in Normal operating mode.
479		NotOperatingNormally,
480		/// Error that is reported by the lanes manager.
481		LanesManager(LanesManagerError),
482		/// Message has been treated as invalid by the pallet logic.
483		MessageRejectedByPallet(VerificationError),
484		/// The transaction brings too many messages.
485		TooManyMessagesInTheProof,
486		/// Invalid messages has been submitted.
487		InvalidMessagesProof,
488		/// Invalid messages delivery proof has been submitted.
489		InvalidMessagesDeliveryProof,
490		/// The relayer has declared invalid unrewarded relayers state in the
491		/// `receive_messages_delivery_proof` call.
492		InvalidUnrewardedRelayersState,
493		/// The cumulative dispatch weight, passed by relayer is not enough to cover dispatch
494		/// of all bundled messages.
495		InsufficientDispatchWeight,
496		/// Error confirming messages receival.
497		ReceptionConfirmation(ReceptionConfirmationError),
498		/// Error generated by the `OwnedBridgeModule` trait.
499		BridgeModule(bp_runtime::OwnedBridgeModuleError),
500	}
501
502	/// Optional pallet owner.
503	///
504	/// Pallet owner has a right to halt all pallet operations and then resume it. If it is
505	/// `None`, then there are no direct ways to halt/resume pallet operations, but other
506	/// runtime methods may still be used to do that (i.e. democracy::referendum to update halt
507	/// flag directly or call the `set_operating_mode`).
508	#[pallet::storage]
509	#[pallet::getter(fn module_owner)]
510	pub type PalletOwner<T: Config<I>, I: 'static = ()> = StorageValue<_, T::AccountId>;
511
512	/// The current operating mode of the pallet.
513	///
514	/// Depending on the mode either all, some, or no transactions will be allowed.
515	#[pallet::storage]
516	#[pallet::getter(fn operating_mode)]
517	pub type PalletOperatingMode<T: Config<I>, I: 'static = ()> =
518		StorageValue<_, MessagesOperatingMode, ValueQuery>;
519
520	// TODO: https://github.com/paritytech/parity-bridges-common/pull/2213: let's limit number of
521	// possible opened lanes && use it to constraint maps below
522
523	/// Map of lane id => inbound lane data.
524	#[pallet::storage]
525	pub type InboundLanes<T: Config<I>, I: 'static = ()> =
526		StorageMap<_, Blake2_128Concat, T::LaneId, StoredInboundLaneData<T, I>, OptionQuery>;
527
528	/// Map of lane id => outbound lane data.
529	#[pallet::storage]
530	pub type OutboundLanes<T: Config<I>, I: 'static = ()> = StorageMap<
531		Hasher = Blake2_128Concat,
532		Key = T::LaneId,
533		Value = OutboundLaneData,
534		QueryKind = OptionQuery,
535	>;
536
537	/// All queued outbound messages.
538	#[pallet::storage]
539	pub type OutboundMessages<T: Config<I>, I: 'static = ()> =
540		StorageMap<_, Blake2_128Concat, MessageKey<T::LaneId>, StoredMessagePayload<T, I>>;
541
542	#[pallet::genesis_config]
543	#[derive(DefaultNoBound)]
544	pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
545		/// Initial pallet operating mode.
546		pub operating_mode: MessagesOperatingMode,
547		/// Initial pallet owner.
548		pub owner: Option<T::AccountId>,
549		/// Opened lanes.
550		pub opened_lanes: Vec<T::LaneId>,
551		/// Dummy marker.
552		#[serde(skip)]
553		pub _phantom: sp_std::marker::PhantomData<I>,
554	}
555
556	#[pallet::genesis_build]
557	impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
558		fn build(&self) {
559			PalletOperatingMode::<T, I>::put(self.operating_mode);
560			if let Some(ref owner) = self.owner {
561				PalletOwner::<T, I>::put(owner);
562			}
563
564			for lane_id in &self.opened_lanes {
565				InboundLanes::<T, I>::insert(lane_id, InboundLaneData::opened());
566				OutboundLanes::<T, I>::insert(lane_id, OutboundLaneData::opened());
567			}
568		}
569	}
570
571	#[pallet::hooks]
572	impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
573		#[cfg(feature = "try-runtime")]
574		fn try_state(_n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
575			Self::do_try_state()
576		}
577	}
578
579	impl<T: Config<I>, I: 'static> Pallet<T, I> {
580		/// Get stored data of the outbound message with given nonce.
581		pub fn outbound_message_data(
582			lane: T::LaneId,
583			nonce: MessageNonce,
584		) -> Option<MessagePayload> {
585			OutboundMessages::<T, I>::get(MessageKey { lane_id: lane, nonce }).map(Into::into)
586		}
587
588		/// Prepare data, related to given inbound message.
589		pub fn inbound_message_data(
590			lane: T::LaneId,
591			payload: MessagePayload,
592			outbound_details: OutboundMessageDetails,
593		) -> InboundMessageDetails {
594			let mut dispatch_message = DispatchMessage {
595				key: MessageKey { lane_id: lane, nonce: outbound_details.nonce },
596				data: payload.into(),
597			};
598			InboundMessageDetails {
599				dispatch_weight: T::MessageDispatch::dispatch_weight(&mut dispatch_message),
600			}
601		}
602
603		/// Return outbound lane data.
604		pub fn outbound_lane_data(lane: T::LaneId) -> Option<OutboundLaneData> {
605			OutboundLanes::<T, I>::get(lane)
606		}
607
608		/// Return inbound lane data.
609		pub fn inbound_lane_data(
610			lane: T::LaneId,
611		) -> Option<InboundLaneData<AccountIdOf<BridgedChainOf<T, I>>>> {
612			InboundLanes::<T, I>::get(lane).map(|lane| lane.0)
613		}
614	}
615
616	#[cfg(any(feature = "try-runtime", test))]
617	impl<T: Config<I>, I: 'static> Pallet<T, I> {
618		/// Ensure the correctness of the state of this pallet.
619		pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
620			Self::do_try_state_for_outbound_lanes()
621		}
622
623		/// Ensure the correctness of the state of outbound lanes.
624		pub fn do_try_state_for_outbound_lanes() -> Result<(), sp_runtime::TryRuntimeError> {
625			use sp_runtime::traits::One;
626			use sp_std::vec::Vec;
627
628			// collect unpruned lanes
629			let mut unpruned_lanes = Vec::new();
630			for (lane_id, lane_data) in OutboundLanes::<T, I>::iter() {
631				let Some(expected_last_prunned_nonce) =
632					lane_data.oldest_unpruned_nonce.checked_sub(One::one())
633				else {
634					continue;
635				};
636
637				// collect message_nonces that were supposed to be pruned
638				let mut unpruned_message_nonces = Vec::new();
639				const MAX_MESSAGES_ITERATION: u64 = 16;
640				let start_nonce =
641					expected_last_prunned_nonce.checked_sub(MAX_MESSAGES_ITERATION).unwrap_or(0);
642				for current_nonce in start_nonce..=expected_last_prunned_nonce {
643					// check a message for current_nonce
644					if OutboundMessages::<T, I>::contains_key(MessageKey {
645						lane_id,
646						nonce: current_nonce,
647					}) {
648						unpruned_message_nonces.push(current_nonce);
649					}
650				}
651
652				if !unpruned_message_nonces.is_empty() {
653					log::warn!(
654						target: LOG_TARGET,
655						"do_try_state_for_outbound_lanes for lane_id: {lane_id:?} with lane_data: {lane_data:?} found unpruned_message_nonces: {unpruned_message_nonces:?}",
656					);
657					unpruned_lanes.push((lane_id, lane_data, unpruned_message_nonces));
658				}
659			}
660
661			// ensure messages before `oldest_unpruned_nonce` are really pruned.
662			ensure!(unpruned_lanes.is_empty(), "Found unpruned lanes!");
663
664			Ok(())
665		}
666	}
667}
668
669/// Structure, containing a validated message payload and all the info required
670/// to send it on the bridge.
671#[derive(Debug, PartialEq, Eq)]
672pub struct SendMessageArgs<T: Config<I>, I: 'static> {
673	lane_id: T::LaneId,
674	lane: OutboundLane<RuntimeOutboundLaneStorage<T, I>>,
675	payload: StoredMessagePayload<T, I>,
676}
677
678impl<T, I> bp_messages::source_chain::MessagesBridge<T::OutboundPayload, T::LaneId> for Pallet<T, I>
679where
680	T: Config<I>,
681	I: 'static,
682{
683	type Error = Error<T, I>;
684	type SendMessageArgs = SendMessageArgs<T, I>;
685
686	fn validate_message(
687		lane_id: T::LaneId,
688		message: &T::OutboundPayload,
689	) -> Result<SendMessageArgs<T, I>, Self::Error> {
690		// we can't accept any messages if the pallet is halted
691		ensure_normal_operating_mode::<T, I>()?;
692
693		// check lane
694		let lane = active_outbound_lane::<T, I>(lane_id)?;
695
696		Ok(SendMessageArgs {
697			lane_id,
698			lane,
699			payload: StoredMessagePayload::<T, I>::try_from(message.encode()).map_err(|_| {
700				Error::<T, I>::MessageRejectedByPallet(VerificationError::MessageTooLarge)
701			})?,
702		})
703	}
704
705	fn send_message(args: SendMessageArgs<T, I>) -> SendMessageArtifacts {
706		// save message in outbound storage and emit event
707		let mut lane = args.lane;
708		let message_len = args.payload.len();
709		let nonce = lane.send_message(args.payload);
710
711		// return number of messages in the queue to let sender know about its state
712		let enqueued_messages = lane.data().queued_messages().saturating_len();
713
714		log::trace!(
715			target: LOG_TARGET,
716			"Accepted message {} to lane {:?}. Message size: {:?}",
717			nonce,
718			args.lane_id,
719			message_len,
720		);
721
722		Pallet::<T, I>::deposit_event(Event::MessageAccepted {
723			lane_id: args.lane_id.into(),
724			nonce,
725		});
726
727		SendMessageArtifacts { nonce, enqueued_messages }
728	}
729}
730
731/// Ensure that the pallet is in normal operational mode.
732fn ensure_normal_operating_mode<T: Config<I>, I: 'static>() -> Result<(), Error<T, I>> {
733	if PalletOperatingMode::<T, I>::get() ==
734		MessagesOperatingMode::Basic(BasicOperatingMode::Normal)
735	{
736		return Ok(())
737	}
738
739	Err(Error::<T, I>::NotOperatingNormally)
740}
741
742/// Creates new inbound lane object, backed by runtime storage. Lane must be active.
743fn active_inbound_lane<T: Config<I>, I: 'static>(
744	lane_id: T::LaneId,
745) -> Result<InboundLane<RuntimeInboundLaneStorage<T, I>>, Error<T, I>> {
746	LanesManager::<T, I>::new()
747		.active_inbound_lane(lane_id)
748		.map_err(Error::LanesManager)
749}
750
751/// Creates new outbound lane object, backed by runtime storage. Lane must be active.
752fn active_outbound_lane<T: Config<I>, I: 'static>(
753	lane_id: T::LaneId,
754) -> Result<OutboundLane<RuntimeOutboundLaneStorage<T, I>>, Error<T, I>> {
755	LanesManager::<T, I>::new()
756		.active_outbound_lane(lane_id)
757		.map_err(Error::LanesManager)
758}
759
760/// Creates new outbound lane object, backed by runtime storage.
761fn any_state_outbound_lane<T: Config<I>, I: 'static>(
762	lane_id: T::LaneId,
763) -> Result<OutboundLane<RuntimeOutboundLaneStorage<T, I>>, Error<T, I>> {
764	LanesManager::<T, I>::new()
765		.any_state_outbound_lane(lane_id)
766		.map_err(Error::LanesManager)
767}
768
769/// Verify messages proof and return proved messages with decoded payload.
770fn verify_and_decode_messages_proof<T: Config<I>, I: 'static>(
771	proof: FromBridgedChainMessagesProof<HashOf<BridgedChainOf<T, I>>, T::LaneId>,
772	messages_count: u32,
773) -> Result<
774	ProvedMessages<T::LaneId, DispatchMessage<T::InboundPayload, T::LaneId>>,
775	VerificationError,
776> {
777	// `receive_messages_proof` weight formula and `MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX`
778	// check guarantees that the `message_count` is sane and Vec<Message> may be allocated.
779	// (tx with too many messages will either be rejected from the pool, or will fail earlier)
780	proofs::verify_messages_proof::<T, I>(proof, messages_count).map(|(lane, lane_data)| {
781		(
782			lane,
783			ProvedLaneMessages {
784				lane_state: lane_data.lane_state,
785				messages: lane_data.messages.into_iter().map(Into::into).collect(),
786			},
787		)
788	})
789}