Skip to content
lib.rs 71.6 KiB
Newer Older
// Copyright 2019-2021 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/>.

//! Runtime module that allows sending and receiving messages using lane concept:
//!
//! 1) the message is sent using `send_message()` call;
//! 2) every outbound message is assigned nonce;
//! 3) the messages are stored in the storage;
//! 4) external component (relay) delivers messages to bridged chain;
//! 5) messages are processed in order (ordered by assigned nonce);
//! 6) relay may send proof-of-delivery back to this chain.
//!
//! Once message is sent, its progress can be tracked by looking at module events.
//! The assigned nonce is reported using `MessageAccepted` event. When message is
//! delivered to the the bridged chain, it is reported using `MessagesDelivered` event.
//!
//! **IMPORTANT NOTE**: after generating weights (custom `WeighInfo` implementation) for
//! your runtime (where this module is plugged to), please add test for these weights.
//! The test should call the `ensure_weights_are_correct` function from this module.
//! If this test fails with your weights, then either weights are computed incorrectly,
//! or some benchmarks assumptions are broken for your runtime.

#![cfg_attr(not(feature = "std"), no_std)]
// Generated by `decl_event!`
#![allow(clippy::unused_unit)]
pub use crate::weights_ext::{
	ensure_able_to_receive_confirmation, ensure_able_to_receive_message, ensure_weights_are_correct, WeightInfoExt,
	EXPECTED_DEFAULT_MESSAGE_LENGTH,
};
use crate::inbound_lane::{InboundLane, InboundLaneStorage, ReceivalResult};
use crate::outbound_lane::{OutboundLane, OutboundLaneStorage, ReceivalConfirmationResult};
use crate::weights::WeightInfo;
	source_chain::{
		LaneMessageVerifier, MessageDeliveryAndDispatchPayment, OnDeliveryConfirmed, RelayersRewards, TargetHeaderChain,
	},
	target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain},
	total_unrewarded_messages, DeliveredMessages, InboundLaneData, LaneId, MessageData, MessageKey, MessageNonce,
	OperatingMode, OutboundLaneData, Parameter as MessagesParameter, UnrewardedRelayersState,
use codec::{Decode, Encode};
	decl_error, decl_event, decl_module, decl_storage,
	dispatch::DispatchResultWithPostInfo,
	weights::{DispatchClass, Pays, PostDispatchInfo, Weight},
use frame_system::{ensure_signed, RawOrigin};
use num_traits::{SaturatingAdd, Zero};
use sp_runtime::traits::BadOrigin;
use sp_std::{cell::RefCell, cmp::PartialOrd, marker::PhantomData, prelude::*};

mod inbound_lane;
mod outbound_lane;
mod weights_ext;
#[cfg(feature = "runtime-benchmarks")]
pub mod benchmarking;

#[cfg(test)]
mod mock;

/// The module configuration trait
pub trait Config<I = DefaultInstance>: frame_system::Config {
	/// They overarching event type.
	type Event: From<Event<Self, I>> + Into<<Self as frame_system::Config>::Event>;
	/// Benchmarks results from runtime we're plugged into.
	type WeightInfo: WeightInfoExt;
	/// Pallet parameter that is opaque to the pallet itself, but may be used by the runtime
	/// for integrating the pallet.
	///
	/// All pallet parameters may only be updated either by the root, or by the pallet owner.
	type Parameter: MessagesParameter;
	/// Maximal number of messages that may be pruned during maintenance. Maintenance occurs
	/// whenever new message is sent. The reason is that if you want to use lane, you should
	/// be ready to pay for its maintenance.
	type MaxMessagesToPruneAtOnce: Get<MessageNonce>;
	/// Maximal number of unrewarded relayer entries at inbound lane. Unrewarded means that the
	/// relayer has delivered messages, but either confirmations haven't been delivered back to the
	/// source chain, or we haven't received reward confirmations yet.
	/// This constant limits maximal number of entries in the `InboundLaneData::relayers`. Keep
	/// in mind that the same relayer account may take several (non-consecutive) entries in this
	/// set.
	type MaxUnrewardedRelayerEntriesAtInboundLane: Get<MessageNonce>;
	/// Maximal number of unconfirmed messages at inbound lane. Unconfirmed means that the
	/// message has been delivered, but either confirmations haven't been delivered back to the
	/// source chain, or we haven't received reward confirmations for these messages yet.
	///
	/// This constant limits difference between last message from last entry of the
	/// `InboundLaneData::relayers` and first message at the first entry.
	///
	/// There is no point of making this parameter lesser than MaxUnrewardedRelayerEntriesAtInboundLane,
	/// because then maximal number of relayer entries will be limited by maximal number of messages.
	///
	/// This value also represents maximal number of messages in single delivery transaction. Transaction
	/// that is declaring more messages than this value, will be rejected. Even if these messages are
	/// from different lanes.
	type MaxUnconfirmedMessagesAtInboundLane: Get<MessageNonce>;
	/// Payload type of outbound messages. This payload is dispatched on the bridged chain.
	type OutboundPayload: Parameter + Size;
	/// Message fee type of outbound messages. This fee is paid on this chain.
	type OutboundMessageFee: Default + From<u64> + PartialOrd + Parameter + SaturatingAdd + Zero;

	/// Payload type of inbound messages. This payload is dispatched on this chain.
	type InboundPayload: Decode;
	/// Message fee type of inbound messages. This fee is paid on the bridged chain.
	type InboundMessageFee: Decode;
	/// Identifier of relayer that deliver messages to this chain. Relayer reward is paid on the bridged chain.
	type InboundRelayer: Parameter;
	/// A type which can be turned into an AccountId from a 256-bit hash.
	///
	/// Used when deriving the shared relayer fund account.
	type AccountIdConverter: sp_runtime::traits::Convert<sp_core::hash::H256, Self::AccountId>;

	// Types that are used by outbound_lane (on source chain).

	/// Target header chain.
	type TargetHeaderChain: TargetHeaderChain<Self::OutboundPayload, Self::AccountId>;
	/// Message payload verifier.
	type LaneMessageVerifier: LaneMessageVerifier<Self::AccountId, Self::OutboundPayload, Self::OutboundMessageFee>;
	/// Message delivery payment.
	type MessageDeliveryAndDispatchPayment: MessageDeliveryAndDispatchPayment<Self::AccountId, Self::OutboundMessageFee>;
	/// Handler for delivered messages.
	type OnDeliveryConfirmed: OnDeliveryConfirmed;

	// Types that are used by inbound_lane (on target chain).

	/// Source header chain, as it is represented on target chain.
	type SourceHeaderChain: SourceHeaderChain<Self::InboundMessageFee>;
	type MessageDispatch: MessageDispatch<
		Self::AccountId,
		Self::InboundMessageFee,
		DispatchPayload = Self::InboundPayload,
	>;
/// Shortcut to messages proof type for Config.
type MessagesProofOf<T, I> =
	<<T as Config<I>>::SourceHeaderChain as SourceHeaderChain<<T as Config<I>>::InboundMessageFee>>::MessagesProof;
/// Shortcut to messages delivery proof type for Config.
type MessagesDeliveryProofOf<T, I> = <<T as Config<I>>::TargetHeaderChain as TargetHeaderChain<
	<T as Config<I>>::OutboundPayload,
	<T as frame_system::Config>::AccountId,
>>::MessagesDeliveryProof;
Hernando Castano's avatar
Hernando Castano committed
	pub enum Error for Pallet<T: Config<I>, I: Instance> {
		/// All pallet operations are halted.
		Halted,
		/// Message has been treated as invalid by chain verifier.
		MessageRejectedByChainVerifier,
		/// Message has been treated as invalid by lane verifier.
		MessageRejectedByLaneVerifier,
		/// Submitter has failed to pay fee for delivering and dispatching messages.
		FailedToWithdrawMessageFee,
		/// The transaction brings too many messages.
		TooManyMessagesInTheProof,
		/// Invalid messages has been submitted.
		InvalidMessagesProof,
		/// Invalid messages dispatch weight has been declared by the relayer.
		InvalidMessagesDispatchWeight,
		/// Invalid messages delivery proof has been submitted.
		InvalidMessagesDeliveryProof,
		/// The bridged chain has invalid `UnrewardedRelayers` in its storage (fatal for the lane).
		InvalidUnrewardedRelayers,
		/// The relayer has declared invalid unrewarded relayers state in the `receive_messages_delivery_proof` call.
		InvalidUnrewardedRelayersState,
		/// The message someone is trying to work with (i.e. increase fee) is already-delivered.
		MessageIsAlreadyDelivered,
		/// The message someone is trying to work with (i.e. increase fee) is not yet sent.
		MessageIsNotYetSent,
		/// The number of actually confirmed messages is going to be larger than the number of messages in the proof.
		/// This may mean that this or bridged chain storage is corrupted.
		TryingToConfirmMoreMessagesThanExpected,
Hernando Castano's avatar
Hernando Castano committed
	trait Store for Pallet<T: Config<I>, I: Instance = DefaultInstance> as BridgeMessages {
		/// Optional pallet owner.
		///
		/// Pallet owner has a right to halt all pallet operations and then resume it. If it is
		/// `None`, then there are no direct ways to halt/resume pallet operations, but other
		/// runtime methods may still be used to do that (i.e. democracy::referendum to update halt
		/// flag directly or call the `halt_operations`).
Hernando Castano's avatar
Hernando Castano committed
		pub PalletOwner get(fn module_owner): Option<T::AccountId>;
		/// The current operating mode of the pallet.
		///
		/// Depending on the mode either all, some, or no transactions will be allowed.
		pub PalletOperatingMode get(fn operating_mode) config(): OperatingMode;
		/// Map of lane id => inbound lane data.
		pub InboundLanes: map hasher(blake2_128_concat) LaneId => InboundLaneData<T::InboundRelayer>;
		/// Map of lane id => outbound lane data.
		pub OutboundLanes: map hasher(blake2_128_concat) LaneId => OutboundLaneData;
		/// All queued outbound messages.
		pub OutboundMessages: map hasher(blake2_128_concat) MessageKey => Option<MessageData<T::OutboundMessageFee>>;
	add_extra_genesis {
		config(phantom): sp_std::marker::PhantomData<I>;
		config(owner): Option<T::AccountId>;
		build(|config| {
			if let Some(ref owner) = config.owner {
Hernando Castano's avatar
Hernando Castano committed
				<PalletOwner<T, I>>::put(owner);
	pub enum Event<T, I = DefaultInstance>
	where
		AccountId = <T as frame_system::Config>::AccountId,
		Parameter = <T as Config<I>>::Parameter,
		/// Pallet parameter has been updated.
		ParameterUpdated(Parameter),
		/// Message has been accepted and is waiting to be delivered.
		MessageAccepted(LaneId, MessageNonce),
		/// Messages in the inclusive range have been delivered to the bridged chain.
		MessagesDelivered(LaneId, DeliveredMessages),
		/// Phantom member, never used.
		Dummy(PhantomData<(AccountId, I)>),
	pub struct Module<T: Config<I>, I: Instance = DefaultInstance> for enum Call where origin: T::Origin {
		/// Deposit one of this module's events by using the default implementation.
		fn deposit_event() = default;

		/// Ensure runtime invariants.
		fn on_runtime_upgrade() -> Weight {
			let reads = T::MessageDeliveryAndDispatchPayment::initialize(
				&Self::relayer_fund_account_id()
			);
			T::DbWeight::get().reads(reads as u64)
		}

Hernando Castano's avatar
Hernando Castano committed
		/// Change `PalletOwner`.
Hernando Castano's avatar
Hernando Castano committed
		/// May only be called either by root, or by `PalletOwner`.
		#[weight = (T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational)]
		pub fn set_owner(origin, new_owner: Option<T::AccountId>) {
			ensure_owner_or_root::<T, I>(origin)?;
			match new_owner {
				Some(new_owner) => {
Hernando Castano's avatar
Hernando Castano committed
					PalletOwner::<T, I>::put(&new_owner);
					log::info!(target: "runtime::bridge-messages", "Setting pallet Owner to: {:?}", new_owner);
Hernando Castano's avatar
Hernando Castano committed
					PalletOwner::<T, I>::kill();
					log::info!(target: "runtime::bridge-messages", "Removed Owner of pallet.");
		/// Halt or resume all/some pallet operations.
Hernando Castano's avatar
Hernando Castano committed
		/// May only be called either by root, or by `PalletOwner`.
		#[weight = (T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational)]
		pub fn set_operating_mode(origin, operating_mode: OperatingMode) {
			ensure_owner_or_root::<T, I>(origin)?;
			<PalletOperatingMode<I>>::put(operating_mode);
			log::info!(
				target: "runtime::bridge-messages",
				"Setting messages pallet operating mode to {:?}.",
				operating_mode,
			);
Hernando Castano's avatar
Hernando Castano committed
		/// May only be called either by root, or by `PalletOwner`.
		///
		/// The weight is: single read for permissions check + 2 writes for parameter value and event.
		#[weight = (T::DbWeight::get().reads_writes(1, 2), DispatchClass::Operational)]
		pub fn update_pallet_parameter(origin, parameter: T::Parameter) {
			ensure_owner_or_root::<T, I>(origin)?;
			parameter.save();
			Self::deposit_event(RawEvent::ParameterUpdated(parameter));
		}

		/// Send message over lane.
		#[weight = T::WeightInfo::send_message_weight(payload)]
		pub fn send_message(
			origin,
			lane_id: LaneId,
			payload: T::OutboundPayload,
			delivery_and_dispatch_fee: T::OutboundMessageFee,
		) -> DispatchResultWithPostInfo {
			ensure_normal_operating_mode::<T, I>()?;
			let submitter = origin.into().map_err(|_| BadOrigin)?;
			// initially, actual (post-dispatch) weight is equal to pre-dispatch weight
			let mut actual_weight = T::WeightInfo::send_message_weight(&payload);

			// let's first check if message can be delivered to target chain
			T::TargetHeaderChain::verify_message(&payload)
				.map_err(|err| {
Hernando Castano's avatar
Hernando Castano committed
					log::trace!(
						target: "runtime::bridge-messages",
						"Message to lane {:?} is rejected by target chain: {:?}",
						lane_id,
						err,
					);

					Error::<T, I>::MessageRejectedByChainVerifier
				})?;

			// now let's enforce any additional lane rules
			let mut lane = outbound_lane::<T, I>(lane_id);
			T::LaneMessageVerifier::verify_message(
				&submitter,
				&delivery_and_dispatch_fee,
				&lane_id,
Hernando Castano's avatar
Hernando Castano committed
				log::trace!(
					target: "runtime::bridge-messages",
					"Message to lane {:?} is rejected by lane verifier: {:?}",
					lane_id,
					err,
				);

				Error::<T, I>::MessageRejectedByLaneVerifier
			})?;

			// let's withdraw delivery and dispatch fee from submitter
			T::MessageDeliveryAndDispatchPayment::pay_delivery_and_dispatch_fee(
				&submitter,
				&delivery_and_dispatch_fee,
				&Self::relayer_fund_account_id(),
Hernando Castano's avatar
Hernando Castano committed
				log::trace!(
					target: "runtime::bridge-messages",
					"Message to lane {:?} is rejected because submitter {:?} is unable to pay fee {:?}: {:?}",
					lane_id,
					submitter,
					delivery_and_dispatch_fee,
					err,
				);

				Error::<T, I>::FailedToWithdrawMessageFee
			})?;

			// finally, save message in outbound storage and emit event
			let encoded_payload = payload.encode();
			let encoded_payload_len = encoded_payload.len();
			let nonce = lane.send_message(MessageData {
				fee: delivery_and_dispatch_fee,
			});

			// message sender pays for pruning at most `MaxMessagesToPruneAtOnce` messages
			// the cost of pruning every message is roughly single db write
			// => lets refund sender if less than `MaxMessagesToPruneAtOnce` messages pruned
			let max_messages_to_prune = T::MaxMessagesToPruneAtOnce::get();
			let pruned_messages = lane.prune_messages(max_messages_to_prune);
			if let Some(extra_messages) = max_messages_to_prune.checked_sub(pruned_messages) {
				actual_weight = actual_weight.saturating_sub(T::DbWeight::get().writes(extra_messages));
			}
Hernando Castano's avatar
Hernando Castano committed
			log::trace!(
				target: "runtime::bridge-messages",
				"Accepted message {} to lane {:?}. Message size: {:?}",
			Self::deposit_event(RawEvent::MessageAccepted(lane_id, nonce));
			Ok(PostDispatchInfo {
				actual_weight: Some(actual_weight),
				pays_fee: Pays::Yes,
			})
		/// Pay additional fee for the message.
		#[weight = T::WeightInfo::maximal_increase_message_fee()]
		pub fn increase_message_fee(
			origin,
			lane_id: LaneId,
			nonce: MessageNonce,
			additional_fee: T::OutboundMessageFee,
		) -> DispatchResultWithPostInfo {
			ensure_not_halted::<T, I>()?;
			// if someone tries to pay for already-delivered message, we're rejecting this intention
			// (otherwise this additional fee will be locked forever in relayers fund)
			//
			// if someone tries to pay for not-yet-sent message, we're rejeting this intention, or
			// we're risking to have mess in the storage
			let lane = outbound_lane::<T, I>(lane_id);
			ensure!(nonce > lane.data().latest_received_nonce, Error::<T, I>::MessageIsAlreadyDelivered);
			ensure!(nonce <= lane.data().latest_generated_nonce, Error::<T, I>::MessageIsNotYetSent);

			// withdraw additional fee from submitter
			let submitter = origin.into().map_err(|_| BadOrigin)?;
			T::MessageDeliveryAndDispatchPayment::pay_delivery_and_dispatch_fee(
				&submitter,
				&additional_fee,
				&Self::relayer_fund_account_id(),
			).map_err(|err| {
Hernando Castano's avatar
Hernando Castano committed
				log::trace!(
					target: "runtime::bridge-messages",
					"Submitter {:?} can't pay additional fee {:?} for the message {:?}/{:?}: {:?}",
					submitter,
					additional_fee,
					lane_id,
					nonce,
					err,
				);

				Error::<T, I>::FailedToWithdrawMessageFee
			})?;

			// and finally update fee in the storage
			let message_key = MessageKey { lane_id, nonce };
			let message_size = OutboundMessages::<T, I>::mutate(message_key, |message_data| {
				// saturating_add is fine here - overflow here means that someone controls all
				// chain funds, which shouldn't ever happen + `pay_delivery_and_dispatch_fee`
				// above will fail before we reach here
				let message_data = message_data
					.as_mut()
					.expect("the message is sent and not yet delivered; so it is in the storage; qed");
				message_data.fee = message_data.fee.saturating_add(&additional_fee);
			// compute actual dispatch weight that depends on the stored message size
			let actual_weight = sp_std::cmp::min(
				T::WeightInfo::maximal_increase_message_fee(),
				T::WeightInfo::increase_message_fee(message_size as _),
			);

			Ok(PostDispatchInfo {
				actual_weight: Some(actual_weight),
				pays_fee: Pays::Yes,
			})
		/// Receive messages proof from bridged chain.
		///
		/// The weight of the call assumes that the transaction always brings outbound lane
		/// state update. Because of that, the submitter (relayer) has no benefit of not including
		/// this data in the transaction, so reward confirmations lags should be minimal.
		#[weight = T::WeightInfo::receive_messages_proof_weight(proof, *messages_count, *dispatch_weight)]
		pub fn receive_messages_proof(
			origin,
			relayer_id_at_bridged_chain: T::InboundRelayer,
			proof: MessagesProofOf<T, I>,
			messages_count: u32,
		) -> DispatchResultWithPostInfo {
			ensure_not_halted::<T, I>()?;
			let relayer_id_at_this_chain = ensure_signed(origin)?;
			// reject transactions that are declaring too many messages
			ensure!(
				MessageNonce::from(messages_count) <= T::MaxUnconfirmedMessagesAtInboundLane::get(),
				Error::<T, I>::TooManyMessagesInTheProof
			);

			// why do we need to know the weight of this (`receive_messages_proof`) call? Because
			// we may want to return some funds for not-dispatching (or partially dispatching) some
			// messages to the call origin (relayer). And this is done by returning actual weight
			// from the call. But we only know dispatch weight of every messages. So to refund relayer
			// because we have not dispatched Message, we need to:
			//
			// ActualWeight = DeclaredWeight - Message.DispatchWeight
			//
			// The DeclaredWeight is exactly what's computed here. Unfortunately it is impossible
			// to get pre-computed value (and it has been already computed by the executive).
			let declared_weight = T::WeightInfo::receive_messages_proof_weight(
				&proof,
				messages_count,
				dispatch_weight,
			);
			let mut actual_weight = declared_weight;

			// verify messages proof && convert proof into messages
			let messages = verify_and_decode_messages_proof::<
				T::SourceHeaderChain,
				T::InboundMessageFee,
				T::InboundPayload,
Hernando Castano's avatar
Hernando Castano committed
					log::trace!(
						target: "runtime::bridge-messages",
						"Rejecting invalid messages proof: {:?}",
						err,
					);
					Error::<T, I>::InvalidMessagesProof
				})?;
			// verify that relayer is paying actual dispatch weight
			let actual_dispatch_weight: Weight = messages
				.values()
				.map(|lane_messages| lane_messages
					.messages
					.iter()
					.map(T::MessageDispatch::dispatch_weight)
					.fold(0, |sum, weight| sum.saturating_add(&weight))
				.fold(0, |sum, weight| sum.saturating_add(weight));
			if dispatch_weight < actual_dispatch_weight {
Hernando Castano's avatar
Hernando Castano committed
				log::trace!(
					target: "runtime::bridge-messages",
					"Rejecting messages proof because of dispatch weight mismatch: declared={}, expected={}",
					dispatch_weight,
					actual_dispatch_weight,
				);

				return Err(Error::<T, I>::InvalidMessagesDispatchWeight.into());
			}

			// dispatch messages and (optionally) update lane(s) state(s)
			let mut total_messages = 0;
			let mut valid_messages = 0;
			for (lane_id, lane_data) in messages {
				let mut lane = inbound_lane::<T, I>(lane_id);

				if let Some(lane_state) = lane_data.lane_state {
					let updated_latest_confirmed_nonce = lane.receive_state_update(lane_state);
					if let Some(updated_latest_confirmed_nonce) = updated_latest_confirmed_nonce {
Hernando Castano's avatar
Hernando Castano committed
						log::trace!(
							target: "runtime::bridge-messages",
							"Received lane {:?} state update: latest_confirmed_nonce={}",
							lane_id,
							updated_latest_confirmed_nonce,
						);
					}
				}

				for message in lane_data.messages {
					debug_assert_eq!(message.key.lane_id, lane_id);

					total_messages += 1;
					let dispatch_weight = T::MessageDispatch::dispatch_weight(&message);
					let receival_result = lane.receive_message::<T::MessageDispatch, T::AccountId>(
						&relayer_id_at_bridged_chain,
						&relayer_id_at_this_chain,
						message.key.nonce,
						message.data,
					);

					// note that we're returning unspent weight to relayer even if message has been
					// rejected by the lane. This allows relayers to submit spam transactions with
					// e.g. the same set of already delivered messages over and over again, without
					// losing funds for messages dispatch. But keep in mind that relayer pays base
					// delivery transaction cost anyway. And base cost covers everything except
					// dispatch, so we have a balance here.
					let (unspent_weight, refund_pay_dispatch_fee) = match receival_result {
						ReceivalResult::Dispatched(dispatch_result) => {
							valid_messages += 1;
							(dispatch_result.unspent_weight, !dispatch_result.dispatch_fee_paid_during_dispatch)
						},
						ReceivalResult::InvalidNonce
							| ReceivalResult::TooManyUnrewardedRelayers
							| ReceivalResult::TooManyUnconfirmedMessages => (dispatch_weight, true),
					};
					actual_weight = actual_weight
						.saturating_sub(sp_std::cmp::min(unspent_weight, dispatch_weight))
						.saturating_sub(
							// delivery call weight formula assumes that the fee is paid at
							// this (target) chain. If the message is prepaid at the source
							// chain, let's refund relayer with this extra cost.
							if refund_pay_dispatch_fee {
								T::WeightInfo::pay_inbound_dispatch_fee_overhead()
							} else {
								0
							}
						);
Hernando Castano's avatar
Hernando Castano committed
			log::trace!(
				target: "runtime::bridge-messages",
				"Received messages: total={}, valid={}. Weight used: {}/{}",
				total_messages,
				valid_messages,
				actual_weight,
				declared_weight,
			Ok(PostDispatchInfo {
				actual_weight: Some(actual_weight),
				pays_fee: Pays::Yes,
			})
		/// Receive messages delivery proof from bridged chain.
		#[weight = T::WeightInfo::receive_messages_delivery_proof_weight(
			proof,
			relayers_state,
			T::DbWeight::get(),
		)]
		pub fn receive_messages_delivery_proof(
			origin,
			proof: MessagesDeliveryProofOf<T, I>,
			relayers_state: UnrewardedRelayersState,
			ensure_not_halted::<T, I>()?;
			// why do we need to know the weight of this (`receive_messages_delivery_proof`) call? Because
			// we may want to return some funds for messages that are not processed by the delivery callback,
			// or if their actual processing weight is less than accounted by weight formula.
			// So to refund relayer, we need to:
			//
			// ActualWeight = DeclaredWeight - UnspentCallbackWeight
			//
			// The DeclaredWeight is exactly what's computed here. Unfortunately it is impossible
			// to get pre-computed value (and it has been already computed by the executive).
			let single_message_callback_overhead = T::WeightInfo::single_message_callback_overhead(T::DbWeight::get());
			let declared_weight = T::WeightInfo::receive_messages_delivery_proof_weight(
				&proof,
				&relayers_state,
				T::DbWeight::get(),
			);
			let mut actual_weight = declared_weight;

			let confirmation_relayer = ensure_signed(origin)?;
			let (lane_id, lane_data) = T::TargetHeaderChain::verify_messages_delivery_proof(proof).map_err(|err| {
Hernando Castano's avatar
Hernando Castano committed
				log::trace!(
					target: "runtime::bridge-messages",
					"Rejecting invalid messages delivery proof: {:?}",
					err,
				);

				Error::<T, I>::InvalidMessagesDeliveryProof
			})?;

			// verify that the relayer has declared correct `lane_data::relayers` state
			// (we only care about total number of entries and messages, because this affects call weight)
			ensure!(
				total_unrewarded_messages(&lane_data.relayers)
					.unwrap_or(MessageNonce::MAX) == relayers_state.total_messages
					&& lane_data.relayers.len() as MessageNonce == relayers_state.unrewarded_relayer_entries,
				Error::<T, I>::InvalidUnrewardedRelayersState
			);

			// mark messages as delivered
			let mut lane = outbound_lane::<T, I>(lane_id);
			let mut relayers_rewards: RelayersRewards<_, T::OutboundMessageFee> = RelayersRewards::new();
			let last_delivered_nonce = lane_data.last_delivered_nonce();
			let confirmed_messages = match lane.confirm_delivery(
				relayers_state.total_messages,
				last_delivered_nonce,
				&lane_data.relayers,
			) {
				ReceivalConfirmationResult::ConfirmedMessages(confirmed_messages) => Some(confirmed_messages),
				ReceivalConfirmationResult::NoNewConfirmations => None,
				ReceivalConfirmationResult::TryingToConfirmMoreMessagesThanExpected(to_confirm_messages_count) => {
					log::trace!(
						target: "runtime::bridge-messages",
						"Messages delivery proof contains too many messages to confirm: {} vs declared {}",
						to_confirm_messages_count,
						relayers_state.total_messages,
					);

					fail!(Error::<T, I>::TryingToConfirmMoreMessagesThanExpected);
				},
				error => {
					log::trace!(
						target: "runtime::bridge-messages",
						"Messages delivery proof contains invalid unrewarded relayers vec: {:?}",
						error,
					);

					fail!(Error::<T, I>::InvalidUnrewardedRelayers);
				},
			};
			if let Some(confirmed_messages) = confirmed_messages {
				// handle messages delivery confirmation
				let preliminary_callback_overhead = relayers_state.total_messages.saturating_mul(
					single_message_callback_overhead
				);
				let actual_callback_weight = T::OnDeliveryConfirmed::on_messages_delivered(
					&lane_id,
					&confirmed_messages,
				);
				match preliminary_callback_overhead.checked_sub(actual_callback_weight) {
					Some(difference) if difference == 0 => (),
					Some(difference) => {
						log::trace!(
							target: "runtime::bridge-messages",
							"Messages delivery callback has returned unspent weight to refund the submitter: \
							{} - {} = {}",
							preliminary_callback_overhead,
							actual_callback_weight,
							difference,
						);
						actual_weight -= difference;
					},
					None => {
						debug_assert!(false, "The delivery confirmation callback is wrong");
						log::trace!(
							target: "runtime::bridge-messages",
							"Messages delivery callback has returned more weight that it may spent: \
							{} vs {}",
							preliminary_callback_overhead,
							actual_callback_weight,
						);
					}
				}

				// emit 'delivered' event
				let received_range = confirmed_messages.begin..=confirmed_messages.end;
				Self::deposit_event(RawEvent::MessagesDelivered(lane_id, confirmed_messages));
				// remember to reward relayers that have delivered messages
				// this loop is bounded by `T::MaxUnrewardedRelayerEntriesAtInboundLane` on the bridged chain
				for entry in lane_data.relayers {
					let nonce_begin = sp_std::cmp::max(entry.messages.begin, *received_range.start());
					let nonce_end = sp_std::cmp::min(entry.messages.end, *received_range.end());
					// loop won't proceed if current entry is ahead of received range (begin > end).
					// this loop is bound by `T::MaxUnconfirmedMessagesAtInboundLane` on the bridged chain
					let mut relayer_reward = relayers_rewards.entry(entry.relayer).or_default();
					for nonce in nonce_begin..nonce_end + 1 {
						let message_data = OutboundMessages::<T, I>::get(MessageKey {
							lane_id,
							nonce,
						}).expect("message was just confirmed; we never prune unconfirmed messages; qed");
						relayer_reward.reward = relayer_reward.reward.saturating_add(&message_data.fee);
						relayer_reward.messages += 1;
			// if some new messages have been confirmed, reward relayers
			if !relayers_rewards.is_empty() {
				let relayer_fund_account = Self::relayer_fund_account_id();
				<T as Config<I>>::MessageDeliveryAndDispatchPayment::pay_relayers_rewards(
					&confirmation_relayer,
					relayers_rewards,
					&relayer_fund_account,
				);
			}

Hernando Castano's avatar
Hernando Castano committed
			log::trace!(
				target: "runtime::bridge-messages",
				"Received messages delivery proof up to (and including) {} at lane {:?}",
				last_delivered_nonce,
			Ok(PostDispatchInfo {
				actual_weight: Some(actual_weight),
				pays_fee: Pays::Yes,
			})
Hernando Castano's avatar
Hernando Castano committed
impl<T: Config<I>, I: Instance> Pallet<T, I> {
	/// Get stored data of the outbound message with given nonce.
	pub fn outbound_message_data(lane: LaneId, nonce: MessageNonce) -> Option<MessageData<T::OutboundMessageFee>> {
		OutboundMessages::<T, I>::get(MessageKey { lane_id: lane, nonce })
	}

	/// Get nonce of latest generated message at given outbound lane.
	pub fn outbound_latest_generated_nonce(lane: LaneId) -> MessageNonce {
		OutboundLanes::<I>::get(&lane).latest_generated_nonce
	}

	/// Get nonce of latest confirmed message at given outbound lane.
	pub fn outbound_latest_received_nonce(lane: LaneId) -> MessageNonce {
		OutboundLanes::<I>::get(&lane).latest_received_nonce
	}

	/// Get nonce of latest received message at given inbound lane.
	pub fn inbound_latest_received_nonce(lane: LaneId) -> MessageNonce {
		InboundLanes::<T, I>::get(&lane).last_delivered_nonce()
	}

	/// Get nonce of latest confirmed message at given inbound lane.
	pub fn inbound_latest_confirmed_nonce(lane: LaneId) -> MessageNonce {
		InboundLanes::<T, I>::get(&lane).last_confirmed_nonce

	/// Get state of unrewarded relayers set.
	pub fn inbound_unrewarded_relayers_state(lane: bp_messages::LaneId) -> bp_messages::UnrewardedRelayersState {
		let relayers = InboundLanes::<T, I>::get(&lane).relayers;
		bp_messages::UnrewardedRelayersState {
			unrewarded_relayer_entries: relayers.len() as _,
			messages_in_oldest_entry: relayers
				.front()
				.map(|entry| 1 + entry.messages.end - entry.messages.begin)
				.unwrap_or(0),
			total_messages: total_unrewarded_messages(&relayers).unwrap_or(MessageNonce::MAX),

	/// AccountId of the shared relayer fund account.
	///
	/// This account is passed to `MessageDeliveryAndDispatchPayment` trait, and depending
	/// on the implementation it can be used to store relayers rewards.
	/// See [InstantCurrencyPayments] for a concrete implementation.
	pub fn relayer_fund_account_id() -> T::AccountId {
		use sp_runtime::traits::Convert;
		let encoded_id = bp_runtime::derive_relayer_fund_account_id(bp_runtime::NO_INSTANCE_ID);
		T::AccountIdConverter::convert(encoded_id)
	}
/// Getting storage keys for messages and lanes states. These keys are normally used when building
/// messages and lanes states proofs.
///
/// Keep in mind that all functions in this module are **NOT** using passed `T` argument, so any
/// runtime can be passed. E.g. if you're verifying proof from Runtime1 in Runtime2, you only have
/// access to Runtime2 and you may pass it to the functions, where required. This is because our
/// maps are not using any Runtime-specific data in the keys.
///
/// On the other side, passing correct instance is required. So if proof has been crafted by the
/// Instance1, you should verify it using Instance1. This is inconvenient if you're using different
/// instances on different sides of the bridge. I.e. in Runtime1 it is Instance2, but on Runtime2
/// it is Instance42. But there's no other way, but to craft this key manually (which is what I'm
/// trying to avoid here) - by using strings like "Instance2", "OutboundMessages", etc.
pub mod storage_keys {
	use super::*;
	use frame_support::{traits::Instance, StorageHasher};
	use sp_core::storage::StorageKey;

	/// Storage key of the outbound message in the runtime storage.
	pub fn message_key<I: Instance>(lane: &LaneId, nonce: MessageNonce) -> StorageKey {
		storage_map_final_key::<I>("OutboundMessages", &MessageKey { lane_id: *lane, nonce }.encode())
	}

	/// Storage key of the outbound message lane state in the runtime storage.
	pub fn outbound_lane_data_key<I: Instance>(lane: &LaneId) -> StorageKey {
		storage_map_final_key::<I>("OutboundLanes", lane)
	}

	/// Storage key of the inbound message lane state in the runtime storage.
	pub fn inbound_lane_data_key<I: Instance>(lane: &LaneId) -> StorageKey {
		storage_map_final_key::<I>("InboundLanes", lane)
	}

	/// This is a copypaste of the `frame_support::storage::generator::StorageMap::storage_map_final_key`.
	fn storage_map_final_key<I: Instance>(map_name: &str, key: &[u8]) -> StorageKey {
		let module_prefix_hashed = frame_support::Twox128::hash(I::PREFIX.as_bytes());
		let storage_prefix_hashed = frame_support::Twox128::hash(map_name.as_bytes());
		let key_hashed = frame_support::Blake2_128Concat::hash(key);

		let mut final_key =
			Vec::with_capacity(module_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.len());

		final_key.extend_from_slice(&module_prefix_hashed[..]);
		final_key.extend_from_slice(&storage_prefix_hashed[..]);
		final_key.extend_from_slice(key_hashed.as_ref());

		StorageKey(final_key)
Hernando Castano's avatar
Hernando Castano committed
/// Ensure that the origin is either root, or `PalletOwner`.
fn ensure_owner_or_root<T: Config<I>, I: Instance>(origin: T::Origin) -> Result<(), BadOrigin> {
	match origin.into() {
		Ok(RawOrigin::Root) => Ok(()),
Hernando Castano's avatar
Hernando Castano committed
		Ok(RawOrigin::Signed(ref signer)) if Some(signer) == Pallet::<T, I>::module_owner().as_ref() => Ok(()),
/// Ensure that the pallet is in normal operational mode.
fn ensure_normal_operating_mode<T: Config<I>, I: Instance>() -> Result<(), Error<T, I>> {
	if PalletOperatingMode::<I>::get() != OperatingMode::Normal {
		Err(Error::<T, I>::Halted)
	} else {
		Ok(())
	}
}

/// Ensure that the pallet is not halted.
fn ensure_not_halted<T: Config<I>, I: Instance>() -> Result<(), Error<T, I>> {
	if PalletOperatingMode::<I>::get() == OperatingMode::Halted {
		Err(Error::<T, I>::Halted)
	} else {
		Ok(())
	}
}

/// Creates new inbound lane object, backed by runtime storage.
fn inbound_lane<T: Config<I>, I: Instance>(lane_id: LaneId) -> InboundLane<RuntimeInboundLaneStorage<T, I>> {
	InboundLane::new(inbound_lane_storage::<T, I>(lane_id))
}

/// Creates new runtime inbound lane storage.
fn inbound_lane_storage<T: Config<I>, I: Instance>(lane_id: LaneId) -> RuntimeInboundLaneStorage<T, I> {
	RuntimeInboundLaneStorage {
		cached_data: RefCell::new(None),
		_phantom: Default::default(),
}

/// Creates new outbound lane object, backed by runtime storage.
fn outbound_lane<T: Config<I>, I: Instance>(lane_id: LaneId) -> OutboundLane<RuntimeOutboundLaneStorage<T, I>> {
	OutboundLane::new(RuntimeOutboundLaneStorage {
		lane_id,
		_phantom: Default::default(),
	})
}

/// Runtime inbound lane storage.
struct RuntimeInboundLaneStorage<T: Config<I>, I = DefaultInstance> {
	cached_data: RefCell<Option<InboundLaneData<T::InboundRelayer>>>,
	_phantom: PhantomData<I>,
impl<T: Config<I>, I: Instance> InboundLaneStorage for RuntimeInboundLaneStorage<T, I> {
	type MessageFee = T::InboundMessageFee;
	type Relayer = T::InboundRelayer;

	fn id(&self) -> LaneId {
		self.lane_id
	}

	fn max_unrewarded_relayer_entries(&self) -> MessageNonce {
		T::MaxUnrewardedRelayerEntriesAtInboundLane::get()
	}

	fn max_unconfirmed_messages(&self) -> MessageNonce {
		T::MaxUnconfirmedMessagesAtInboundLane::get()
	}

	fn data(&self) -> InboundLaneData<T::InboundRelayer> {
		match self.cached_data.clone().into_inner() {
			Some(data) => data,
			None => {
				let data = InboundLanes::<T, I>::get(&self.lane_id);
				*self.cached_data.try_borrow_mut().expect(
					"we're in the single-threaded environment;\
						we have no recursive borrows; qed",
				) = Some(data.clone());
				data
			}
		}
	fn set_data(&mut self, data: InboundLaneData<T::InboundRelayer>) {
		*self.cached_data.try_borrow_mut().expect(
			"we're in the single-threaded environment;\
				we have no recursive borrows; qed",
		) = Some(data.clone());
		InboundLanes::<T, I>::insert(&self.lane_id, data)
	}
}

/// Runtime outbound lane storage.
struct RuntimeOutboundLaneStorage<T, I = DefaultInstance> {
	lane_id: LaneId,
	_phantom: PhantomData<(T, I)>,
}

impl<T: Config<I>, I: Instance> OutboundLaneStorage for RuntimeOutboundLaneStorage<T, I> {
	type MessageFee = T::OutboundMessageFee;

	fn id(&self) -> LaneId {
		self.lane_id
	}

	fn data(&self) -> OutboundLaneData {
		OutboundLanes::<I>::get(&self.lane_id)
	}