1#![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
94pub 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 type RuntimeEvent: From<Event<Self, I>>
111 + IsType<<Self as frame_system::Config>::RuntimeEvent>;
112 type WeightInfo: WeightInfoExt;
114
115 type ThisChain: ChainWithMessages;
117 type BridgedChain: ChainWithMessages;
119 type BridgedHeaderChain: HeaderChain<Self::BridgedChain>;
121
122 type OutboundPayload: Parameter + Size;
124 type InboundPayload: Decode;
126 type LaneId: LaneIdType;
128
129 type DeliveryPayments: DeliveryPayments<Self::AccountId>;
131 type DeliveryConfirmationPayments: DeliveryConfirmationPayments<
134 Self::AccountId,
135 Self::LaneId,
136 >;
137 type OnMessagesDelivered: OnMessagesDelivered<Self::LaneId>;
139
140 type MessageDispatch: MessageDispatch<
142 DispatchPayload = Self::InboundPayload,
143 LaneId = Self::LaneId,
144 >;
145 }
146
147 pub type ThisChainOf<T, I> = <T as Config<I>>::ThisChain;
149 pub type BridgedChainOf<T, I> = <T as Config<I>>::BridgedChain;
151 pub type BridgedHeaderChainOf<T, I> = <T as Config<I>>::BridgedHeaderChain;
153 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 #[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 #[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 #[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 ensure!(
225 MessageNonce::from(messages_count) <=
226 BridgedChainOf::<T, I>::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX,
227 Error::<T, I>::TooManyMessagesInTheProof
228 );
229
230 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 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 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 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 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 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 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 #[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 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 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 let actually_rewarded_relayers = T::DeliveryConfirmationPayments::pay_reward(
406 lane_id,
407 lane_data.relayers,
408 &confirmation_relayer,
409 &received_range,
410 );
411
412 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 T::OnMessagesDelivered::on_messages_delivered(
432 lane_id,
433 lane.data().queued_messages().saturating_len(),
434 );
435
436 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 MessageAccepted {
453 lane_id: T::LaneId,
455 nonce: MessageNonce,
457 },
458 MessagesReceived(
460 ReceivedMessages<
462 <T::MessageDispatch as MessageDispatch>::DispatchLevelResult,
463 T::LaneId,
464 >,
465 ),
466 MessagesDelivered {
468 lane_id: T::LaneId,
470 messages: DeliveredMessages,
472 },
473 }
474
475 #[pallet::error]
476 #[derive(PartialEq, Eq)]
477 pub enum Error<T, I = ()> {
478 NotOperatingNormally,
480 LanesManager(LanesManagerError),
482 MessageRejectedByPallet(VerificationError),
484 TooManyMessagesInTheProof,
486 InvalidMessagesProof,
488 InvalidMessagesDeliveryProof,
490 InvalidUnrewardedRelayersState,
493 InsufficientDispatchWeight,
496 ReceptionConfirmation(ReceptionConfirmationError),
498 BridgeModule(bp_runtime::OwnedBridgeModuleError),
500 }
501
502 #[pallet::storage]
509 #[pallet::getter(fn module_owner)]
510 pub type PalletOwner<T: Config<I>, I: 'static = ()> = StorageValue<_, T::AccountId>;
511
512 #[pallet::storage]
516 #[pallet::getter(fn operating_mode)]
517 pub type PalletOperatingMode<T: Config<I>, I: 'static = ()> =
518 StorageValue<_, MessagesOperatingMode, ValueQuery>;
519
520 #[pallet::storage]
525 pub type InboundLanes<T: Config<I>, I: 'static = ()> =
526 StorageMap<_, Blake2_128Concat, T::LaneId, StoredInboundLaneData<T, I>, OptionQuery>;
527
528 #[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 #[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 pub operating_mode: MessagesOperatingMode,
547 pub owner: Option<T::AccountId>,
549 pub opened_lanes: Vec<T::LaneId>,
551 #[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 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 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 pub fn outbound_lane_data(lane: T::LaneId) -> Option<OutboundLaneData> {
605 OutboundLanes::<T, I>::get(lane)
606 }
607
608 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 pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
620 Self::do_try_state_for_outbound_lanes()
621 }
622
623 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 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 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 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!(unpruned_lanes.is_empty(), "Found unpruned lanes!");
663
664 Ok(())
665 }
666 }
667}
668
669#[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 ensure_normal_operating_mode::<T, I>()?;
692
693 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 let mut lane = args.lane;
708 let message_len = args.payload.len();
709 let nonce = lane.send_message(args.payload);
710
711 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
731fn 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
742fn 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
751fn 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
760fn 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
769fn 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 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}