Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion bin/millau/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,13 +376,14 @@ impl pallet_bridge_messages::Config<WithRialtoMessagesInstance> for Runtime {

type AccountIdConverter = bp_millau::AccountIdConverter;

type SenderOrigin = Origin;
type TargetHeaderChain = crate::rialto_messages::Rialto;
type LaneMessageVerifier = crate::rialto_messages::ToRialtoMessageVerifier;
type MessageDeliveryAndDispatchPayment = pallet_bridge_messages::instant_payments::InstantCurrencyPayments<
Runtime,
WithRialtoMessagesInstance,
pallet_balances::Pallet<Runtime>,
GetDeliveryConfirmationTransactionFee,
RootAccountForPayments,
>;
type OnDeliveryConfirmed = ();

Expand Down
18 changes: 15 additions & 3 deletions bin/millau/runtime/src/rialto_messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
use crate::Runtime;

use bp_messages::{
source_chain::TargetHeaderChain,
source_chain::{SenderOrigin, TargetHeaderChain},
target_chain::{ProvedMessages, SourceHeaderChain},
InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessagesParameter,
};
Expand Down Expand Up @@ -101,10 +101,11 @@ impl messages::ChainWithMessages for Millau {
}

impl messages::ThisChainWithMessages for Millau {
type Origin = crate::Origin;
type Call = crate::Call;

fn is_outbound_lane_enabled(lane: &LaneId) -> bool {
*lane == [0, 0, 0, 0] || *lane == [0, 0, 0, 1]
fn is_message_accepted(send_origin: &Self::Origin, lane: &LaneId) -> bool {
send_origin.linked_account().is_some() && (*lane == [0, 0, 0, 0] || *lane == [0, 0, 0, 1])
}

fn maximal_pending_messages_at_outbound_lane() -> MessageNonce {
Expand Down Expand Up @@ -247,6 +248,17 @@ impl SourceHeaderChain<bp_rialto::Balance> for Rialto {
}
}

impl SenderOrigin<crate::AccountId> for crate::Origin {
fn linked_account(&self) -> Option<crate::AccountId> {
match self.caller {
crate::OriginCaller::system(frame_system::RawOrigin::Signed(ref submitter)) => Some(submitter.clone()),
crate::OriginCaller::system(frame_system::RawOrigin::Root)
| crate::OriginCaller::system(frame_system::RawOrigin::None) => crate::RootAccountForPayments::get(),
_ => None,
}
}
}

/// Millau -> Rialto message lane pallet parameters.
#[derive(RuntimeDebug, Clone, Encode, Decode, PartialEq, Eq)]
pub enum MillauToRialtoMessagesParameter {
Expand Down
3 changes: 2 additions & 1 deletion bin/rialto/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -483,13 +483,14 @@ impl pallet_bridge_messages::Config<WithMillauMessagesInstance> for Runtime {

type AccountIdConverter = bp_rialto::AccountIdConverter;

type SenderOrigin = Origin;
type TargetHeaderChain = crate::millau_messages::Millau;
type LaneMessageVerifier = crate::millau_messages::ToMillauMessageVerifier;
type MessageDeliveryAndDispatchPayment = pallet_bridge_messages::instant_payments::InstantCurrencyPayments<
Runtime,
WithMillauMessagesInstance,
pallet_balances::Pallet<Runtime>,
GetDeliveryConfirmationTransactionFee,
RootAccountForPayments,
>;
type OnDeliveryConfirmed = ();

Expand Down
18 changes: 15 additions & 3 deletions bin/rialto/runtime/src/millau_messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
use crate::Runtime;

use bp_messages::{
source_chain::TargetHeaderChain,
source_chain::{SenderOrigin, TargetHeaderChain},
target_chain::{ProvedMessages, SourceHeaderChain},
InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessagesParameter,
};
Expand Down Expand Up @@ -101,10 +101,11 @@ impl messages::ChainWithMessages for Rialto {
}

impl messages::ThisChainWithMessages for Rialto {
type Origin = crate::Origin;
type Call = crate::Call;

fn is_outbound_lane_enabled(lane: &LaneId) -> bool {
*lane == [0, 0, 0, 0] || *lane == [0, 0, 0, 1]
fn is_message_accepted(send_origin: &Self::Origin, lane: &LaneId) -> bool {
send_origin.linked_account().is_some() && (*lane == [0, 0, 0, 0] || *lane == [0, 0, 0, 1])
}

fn maximal_pending_messages_at_outbound_lane() -> MessageNonce {
Expand Down Expand Up @@ -247,6 +248,17 @@ impl SourceHeaderChain<bp_millau::Balance> for Millau {
}
}

impl SenderOrigin<crate::AccountId> for crate::Origin {
fn linked_account(&self) -> Option<crate::AccountId> {
match self.caller {
crate::OriginCaller::system(frame_system::RawOrigin::Signed(ref submitter)) => Some(submitter.clone()),
crate::OriginCaller::system(frame_system::RawOrigin::Root)
| crate::OriginCaller::system(frame_system::RawOrigin::None) => crate::RootAccountForPayments::get(),
_ => None,
}
}
}

/// Rialto -> Millau message lane pallet parameters.
#[derive(RuntimeDebug, Clone, Encode, Decode, PartialEq, Eq)]
pub enum RialtoToMillauMessagesParameter {
Expand Down
2 changes: 2 additions & 0 deletions bin/runtime-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pallet-bridge-messages = { path = "../../modules/messages", default-features = f
# Substrate dependencies

frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false }
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" , default-features = false }
Expand All @@ -39,6 +40,7 @@ std = [
"bp-runtime/std",
"codec/std",
"frame-support/std",
"frame-system/std",
"hash-db/std",
"pallet-bridge-dispatch/std",
"pallet-bridge-grandpa/std",
Expand Down
7 changes: 5 additions & 2 deletions bin/runtime-common/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,11 @@ corresponding chain. There is single exception, though (it may be changed in the

This trait represents this chain from bridge point of view. Let's review every method of this trait:

- `ThisChainWithMessages::is_outbound_lane_enabled`: is used to check whether given lane accepts
outbound messages.
- `ThisChainWithMessages::is_message_accepted`: is used to check whether given lane accepts
messages. The send-message origin is passed to the function, so you may e.g. verify that only
given pallet is able to send messages over selected lane. **IMPORTANT**: if you assume that the
message must be paid by the sender, you must ensure that the sender origin has linked the account
for paying message delivery and dispatch fee.

- `ThisChainWithMessages::maximal_pending_messages_at_outbound_lane`: you should return maximal
number of pending (undelivered) messages from this function. Returning small values would require
Expand Down
82 changes: 60 additions & 22 deletions bin/runtime-common/src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

use bp_message_dispatch::MessageDispatch as _;
use bp_messages::{
source_chain::{LaneMessageVerifier, Sender},
source_chain::LaneMessageVerifier,
target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages},
InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData,
};
Expand Down Expand Up @@ -95,11 +95,13 @@ pub struct MessageTransaction<Weight> {

/// This chain that has `pallet-bridge-messages` and `dispatch` modules.
pub trait ThisChainWithMessages: ChainWithMessages {
/// Call origin on the chain.
type Origin;
/// Call type on the chain.
type Call: Encode + Decode;

/// Are we accepting any messages to the given lane?
fn is_outbound_lane_enabled(lane: &LaneId) -> bool;
/// Do we accept message sent by given origin to given lane?
fn is_message_accepted(origin: &Self::Origin, lane: &LaneId) -> bool;

/// Maximal number of pending (not yet delivered) messages at This chain.
///
Expand Down Expand Up @@ -149,6 +151,7 @@ pub(crate) type SignatureOf<C> = <C as ChainWithMessages>::Signature;
pub(crate) type WeightOf<C> = <C as ChainWithMessages>::Weight;
pub(crate) type BalanceOf<C> = <C as ChainWithMessages>::Balance;

pub(crate) type OriginOf<C> = <C as ThisChainWithMessages>::Origin;
pub(crate) type CallOf<C> = <C as ThisChainWithMessages>::Call;

/// Raw storage proof type (just raw trie nodes).
Expand Down Expand Up @@ -240,29 +243,38 @@ pub mod source {
#[derive(RuntimeDebug)]
pub struct FromThisChainMessageVerifier<B>(PhantomData<B>);

pub(crate) const OUTBOUND_LANE_DISABLED: &str = "The outbound message lane is disabled.";
pub(crate) const MESSAGE_REJECTED_BY_OUTBOUND_LANE: &str = "The outbound message lane has rejected the message.";
pub(crate) const TOO_MANY_PENDING_MESSAGES: &str = "Too many pending messages at the lane.";
pub(crate) const BAD_ORIGIN: &str = "Unable to match the source origin to expected target origin.";
pub(crate) const TOO_LOW_FEE: &str = "Provided fee is below minimal threshold required by the lane.";
pub(crate) const INVALID_SUBMITTER_ORIGIN: &str = "The submitter origin is invalid.";

impl<B> LaneMessageVerifier<AccountIdOf<ThisChain<B>>, FromThisChainMessagePayload<B>, BalanceOf<ThisChain<B>>>
for FromThisChainMessageVerifier<B>
impl<B>
LaneMessageVerifier<
OriginOf<ThisChain<B>>,
AccountIdOf<ThisChain<B>>,
FromThisChainMessagePayload<B>,
BalanceOf<ThisChain<B>>,
> for FromThisChainMessageVerifier<B>
where
B: MessageBridge,
// matches requirements from the `frame_system::Config::Origin`
OriginOf<ThisChain<B>>:
Clone + Into<Result<frame_system::RawOrigin<AccountIdOf<ThisChain<B>>>, OriginOf<ThisChain<B>>>>,
AccountIdOf<ThisChain<B>>: PartialEq + Clone,
{
type Error = &'static str;

fn verify_message(
submitter: &Sender<AccountIdOf<ThisChain<B>>>,
submitter: &OriginOf<ThisChain<B>>,
delivery_and_dispatch_fee: &BalanceOf<ThisChain<B>>,
lane: &LaneId,
lane_outbound_data: &OutboundLaneData,
payload: &FromThisChainMessagePayload<B>,
) -> Result<(), Self::Error> {
// reject message if lane is blocked
if !ThisChain::<B>::is_outbound_lane_enabled(lane) {
return Err(OUTBOUND_LANE_DISABLED);
if !ThisChain::<B>::is_message_accepted(submitter, lane) {
return Err(MESSAGE_REJECTED_BY_OUTBOUND_LANE);
}

// reject message if there are too many pending messages at this lane
Expand All @@ -276,7 +288,13 @@ pub mod source {

// Do the dispatch-specific check. We assume that the target chain uses
// `Dispatch`, so we verify the message accordingly.
pallet_bridge_dispatch::verify_message_origin(submitter, payload).map_err(|_| BAD_ORIGIN)?;
let raw_origin_or_err: Result<frame_system::RawOrigin<AccountIdOf<ThisChain<B>>>, OriginOf<ThisChain<B>>> =
submitter.clone().into();
pallet_bridge_dispatch::verify_message_origin(
&raw_origin_or_err.map_err(|_| INVALID_SUBMITTER_ORIGIN)?,
payload,
)
.map_err(|_| BAD_ORIGIN)?;

let minimal_fee_in_this_tokens =
estimate_message_dispatch_and_delivery_fee::<B>(payload, B::RELAYER_FEE_PERCENT)?;
Expand Down Expand Up @@ -781,6 +799,14 @@ mod tests {
#[codec(index = 84)]
Mint,
}
#[derive(Clone, Debug)]
struct ThisChainOrigin(Result<frame_system::RawOrigin<ThisChainAccountId>, ()>);

impl From<ThisChainOrigin> for Result<frame_system::RawOrigin<ThisChainAccountId>, ThisChainOrigin> {
fn from(origin: ThisChainOrigin) -> Result<frame_system::RawOrigin<ThisChainAccountId>, ThisChainOrigin> {
origin.clone().0.map_err(|_| origin)
}
}

#[derive(Debug, PartialEq, Decode, Encode)]
struct BridgedChainAccountId(u32);
Expand All @@ -790,6 +816,16 @@ mod tests {
struct BridgedChainSignature(u32);
#[derive(Debug, PartialEq, Decode, Encode)]
enum BridgedChainCall {}
#[derive(Clone, Debug)]
struct BridgedChainOrigin;

impl From<BridgedChainOrigin> for Result<frame_system::RawOrigin<BridgedChainAccountId>, BridgedChainOrigin> {
fn from(
_origin: BridgedChainOrigin,
) -> Result<frame_system::RawOrigin<BridgedChainAccountId>, BridgedChainOrigin> {
unreachable!()
}
}

macro_rules! impl_wrapped_balance {
($name:ident) => {
Expand Down Expand Up @@ -867,9 +903,10 @@ mod tests {
}

impl ThisChainWithMessages for ThisChain {
type Origin = ThisChainOrigin;
type Call = ThisChainCall;

fn is_outbound_lane_enabled(lane: &LaneId) -> bool {
fn is_message_accepted(_send_origin: &Self::Origin, lane: &LaneId) -> bool {
lane == TEST_LANE_ID
}

Expand Down Expand Up @@ -923,9 +960,10 @@ mod tests {
}

impl ThisChainWithMessages for BridgedChain {
type Origin = BridgedChainOrigin;
type Call = BridgedChainCall;

fn is_outbound_lane_enabled(_lane: &LaneId) -> bool {
fn is_message_accepted(_send_origin: &Self::Origin, _lane: &LaneId) -> bool {
unreachable!()
}

Expand Down Expand Up @@ -1050,7 +1088,7 @@ mod tests {
// and now check that the verifier checks the fee
assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&Sender::Root,
&ThisChainOrigin(Ok(frame_system::RawOrigin::Root)),
&ThisChainBalance(1),
TEST_LANE_ID,
&test_lane_outbound_data(),
Expand All @@ -1060,7 +1098,7 @@ mod tests {
);
assert!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&Sender::Root,
&ThisChainOrigin(Ok(frame_system::RawOrigin::Root)),
&ThisChainBalance(1_000_000),
TEST_LANE_ID,
&test_lane_outbound_data(),
Expand All @@ -1084,7 +1122,7 @@ mod tests {
// and now check that the verifier checks the fee
assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&Sender::Signed(ThisChainAccountId(0)),
&ThisChainOrigin(Ok(frame_system::RawOrigin::Signed(ThisChainAccountId(0)))),
&ThisChainBalance(1_000_000),
TEST_LANE_ID,
&test_lane_outbound_data(),
Expand All @@ -1094,7 +1132,7 @@ mod tests {
);
assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&Sender::None,
&ThisChainOrigin(Ok(frame_system::RawOrigin::None)),
&ThisChainBalance(1_000_000),
TEST_LANE_ID,
&test_lane_outbound_data(),
Expand All @@ -1104,7 +1142,7 @@ mod tests {
);
assert!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&Sender::Root,
&ThisChainOrigin(Ok(frame_system::RawOrigin::Root)),
&ThisChainBalance(1_000_000),
TEST_LANE_ID,
&test_lane_outbound_data(),
Expand All @@ -1128,7 +1166,7 @@ mod tests {
// and now check that the verifier checks the fee
assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&Sender::Signed(ThisChainAccountId(0)),
&ThisChainOrigin(Ok(frame_system::RawOrigin::Signed(ThisChainAccountId(0)))),
&ThisChainBalance(1_000_000),
TEST_LANE_ID,
&test_lane_outbound_data(),
Expand All @@ -1138,7 +1176,7 @@ mod tests {
);
assert!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&Sender::Signed(ThisChainAccountId(1)),
&ThisChainOrigin(Ok(frame_system::RawOrigin::Signed(ThisChainAccountId(1)))),
&ThisChainBalance(1_000_000),
TEST_LANE_ID,
&test_lane_outbound_data(),
Expand All @@ -1152,21 +1190,21 @@ mod tests {
fn message_is_rejected_when_sent_using_disabled_lane() {
assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&Sender::Root,
&ThisChainOrigin(Ok(frame_system::RawOrigin::Root)),
&ThisChainBalance(1_000_000),
b"dsbl",
&test_lane_outbound_data(),
&regular_outbound_message_payload(),
),
Err(source::OUTBOUND_LANE_DISABLED)
Err(source::MESSAGE_REJECTED_BY_OUTBOUND_LANE)
);
}

#[test]
fn message_is_rejected_when_there_are_too_many_pending_messages_at_outbound_lane() {
assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&Sender::Root,
&ThisChainOrigin(Ok(frame_system::RawOrigin::Root)),
&ThisChainBalance(1_000_000),
TEST_LANE_ID,
&OutboundLaneData {
Expand Down
Loading