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};
Svyatoslav Nikolsky
committed
use crate::outbound_lane::{OutboundLane, OutboundLaneStorage, ReceivalConfirmationResult};
use bp_messages::{
Svyatoslav Nikolsky
committed
source_chain::{
LaneMessageVerifier, MessageDeliveryAndDispatchPayment, OnDeliveryConfirmed, RelayersRewards, TargetHeaderChain,
},
target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain},
Svyatoslav Nikolsky
committed
total_unrewarded_messages, DeliveredMessages, InboundLaneData, LaneId, MessageData, MessageKey, MessageNonce,
OperatingMode, OutboundLaneData, Parameter as MessagesParameter, UnrewardedRelayersState,
Hector Bulgarini
committed
use bp_runtime::{ChainId, Size};
use frame_support::{
decl_error, decl_event, decl_module, decl_storage,
dispatch::DispatchResultWithPostInfo,
Svyatoslav Nikolsky
committed
ensure, fail,
weights::{DispatchClass, Pays, PostDispatchInfo, Weight},
Parameter, StorageMap,
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;
Svyatoslav Nikolsky
committed
pub mod instant_payments;
Svyatoslav Nikolsky
committed
#[cfg(feature = "runtime-benchmarks")]
pub mod benchmarking;
#[cfg(test)]
mod mock;
/// The module configuration trait
pub trait Config<I = DefaultInstance>: frame_system::Config {
// General types
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>;
Svyatoslav Nikolsky
committed
/// 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>;
/// Message dispatch.
type MessageDispatch: MessageDispatch<
Self::AccountId,
Self::InboundMessageFee,
DispatchPayload = Self::InboundPayload,
>;
Hector Bulgarini
committed
/// Chain Id for the bridged chain.
type BridgedChainId: Get<ChainId>;
/// Shortcut to messages proof type for Config.
<<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;
decl_error! {
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 delivery proof has been submitted.
InvalidMessagesDeliveryProof,
Svyatoslav Nikolsky
committed
/// The bridged chain has invalid `UnrewardedRelayers` in its storage (fatal for the lane).
InvalidUnrewardedRelayers,
Svyatoslav Nikolsky
committed
/// 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.
Svyatoslav Nikolsky
committed
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,
}
decl_storage! {
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`).
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 {
}
decl_event!(
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),
Svyatoslav Nikolsky
committed
/// Messages in the inclusive range have been delivered to the bridged chain.
MessagesDelivered(LaneId, DeliveredMessages),
/// Phantom member, never used.
Dummy(PhantomData<(AccountId, I)>),
}
);
decl_module! {
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;
Hector Bulgarini
committed
/// Gets the chain id value from the instance.
const BridgedChainId: ChainId = T::BridgedChainId::get();
/// 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)
}
/// 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 {
log::info!(target: "runtime::bridge-messages", "Setting pallet Owner to: {:?}", new_owner);
log::info!(target: "runtime::bridge-messages", "Removed Owner of pallet.");
/// Halt or resume all/some pallet operations.
/// 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,
);
/// Update pallet parameter.
///
/// 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| {
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,
&lane.data(),
&payload,
).map_err(|err| {
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,
).map_err(|err| {
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 {
payload: encoded_payload,
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));
}
target: "runtime::bridge-messages",
"Accepted message {} to lane {:?}. Message size: {:?}",
nonce,
lane_id,
encoded_payload_len,
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| {
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);
message_data.payload.len()
// 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>,
dispatch_weight: Weight,
) -> 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,
>(proof, messages_count)
target: "runtime::bridge-messages",
"Rejecting invalid messages proof: {:?}",
err,
);
Error::<T, I>::InvalidMessagesProof
})?;
// dispatch messages and (optionally) update lane(s) state(s)
let mut total_messages = 0;
let mut valid_messages = 0;
let mut dispatch_weight_left = dispatch_weight;
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 {
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);
// ensure that relayer has declared enough weight for dispatching next message on
// this lane. We can't dispatch lane messages out-of-order, so if declared weight
// is not enough, let's move to next lane
let dispatch_weight = T::MessageDispatch::dispatch_weight(&message);
if dispatch_weight > dispatch_weight_left {
log::trace!(
target: "runtime::bridge-messages",
"Cannot dispatch any more messages on lane {:?}. Weight: declared={}, left={}",
lane_id,
dispatch_weight,
dispatch_weight_left,
);
break;
}
total_messages += 1;
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),
};
let unspent_weight = sp_std::cmp::min(unspent_weight, dispatch_weight);
dispatch_weight_left -= dispatch_weight - unspent_weight;
actual_weight = actual_weight
.saturating_sub(unspent_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
}
);
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(),
)]
Svyatoslav Nikolsky
committed
pub fn receive_messages_delivery_proof(
origin,
proof: MessagesDeliveryProofOf<T, I>,
relayers_state: UnrewardedRelayersState,
) -> DispatchResultWithPostInfo {
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| {
target: "runtime::bridge-messages",
"Rejecting invalid messages delivery proof: {:?}",
err,
);
Error::<T, I>::InvalidMessagesDeliveryProof
})?;
Svyatoslav Nikolsky
committed
// 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
Svyatoslav Nikolsky
committed
&& 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);
Svyatoslav Nikolsky
committed
let mut relayers_rewards: RelayersRewards<_, T::OutboundMessageFee> = RelayersRewards::new();
let last_delivered_nonce = lane_data.last_delivered_nonce();
Svyatoslav Nikolsky
committed
let confirmed_messages = match lane.confirm_delivery(
relayers_state.total_messages,
last_delivered_nonce,
&lane_data.relayers,
) {
Svyatoslav Nikolsky
committed
ReceivalConfirmationResult::ConfirmedMessages(confirmed_messages) => Some(confirmed_messages),
ReceivalConfirmationResult::NoNewConfirmations => None,
Svyatoslav Nikolsky
committed
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);
},
Svyatoslav Nikolsky
committed
error => {
log::trace!(
target: "runtime::bridge-messages",
"Messages delivery proof contains invalid unrewarded relayers vec: {:?}",
error,
);
fail!(Error::<T, I>::InvalidUnrewardedRelayers);
},
};
Svyatoslav Nikolsky
committed
Svyatoslav Nikolsky
committed
if let Some(confirmed_messages) = confirmed_messages {
// handle messages delivery confirmation
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
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,
);
}
}
Svyatoslav Nikolsky
committed
// emit 'delivered' event
let received_range = confirmed_messages.begin..=confirmed_messages.end;
Self::deposit_event(RawEvent::MessagesDelivered(lane_id, confirmed_messages));
Svyatoslav Nikolsky
committed
// remember to reward relayers that have delivered messages
// this loop is bounded by `T::MaxUnrewardedRelayerEntriesAtInboundLane` on the bridged chain
Svyatoslav Nikolsky
committed
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
Svyatoslav Nikolsky
committed
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");
Svyatoslav Nikolsky
committed
relayer_reward.reward = relayer_reward.reward.saturating_add(&message_data.fee);
relayer_reward.messages += 1;
Svyatoslav Nikolsky
committed
// 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,
);
}
target: "runtime::bridge-messages",
"Received messages delivery proof up to (and including) {} at lane {:?}",
last_delivered_nonce,
lane_id,
);
Ok(PostDispatchInfo {
actual_weight: Some(actual_weight),
pays_fee: Pays::Yes,
})
/// 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 })
Svyatoslav Nikolsky
committed
}
/// Get nonce of the latest generated message at given outbound lane.
Svyatoslav Nikolsky
committed
pub fn outbound_latest_generated_nonce(lane: LaneId) -> MessageNonce {
OutboundLanes::<I>::get(&lane).latest_generated_nonce
}
/// Get nonce of the latest confirmed message at given outbound lane.
Svyatoslav Nikolsky
committed
pub fn outbound_latest_received_nonce(lane: LaneId) -> MessageNonce {
OutboundLanes::<I>::get(&lane).latest_received_nonce
}
/// Get nonce of the latest received message at given inbound lane.
Svyatoslav Nikolsky
committed
pub fn inbound_latest_received_nonce(lane: LaneId) -> MessageNonce {
InboundLanes::<T, I>::get(&lane).last_delivered_nonce()
Svyatoslav Nikolsky
committed
}
/// Get nonce of the latest confirmed message at given inbound lane.
Svyatoslav Nikolsky
committed
pub fn inbound_latest_confirmed_nonce(lane: LaneId) -> MessageNonce {
InboundLanes::<T, I>::get(&lane).last_confirmed_nonce
Svyatoslav Nikolsky
committed
}
/// 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 _,
Svyatoslav Nikolsky
committed
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)
}
Svyatoslav Nikolsky
committed
}
/// 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)
}
}
/// 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(()),
Ok(RawOrigin::Signed(ref signer)) if Some(signer) == Pallet::<T, I>::module_owner().as_ref() => Ok(()),
_ => Err(BadOrigin),
}
}
/// 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)
}