Support dedicated lanes for pallets (#962)

* pass call origin to the message verifier

* is_outbound_lane_enabled -> is_message_accepted

* trait SenderOrigin

* only accept messages from token swap pallet to token swap lane

* tests for edge cases of pay_delivery_and_dispatch_fee

* fixed origin verification

* fmt

* fix benchmarks compilation

* fix TODO with None account and non-zero message fee (already covered by tests)

* revert cargo fmt changes temporarily
This commit is contained in:
Svyatoslav Nikolsky
2022-03-14 16:36:58 +03:00
committed by Bastian Köcher
parent 7b7b8baa60
commit ed2a3082ef
15 changed files with 361 additions and 129 deletions
+2 -3
View File
@@ -456,10 +456,9 @@ impl pallet_bridge_messages::Config<WithRialtoMessagesInstance> for Runtime {
type MessageDeliveryAndDispatchPayment = type MessageDeliveryAndDispatchPayment =
pallet_bridge_messages::instant_payments::InstantCurrencyPayments< pallet_bridge_messages::instant_payments::InstantCurrencyPayments<
Runtime, Runtime,
(), WithRialtoMessagesInstance,
pallet_balances::Pallet<Runtime>, pallet_balances::Pallet<Runtime>,
GetDeliveryConfirmationTransactionFee, GetDeliveryConfirmationTransactionFee,
RootAccountForPayments,
>; >;
type OnMessageAccepted = (); type OnMessageAccepted = ();
type OnDeliveryConfirmed = type OnDeliveryConfirmed =
@@ -525,7 +524,7 @@ construct_runtime!(
BridgeRialtoGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage}, BridgeRialtoGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage},
BridgeDispatch: pallet_bridge_dispatch::{Pallet, Event<T>}, BridgeDispatch: pallet_bridge_dispatch::{Pallet, Event<T>},
BridgeRialtoMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event<T>, Config<T>}, BridgeRialtoMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event<T>, Config<T>},
BridgeRialtoTokenSwap: pallet_bridge_token_swap::{Pallet, Call, Storage, Event<T>}, BridgeRialtoTokenSwap: pallet_bridge_token_swap::{Pallet, Call, Storage, Event<T>, Origin<T>},
// Westend bridge modules. // Westend bridge modules.
BridgeWestendGrandpa: pallet_bridge_grandpa::<Instance1>::{Pallet, Call, Config<T>, Storage}, BridgeWestendGrandpa: pallet_bridge_grandpa::<Instance1>::{Pallet, Call, Config<T>, Storage},
@@ -19,7 +19,7 @@
use crate::Runtime; use crate::Runtime;
use bp_messages::{ use bp_messages::{
source_chain::TargetHeaderChain, source_chain::{SenderOrigin, TargetHeaderChain},
target_chain::{ProvedMessages, SourceHeaderChain}, target_chain::{ProvedMessages, SourceHeaderChain},
InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessagesParameter, InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessagesParameter,
}; };
@@ -116,12 +116,23 @@ impl messages::ChainWithMessages for Millau {
} }
impl messages::ThisChainWithMessages for Millau { impl messages::ThisChainWithMessages for Millau {
type Origin = crate::Origin;
type Call = crate::Call; type Call = crate::Call;
fn is_outbound_lane_enabled(lane: &LaneId) -> bool { fn is_message_accepted(send_origin: &Self::Origin, lane: &LaneId) -> bool {
*lane == [0, 0, 0, 0] || // lanes 0x00000000 && 0x00000001 are accepting any paid messages, while
*lane == [0, 0, 0, 1] || // `TokenSwapMessageLane` only accepts messages from token swap pallet
*lane == crate::TokenSwapMessagesLane::get() let token_swap_dedicated_lane = crate::TokenSwapMessagesLane::get();
match *lane {
[0, 0, 0, 0] | [0, 0, 0, 1] => send_origin.linked_account().is_some(),
_ if *lane == token_swap_dedicated_lane => matches!(
send_origin.caller,
crate::OriginCaller::BridgeRialtoTokenSwap(
pallet_bridge_token_swap::RawOrigin::TokenSwap { .. }
)
),
_ => false,
}
} }
fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { fn maximal_pending_messages_at_outbound_lane() -> MessageNonce {
@@ -277,6 +288,25 @@ 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(),
crate::OriginCaller::BridgeRialtoTokenSwap(
pallet_bridge_token_swap::RawOrigin::TokenSwap {
ref swap_account_at_this_chain,
..
},
) => Some(swap_account_at_this_chain.clone()),
_ => None,
}
}
}
/// Millau -> Rialto message lane pallet parameters. /// Millau -> Rialto message lane pallet parameters.
#[derive(RuntimeDebug, Clone, Encode, Decode, PartialEq, Eq, TypeInfo)] #[derive(RuntimeDebug, Clone, Encode, Decode, PartialEq, Eq, TypeInfo)]
pub enum MillauToRialtoMessagesParameter { pub enum MillauToRialtoMessagesParameter {
+1 -2
View File
@@ -457,10 +457,9 @@ impl pallet_bridge_messages::Config<WithMillauMessagesInstance> for Runtime {
type MessageDeliveryAndDispatchPayment = type MessageDeliveryAndDispatchPayment =
pallet_bridge_messages::instant_payments::InstantCurrencyPayments< pallet_bridge_messages::instant_payments::InstantCurrencyPayments<
Runtime, Runtime,
(), WithMillauMessagesInstance,
pallet_balances::Pallet<Runtime>, pallet_balances::Pallet<Runtime>,
GetDeliveryConfirmationTransactionFee, GetDeliveryConfirmationTransactionFee,
RootAccountForPayments,
>; >;
type OnMessageAccepted = (); type OnMessageAccepted = ();
type OnDeliveryConfirmed = (); type OnDeliveryConfirmed = ();
@@ -19,7 +19,7 @@
use crate::Runtime; use crate::Runtime;
use bp_messages::{ use bp_messages::{
source_chain::TargetHeaderChain, source_chain::{SenderOrigin, TargetHeaderChain},
target_chain::{ProvedMessages, SourceHeaderChain}, target_chain::{ProvedMessages, SourceHeaderChain},
InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessagesParameter, InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessagesParameter,
}; };
@@ -116,10 +116,11 @@ impl messages::ChainWithMessages for Rialto {
} }
impl messages::ThisChainWithMessages for Rialto { impl messages::ThisChainWithMessages for Rialto {
type Origin = crate::Origin;
type Call = crate::Call; type Call = crate::Call;
fn is_outbound_lane_enabled(lane: &LaneId) -> bool { fn is_message_accepted(send_origin: &Self::Origin, lane: &LaneId) -> bool {
*lane == [0, 0, 0, 0] || *lane == [0, 0, 0, 1] send_origin.linked_account().is_some() && (*lane == [0, 0, 0, 0] || *lane == [0, 0, 0, 1])
} }
fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { fn maximal_pending_messages_at_outbound_lane() -> MessageNonce {
@@ -275,6 +276,19 @@ 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. /// Rialto -> Millau message lane pallet parameters.
#[derive(RuntimeDebug, Clone, Encode, Decode, PartialEq, Eq, TypeInfo)] #[derive(RuntimeDebug, Clone, Encode, Decode, PartialEq, Eq, TypeInfo)]
pub enum RialtoToMillauMessagesParameter { pub enum RialtoToMillauMessagesParameter {
+2 -3
View File
@@ -26,7 +26,7 @@ pallet-bridge-messages = { path = "../../modules/messages", default-features = f
# Substrate dependencies # Substrate dependencies
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } 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, optional = true } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true }
pallet-transaction-payment = { 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-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
@@ -45,6 +45,7 @@ std = [
"bp-runtime/std", "bp-runtime/std",
"codec/std", "codec/std",
"frame-support/std", "frame-support/std",
"frame-system/std",
"hash-db/std", "hash-db/std",
"pallet-bridge-dispatch/std", "pallet-bridge-dispatch/std",
"pallet-bridge-grandpa/std", "pallet-bridge-grandpa/std",
@@ -60,7 +61,6 @@ std = [
] ]
runtime-benchmarks = [ runtime-benchmarks = [
"ed25519-dalek/u64_backend", "ed25519-dalek/u64_backend",
"frame-system",
"pallet-balances", "pallet-balances",
"pallet-bridge-grandpa/runtime-benchmarks", "pallet-bridge-grandpa/runtime-benchmarks",
"pallet-bridge-messages/runtime-benchmarks", "pallet-bridge-messages/runtime-benchmarks",
@@ -68,6 +68,5 @@ runtime-benchmarks = [
"sp-version", "sp-version",
] ]
integrity-test = [ integrity-test = [
"frame-system",
"static_assertions", "static_assertions",
] ]
+5 -2
View File
@@ -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: 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 - `ThisChainWithMessages::is_message_accepted`: is used to check whether given lane accepts
outbound messages. 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 - `ThisChainWithMessages::maximal_pending_messages_at_outbound_lane`: you should return maximal
number of pending (undelivered) messages from this function. Returning small values would require number of pending (undelivered) messages from this function. Returning small values would require
+71 -21
View File
@@ -22,7 +22,7 @@
use bp_message_dispatch::MessageDispatch as _; use bp_message_dispatch::MessageDispatch as _;
use bp_messages::{ use bp_messages::{
source_chain::{LaneMessageVerifier, Sender}, source_chain::LaneMessageVerifier,
target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages}, target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages},
InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData, InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData,
}; };
@@ -111,11 +111,13 @@ pub struct MessageTransaction<Weight> {
/// This chain that has `pallet-bridge-messages` and `dispatch` modules. /// This chain that has `pallet-bridge-messages` and `dispatch` modules.
pub trait ThisChainWithMessages: ChainWithMessages { pub trait ThisChainWithMessages: ChainWithMessages {
/// Call origin on the chain.
type Origin;
/// Call type on the chain. /// Call type on the chain.
type Call: Encode + Decode; type Call: Encode + Decode;
/// Are we accepting any messages to the given lane? /// Do we accept message sent by given origin to given lane?
fn is_outbound_lane_enabled(lane: &LaneId) -> bool; fn is_message_accepted(origin: &Self::Origin, lane: &LaneId) -> bool;
/// Maximal number of pending (not yet delivered) messages at This chain. /// Maximal number of pending (not yet delivered) messages at This chain.
/// ///
@@ -173,6 +175,8 @@ pub type SignatureOf<C> = <C as ChainWithMessages>::Signature;
pub type WeightOf<C> = <C as ChainWithMessages>::Weight; pub type WeightOf<C> = <C as ChainWithMessages>::Weight;
/// Type of balances that is used on the chain. /// Type of balances that is used on the chain.
pub type BalanceOf<C> = <C as ChainWithMessages>::Balance; pub type BalanceOf<C> = <C as ChainWithMessages>::Balance;
/// Type of origin that is used on the chain.
pub type OriginOf<C> = <C as ThisChainWithMessages>::Origin;
/// Type of call that is used on this chain. /// Type of call that is used on this chain.
pub type CallOf<C> = <C as ThisChainWithMessages>::Call; pub type CallOf<C> = <C as ThisChainWithMessages>::Call;
@@ -270,7 +274,8 @@ pub mod source {
pub struct FromThisChainMessageVerifier<B>(PhantomData<B>); pub struct FromThisChainMessageVerifier<B>(PhantomData<B>);
/// The error message returned from LaneMessageVerifier when outbound lane is disabled. /// The error message returned from LaneMessageVerifier when outbound lane is disabled.
pub const OUTBOUND_LANE_DISABLED: &str = "The outbound message lane is disabled."; pub const MESSAGE_REJECTED_BY_OUTBOUND_LANE: &str =
"The outbound message lane has rejected the message.";
/// The error message returned from LaneMessageVerifier when too many pending messages at the /// The error message returned from LaneMessageVerifier when too many pending messages at the
/// lane. /// lane.
pub const TOO_MANY_PENDING_MESSAGES: &str = "Too many pending messages at the lane."; pub const TOO_MANY_PENDING_MESSAGES: &str = "Too many pending messages at the lane.";
@@ -281,26 +286,30 @@ pub mod source {
impl<B> impl<B>
LaneMessageVerifier< LaneMessageVerifier<
OriginOf<ThisChain<B>>,
AccountIdOf<ThisChain<B>>, AccountIdOf<ThisChain<B>>,
FromThisChainMessagePayload<B>, FromThisChainMessagePayload<B>,
BalanceOf<ThisChain<B>>, BalanceOf<ThisChain<B>>,
> for FromThisChainMessageVerifier<B> > for FromThisChainMessageVerifier<B>
where where
B: MessageBridge, 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, AccountIdOf<ThisChain<B>>: PartialEq + Clone,
{ {
type Error = &'static str; type Error = &'static str;
fn verify_message( fn verify_message(
submitter: &Sender<AccountIdOf<ThisChain<B>>>, submitter: &OriginOf<ThisChain<B>>,
delivery_and_dispatch_fee: &BalanceOf<ThisChain<B>>, delivery_and_dispatch_fee: &BalanceOf<ThisChain<B>>,
lane: &LaneId, lane: &LaneId,
lane_outbound_data: &OutboundLaneData, lane_outbound_data: &OutboundLaneData,
payload: &FromThisChainMessagePayload<B>, payload: &FromThisChainMessagePayload<B>,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
// reject message if lane is blocked // reject message if lane is blocked
if !ThisChain::<B>::is_outbound_lane_enabled(lane) { if !ThisChain::<B>::is_message_accepted(submitter, lane) {
return Err(OUTBOUND_LANE_DISABLED) return Err(MESSAGE_REJECTED_BY_OUTBOUND_LANE)
} }
// reject message if there are too many pending messages at this lane // reject message if there are too many pending messages at this lane
@@ -314,8 +323,23 @@ pub mod source {
// Do the dispatch-specific check. We assume that the target chain uses // Do the dispatch-specific check. We assume that the target chain uses
// `Dispatch`, so we verify the message accordingly. // `Dispatch`, so we verify the message accordingly.
pallet_bridge_dispatch::verify_message_origin(submitter, payload) let raw_origin_or_err: Result<
.map_err(|_| BAD_ORIGIN)?; frame_system::RawOrigin<AccountIdOf<ThisChain<B>>>,
OriginOf<ThisChain<B>>,
> = submitter.clone().into();
match raw_origin_or_err {
Ok(raw_origin) =>
pallet_bridge_dispatch::verify_message_origin(&raw_origin, payload)
.map(drop)
.map_err(|_| BAD_ORIGIN)?,
Err(_) => {
// so what it means that we've failed to convert origin to the
// `frame_system::RawOrigin`? now it means that the custom pallet origin has
// been used to send the message. Do we need to verify it? The answer is no,
// because pallet may craft any origin (e.g. root) && we can't verify whether it
// is valid, or not.
},
};
let minimal_fee_in_this_tokens = estimate_message_dispatch_and_delivery_fee::<B>( let minimal_fee_in_this_tokens = estimate_message_dispatch_and_delivery_fee::<B>(
payload, payload,
@@ -854,6 +878,18 @@ mod tests {
#[codec(index = 84)] #[codec(index = 84)]
Mint, 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)] #[derive(Debug, PartialEq, Decode, Encode)]
struct BridgedChainAccountId(u32); struct BridgedChainAccountId(u32);
@@ -863,6 +899,18 @@ mod tests {
struct BridgedChainSignature(u32); struct BridgedChainSignature(u32);
#[derive(Debug, PartialEq, Decode, Encode)] #[derive(Debug, PartialEq, Decode, Encode)]
enum BridgedChainCall {} 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 { macro_rules! impl_wrapped_balance {
($name:ident) => { ($name:ident) => {
@@ -940,9 +988,10 @@ mod tests {
} }
impl ThisChainWithMessages for ThisChain { impl ThisChainWithMessages for ThisChain {
type Origin = ThisChainOrigin;
type Call = ThisChainCall; 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 lane == TEST_LANE_ID
} }
@@ -1000,9 +1049,10 @@ mod tests {
} }
impl ThisChainWithMessages for BridgedChain { impl ThisChainWithMessages for BridgedChain {
type Origin = BridgedChainOrigin;
type Call = BridgedChainCall; type Call = BridgedChainCall;
fn is_outbound_lane_enabled(_lane: &LaneId) -> bool { fn is_message_accepted(_send_origin: &Self::Origin, _lane: &LaneId) -> bool {
unreachable!() unreachable!()
} }
@@ -1141,7 +1191,7 @@ mod tests {
// and now check that the verifier checks the fee // and now check that the verifier checks the fee
assert_eq!( assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message( source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&Sender::Root, &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)),
&ThisChainBalance(1), &ThisChainBalance(1),
TEST_LANE_ID, TEST_LANE_ID,
&test_lane_outbound_data(), &test_lane_outbound_data(),
@@ -1150,7 +1200,7 @@ mod tests {
Err(source::TOO_LOW_FEE) Err(source::TOO_LOW_FEE)
); );
assert!(source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message( assert!(source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&Sender::Root, &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)),
&ThisChainBalance(1_000_000), &ThisChainBalance(1_000_000),
TEST_LANE_ID, TEST_LANE_ID,
&test_lane_outbound_data(), &test_lane_outbound_data(),
@@ -1173,7 +1223,7 @@ mod tests {
// and now check that the verifier checks the fee // and now check that the verifier checks the fee
assert_eq!( assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message( source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&Sender::Signed(ThisChainAccountId(0)), &ThisChainOrigin(Ok(frame_system::RawOrigin::Signed(ThisChainAccountId(0)))),
&ThisChainBalance(1_000_000), &ThisChainBalance(1_000_000),
TEST_LANE_ID, TEST_LANE_ID,
&test_lane_outbound_data(), &test_lane_outbound_data(),
@@ -1183,7 +1233,7 @@ mod tests {
); );
assert_eq!( assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message( source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&Sender::None, &ThisChainOrigin(Ok(frame_system::RawOrigin::None)),
&ThisChainBalance(1_000_000), &ThisChainBalance(1_000_000),
TEST_LANE_ID, TEST_LANE_ID,
&test_lane_outbound_data(), &test_lane_outbound_data(),
@@ -1192,7 +1242,7 @@ mod tests {
Err(source::BAD_ORIGIN) Err(source::BAD_ORIGIN)
); );
assert!(source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message( assert!(source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&Sender::Root, &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)),
&ThisChainBalance(1_000_000), &ThisChainBalance(1_000_000),
TEST_LANE_ID, TEST_LANE_ID,
&test_lane_outbound_data(), &test_lane_outbound_data(),
@@ -1215,7 +1265,7 @@ mod tests {
// and now check that the verifier checks the fee // and now check that the verifier checks the fee
assert_eq!( assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message( source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&Sender::Signed(ThisChainAccountId(0)), &ThisChainOrigin(Ok(frame_system::RawOrigin::Signed(ThisChainAccountId(0)))),
&ThisChainBalance(1_000_000), &ThisChainBalance(1_000_000),
TEST_LANE_ID, TEST_LANE_ID,
&test_lane_outbound_data(), &test_lane_outbound_data(),
@@ -1224,7 +1274,7 @@ mod tests {
Err(source::BAD_ORIGIN) Err(source::BAD_ORIGIN)
); );
assert!(source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message( assert!(source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&Sender::Signed(ThisChainAccountId(1)), &ThisChainOrigin(Ok(frame_system::RawOrigin::Signed(ThisChainAccountId(1)))),
&ThisChainBalance(1_000_000), &ThisChainBalance(1_000_000),
TEST_LANE_ID, TEST_LANE_ID,
&test_lane_outbound_data(), &test_lane_outbound_data(),
@@ -1237,13 +1287,13 @@ mod tests {
fn message_is_rejected_when_sent_using_disabled_lane() { fn message_is_rejected_when_sent_using_disabled_lane() {
assert_eq!( assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message( source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&Sender::Root, &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)),
&ThisChainBalance(1_000_000), &ThisChainBalance(1_000_000),
b"dsbl", b"dsbl",
&test_lane_outbound_data(), &test_lane_outbound_data(),
&regular_outbound_message_payload(), &regular_outbound_message_payload(),
), ),
Err(source::OUTBOUND_LANE_DISABLED) Err(source::MESSAGE_REJECTED_BY_OUTBOUND_LANE)
); );
} }
@@ -1251,7 +1301,7 @@ mod tests {
fn message_is_rejected_when_there_are_too_many_pending_messages_at_outbound_lane() { fn message_is_rejected_when_there_are_too_many_pending_messages_at_outbound_lane() {
assert_eq!( assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message( source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&Sender::Root, &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)),
&ThisChainBalance(1_000_000), &ThisChainBalance(1_000_000),
TEST_LANE_ID, TEST_LANE_ID,
&OutboundLaneData { &OutboundLaneData {
@@ -22,7 +22,7 @@
use crate::OutboundMessages; use crate::OutboundMessages;
use bp_messages::{ use bp_messages::{
source_chain::{MessageDeliveryAndDispatchPayment, RelayersRewards, Sender}, source_chain::{MessageDeliveryAndDispatchPayment, RelayersRewards, SenderOrigin},
LaneId, MessageKey, MessageNonce, UnrewardedRelayer, LaneId, MessageKey, MessageNonce, UnrewardedRelayer,
}; };
use codec::Encode; use codec::Encode;
@@ -31,6 +31,10 @@ use num_traits::{SaturatingAdd, Zero};
use sp_runtime::traits::Saturating; use sp_runtime::traits::Saturating;
use sp_std::{collections::vec_deque::VecDeque, fmt::Debug, ops::RangeInclusive}; use sp_std::{collections::vec_deque::VecDeque, fmt::Debug, ops::RangeInclusive};
/// Error that occurs when message fee is non-zero, but payer is not defined.
const NON_ZERO_MESSAGE_FEE_CANT_BE_PAID_BY_NONE: &str =
"Non-zero message fee can't be paid by <None>";
/// Instant message payments made in given currency. /// Instant message payments made in given currency.
/// ///
/// The balance is initially reserved in a special `relayers-fund` account, and transferred /// The balance is initially reserved in a special `relayers-fund` account, and transferred
@@ -44,42 +48,50 @@ use sp_std::{collections::vec_deque::VecDeque, fmt::Debug, ops::RangeInclusive};
/// to the relayer account. /// to the relayer account.
/// NOTE It's within relayer's interest to keep their balance above ED as well, to make sure they /// NOTE It's within relayer's interest to keep their balance above ED as well, to make sure they
/// can receive the payment. /// can receive the payment.
pub struct InstantCurrencyPayments<T, I, Currency, GetConfirmationFee, RootAccount> { pub struct InstantCurrencyPayments<T, I, Currency, GetConfirmationFee> {
_phantom: sp_std::marker::PhantomData<(T, I, Currency, GetConfirmationFee, RootAccount)>, _phantom: sp_std::marker::PhantomData<(T, I, Currency, GetConfirmationFee)>,
} }
impl<T, I, Currency, GetConfirmationFee, RootAccount> impl<T, I, Currency, GetConfirmationFee>
MessageDeliveryAndDispatchPayment<T::AccountId, Currency::Balance> MessageDeliveryAndDispatchPayment<T::Origin, T::AccountId, Currency::Balance>
for InstantCurrencyPayments<T, I, Currency, GetConfirmationFee, RootAccount> for InstantCurrencyPayments<T, I, Currency, GetConfirmationFee>
where where
T: frame_system::Config + crate::Config<I>, T: frame_system::Config + crate::Config<I>,
I: 'static, I: 'static,
T::Origin: SenderOrigin<T::AccountId>,
Currency: CurrencyT<T::AccountId, Balance = T::OutboundMessageFee>, Currency: CurrencyT<T::AccountId, Balance = T::OutboundMessageFee>,
Currency::Balance: From<MessageNonce>, Currency::Balance: From<MessageNonce>,
GetConfirmationFee: Get<Currency::Balance>, GetConfirmationFee: Get<Currency::Balance>,
RootAccount: Get<Option<T::AccountId>>,
{ {
type Error = &'static str; type Error = &'static str;
fn pay_delivery_and_dispatch_fee( fn pay_delivery_and_dispatch_fee(
submitter: &Sender<T::AccountId>, submitter: &T::Origin,
fee: &Currency::Balance, fee: &Currency::Balance,
relayer_fund_account: &T::AccountId, relayer_fund_account: &T::AccountId,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
let submitter_account = match submitter.linked_account() {
Some(submitter_account) => submitter_account,
None if !fee.is_zero() => {
// if we'll accept some message that has declared that the `fee` has been paid but
// it isn't actually paid, then it'll lead to problems with delivery confirmation
// payments (see `pay_relayer_rewards` && `confirmation_relayer` in particular)
return Err(NON_ZERO_MESSAGE_FEE_CANT_BE_PAID_BY_NONE)
},
None => {
// message lane verifier has accepted the message before, so this message
// is unpaid **by design**
// => let's just do nothing
return Ok(())
},
};
if !frame_system::Pallet::<T>::account_exists(relayer_fund_account) { if !frame_system::Pallet::<T>::account_exists(relayer_fund_account) {
return Err("The relayer fund account must exist for the message lanes pallet to work correctly."); return Err("The relayer fund account must exist for the message lanes pallet to work correctly.");
} }
let root_account = RootAccount::get();
let account = match submitter {
Sender::Signed(submitter) => submitter,
Sender::Root | Sender::None => root_account
.as_ref()
.ok_or("Sending messages using Root or None origin is disallowed.")?,
};
Currency::transfer( Currency::transfer(
account, &submitter_account,
relayer_fund_account, relayer_fund_account,
*fee, *fee,
// it's fine for the submitter to go below Existential Deposit and die. // it's fine for the submitter to go below Existential Deposit and die.
@@ -226,7 +238,9 @@ fn pay_relayer_reward<Currency, AccountId>(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::mock::{run_test, AccountId as TestAccountId, Balance as TestBalance, TestRuntime}; use crate::mock::{
run_test, AccountId as TestAccountId, Balance as TestBalance, Origin, TestRuntime,
};
use bp_messages::source_chain::RelayerRewards; use bp_messages::source_chain::RelayerRewards;
type Balances = pallet_balances::Pallet<TestRuntime>; type Balances = pallet_balances::Pallet<TestRuntime>;
@@ -245,6 +259,48 @@ mod tests {
.collect() .collect()
} }
#[test]
fn pay_delivery_and_dispatch_fee_fails_on_non_zero_fee_and_unknown_payer() {
frame_support::parameter_types! {
const GetConfirmationFee: TestBalance = 0;
};
run_test(|| {
let result = InstantCurrencyPayments::<
TestRuntime,
(),
Balances,
GetConfirmationFee,
>::pay_delivery_and_dispatch_fee(
&Origin::root(),
&100,
&RELAYERS_FUND_ACCOUNT,
);
assert_eq!(result, Err(NON_ZERO_MESSAGE_FEE_CANT_BE_PAID_BY_NONE));
});
}
#[test]
fn pay_delivery_and_dispatch_succeeds_on_zero_fee_and_unknown_payer() {
frame_support::parameter_types! {
const GetConfirmationFee: TestBalance = 0;
};
run_test(|| {
let result = InstantCurrencyPayments::<
TestRuntime,
(),
Balances,
GetConfirmationFee,
>::pay_delivery_and_dispatch_fee(
&Origin::root(),
&0,
&RELAYERS_FUND_ACCOUNT,
);
assert!(result.is_ok());
});
}
#[test] #[test]
fn confirmation_relayer_is_rewarded_if_it_has_also_delivered_messages() { fn confirmation_relayer_is_rewarded_if_it_has_also_delivered_messages() {
run_test(|| { run_test(|| {
+13 -17
View File
@@ -170,12 +170,14 @@ pub mod pallet {
type TargetHeaderChain: TargetHeaderChain<Self::OutboundPayload, Self::AccountId>; type TargetHeaderChain: TargetHeaderChain<Self::OutboundPayload, Self::AccountId>;
/// Message payload verifier. /// Message payload verifier.
type LaneMessageVerifier: LaneMessageVerifier< type LaneMessageVerifier: LaneMessageVerifier<
Self::Origin,
Self::AccountId, Self::AccountId,
Self::OutboundPayload, Self::OutboundPayload,
Self::OutboundMessageFee, Self::OutboundMessageFee,
>; >;
/// Message delivery payment. /// Message delivery payment.
type MessageDeliveryAndDispatchPayment: MessageDeliveryAndDispatchPayment< type MessageDeliveryAndDispatchPayment: MessageDeliveryAndDispatchPayment<
Self::Origin,
Self::AccountId, Self::AccountId,
Self::OutboundMessageFee, Self::OutboundMessageFee,
>; >;
@@ -276,16 +278,12 @@ pub mod pallet {
payload: T::OutboundPayload, payload: T::OutboundPayload,
delivery_and_dispatch_fee: T::OutboundMessageFee, delivery_and_dispatch_fee: T::OutboundMessageFee,
) -> DispatchResultWithPostInfo { ) -> DispatchResultWithPostInfo {
crate::send_message::<T, I>( crate::send_message::<T, I>(origin, lane_id, payload, delivery_and_dispatch_fee).map(
origin.into().map_err(|_| BadOrigin)?, |sent_message| PostDispatchInfo {
lane_id, actual_weight: Some(sent_message.weight),
payload, pays_fee: Pays::Yes,
delivery_and_dispatch_fee, },
) )
.map(|sent_message| PostDispatchInfo {
actual_weight: Some(sent_message.weight),
pays_fee: Pays::Yes,
})
} }
/// Pay additional fee for the message. /// Pay additional fee for the message.
@@ -313,17 +311,15 @@ pub mod pallet {
); );
// withdraw additional fee from submitter // withdraw additional fee from submitter
let submitter = origin.into().map_err(|_| BadOrigin)?;
T::MessageDeliveryAndDispatchPayment::pay_delivery_and_dispatch_fee( T::MessageDeliveryAndDispatchPayment::pay_delivery_and_dispatch_fee(
&submitter, &origin,
&additional_fee, &additional_fee,
&relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>(), &relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>(),
) )
.map_err(|err| { .map_err(|err| {
log::trace!( log::trace!(
target: "runtime::bridge-messages", target: "runtime::bridge-messages",
"Submitter {:?} can't pay additional fee {:?} for the message {:?}/{:?} to {:?}: {:?}", "Submitter can't pay additional fee {:?} for the message {:?}/{:?} to {:?}: {:?}",
submitter,
additional_fee, additional_fee,
lane_id, lane_id,
nonce, nonce,
@@ -780,6 +776,7 @@ pub fn relayer_fund_account_id<AccountId, AccountIdConverter: Convert<H256, Acco
impl<T, I> impl<T, I>
bp_messages::source_chain::MessagesBridge< bp_messages::source_chain::MessagesBridge<
T::Origin,
T::AccountId, T::AccountId,
T::OutboundMessageFee, T::OutboundMessageFee,
T::OutboundPayload, T::OutboundPayload,
@@ -791,7 +788,7 @@ where
type Error = sp_runtime::DispatchErrorWithPostInfo<PostDispatchInfo>; type Error = sp_runtime::DispatchErrorWithPostInfo<PostDispatchInfo>;
fn send_message( fn send_message(
sender: bp_messages::source_chain::Sender<T::AccountId>, sender: T::Origin,
lane: LaneId, lane: LaneId,
message: T::OutboundPayload, message: T::OutboundPayload,
delivery_and_dispatch_fee: T::OutboundMessageFee, delivery_and_dispatch_fee: T::OutboundMessageFee,
@@ -802,7 +799,7 @@ where
/// Function that actually sends message. /// Function that actually sends message.
fn send_message<T: Config<I>, I: 'static>( fn send_message<T: Config<I>, I: 'static>(
submitter: bp_messages::source_chain::Sender<T::AccountId>, submitter: T::Origin,
lane_id: LaneId, lane_id: LaneId,
payload: T::OutboundPayload, payload: T::OutboundPayload,
delivery_and_dispatch_fee: T::OutboundMessageFee, delivery_and_dispatch_fee: T::OutboundMessageFee,
@@ -856,9 +853,8 @@ fn send_message<T: Config<I>, I: 'static>(
.map_err(|err| { .map_err(|err| {
log::trace!( log::trace!(
target: "runtime::bridge-messages", target: "runtime::bridge-messages",
"Message to lane {:?} is rejected because submitter {:?} is unable to pay fee {:?}: {:?}", "Message to lane {:?} is rejected because submitter is unable to pay fee {:?}: {:?}",
lane_id, lane_id,
submitter,
delivery_and_dispatch_fee, delivery_and_dispatch_fee,
err, err,
); );
+21 -8
View File
@@ -23,7 +23,7 @@ use bitvec::prelude::*;
use bp_messages::{ use bp_messages::{
source_chain::{ source_chain::{
LaneMessageVerifier, MessageDeliveryAndDispatchPayment, OnDeliveryConfirmed, LaneMessageVerifier, MessageDeliveryAndDispatchPayment, OnDeliveryConfirmed,
OnMessageAccepted, Sender, TargetHeaderChain, OnMessageAccepted, SenderOrigin, TargetHeaderChain,
}, },
target_chain::{ target_chain::{
DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain, DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain,
@@ -194,6 +194,16 @@ impl Config for TestRuntime {
type BridgedChainId = TestBridgedChainId; type BridgedChainId = TestBridgedChainId;
} }
impl SenderOrigin<AccountId> for Origin {
fn linked_account(&self) -> Option<AccountId> {
match self.caller {
OriginCaller::system(frame_system::RawOrigin::Signed(ref submitter)) =>
Some(submitter.clone()),
_ => None,
}
}
}
impl Size for TestPayload { impl Size for TestPayload {
fn size_hint(&self) -> u32 { fn size_hint(&self) -> u32 {
16 + self.extra.len() as u32 16 + self.extra.len() as u32
@@ -294,11 +304,13 @@ impl TargetHeaderChain<TestPayload, TestRelayer> for TestTargetHeaderChain {
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct TestLaneMessageVerifier; pub struct TestLaneMessageVerifier;
impl LaneMessageVerifier<AccountId, TestPayload, TestMessageFee> for TestLaneMessageVerifier { impl LaneMessageVerifier<Origin, AccountId, TestPayload, TestMessageFee>
for TestLaneMessageVerifier
{
type Error = &'static str; type Error = &'static str;
fn verify_message( fn verify_message(
_submitter: &Sender<AccountId>, _submitter: &Origin,
delivery_and_dispatch_fee: &TestMessageFee, delivery_and_dispatch_fee: &TestMessageFee,
_lane: &LaneId, _lane: &LaneId,
_lane_outbound_data: &OutboundLaneData, _lane_outbound_data: &OutboundLaneData,
@@ -324,8 +336,8 @@ impl TestMessageDeliveryAndDispatchPayment {
/// Returns true if given fee has been paid by given submitter. /// Returns true if given fee has been paid by given submitter.
pub fn is_fee_paid(submitter: AccountId, fee: TestMessageFee) -> bool { pub fn is_fee_paid(submitter: AccountId, fee: TestMessageFee) -> bool {
frame_support::storage::unhashed::get(b":message-fee:") == let raw_origin: Result<frame_system::RawOrigin<_>, _> = Origin::signed(submitter).into();
Some((Sender::Signed(submitter), fee)) frame_support::storage::unhashed::get(b":message-fee:") == Some((raw_origin.unwrap(), fee))
} }
/// Returns true if given relayer has been rewarded with given balance. The reward-paid flag is /// Returns true if given relayer has been rewarded with given balance. The reward-paid flag is
@@ -336,13 +348,13 @@ impl TestMessageDeliveryAndDispatchPayment {
} }
} }
impl MessageDeliveryAndDispatchPayment<AccountId, TestMessageFee> impl MessageDeliveryAndDispatchPayment<Origin, AccountId, TestMessageFee>
for TestMessageDeliveryAndDispatchPayment for TestMessageDeliveryAndDispatchPayment
{ {
type Error = &'static str; type Error = &'static str;
fn pay_delivery_and_dispatch_fee( fn pay_delivery_and_dispatch_fee(
submitter: &Sender<AccountId>, submitter: &Origin,
fee: &TestMessageFee, fee: &TestMessageFee,
_relayer_fund_account: &AccountId, _relayer_fund_account: &AccountId,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
@@ -350,7 +362,8 @@ impl MessageDeliveryAndDispatchPayment<AccountId, TestMessageFee>
return Err(TEST_ERROR) return Err(TEST_ERROR)
} }
frame_support::storage::unhashed::put(b":message-fee:", &(submitter, fee)); let raw_origin: Result<frame_system::RawOrigin<_>, _> = submitter.clone().into();
frame_support::storage::unhashed::put(b":message-fee:", &(raw_origin.unwrap(), fee));
Ok(()) Ok(())
} }
@@ -18,7 +18,7 @@
use crate::{ use crate::{
swap_account_id, target_account_at_this_chain, BridgedAccountIdOf, BridgedAccountPublicOf, swap_account_id, target_account_at_this_chain, BridgedAccountIdOf, BridgedAccountPublicOf,
BridgedAccountSignatureOf, BridgedBalanceOf, Call, Pallet, ThisChainBalance, BridgedAccountSignatureOf, BridgedBalanceOf, Call, Origin, Pallet, ThisChainBalance,
TokenSwapCreationOf, TokenSwapOf, TokenSwapCreationOf, TokenSwapOf,
}; };
@@ -43,6 +43,7 @@ pub trait Config<I: 'static>: crate::Config<I> {
benchmarks_instance_pallet! { benchmarks_instance_pallet! {
where_clause { where_clause {
where where
Origin<T, I>: Into<T::Origin>,
BridgedAccountPublicOf<T, I>: Decode + Parameter, BridgedAccountPublicOf<T, I>: Decode + Parameter,
BridgedAccountSignatureOf<T, I>: Decode, BridgedAccountSignatureOf<T, I>: Decode,
} }
+78 -26
View File
@@ -70,16 +70,18 @@ use bp_runtime::{messages::DispatchFeePayment, ChainId};
use bp_token_swap::{ use bp_token_swap::{
RawBridgedTransferCall, TokenSwap, TokenSwapCreation, TokenSwapState, TokenSwapType, RawBridgedTransferCall, TokenSwap, TokenSwapCreation, TokenSwapState, TokenSwapType,
}; };
use codec::Encode; use codec::{Decode, Encode};
use frame_support::{ use frame_support::{
fail, fail,
traits::{Currency, ExistenceRequirement}, traits::{Currency, ExistenceRequirement},
weights::PostDispatchInfo, weights::PostDispatchInfo,
RuntimeDebug,
}; };
use scale_info::TypeInfo;
use sp_core::H256; use sp_core::H256;
use sp_io::hashing::blake2_256; use sp_io::hashing::blake2_256;
use sp_runtime::traits::{Convert, Saturating}; use sp_runtime::traits::{Convert, Saturating};
use sp_std::boxed::Box; use sp_std::{boxed::Box, marker::PhantomData};
use weights::WeightInfo; use weights::WeightInfo;
pub use weights_ext::WeightInfoExt; pub use weights_ext::WeightInfoExt;
@@ -95,6 +97,24 @@ pub mod weights_ext;
pub use pallet::*; pub use pallet::*;
/// Name of the `PendingSwaps` storage map.
pub const PENDING_SWAPS_MAP_NAME: &str = "PendingSwaps";
/// Origin for the token swap pallet.
#[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo)]
pub enum RawOrigin<AccountId, I> {
/// The call is originated by the token swap account.
TokenSwap {
/// Id of the account that has started the swap.
source_account_at_this_chain: AccountId,
/// Id of the account that holds the funds during this swap. The message fee is paid from
/// this account funds.
swap_account_at_this_chain: AccountId,
},
/// Dummy to manage the fact we have instancing.
_Phantom(PhantomData<I>),
}
// comes from #[pallet::event] // comes from #[pallet::event]
#[allow(clippy::unused_unit)] #[allow(clippy::unused_unit)]
#[frame_support::pallet] #[frame_support::pallet]
@@ -123,6 +143,7 @@ pub mod pallet {
type OutboundMessageLaneId: Get<LaneId>; type OutboundMessageLaneId: Get<LaneId>;
/// Messages bridge with Bridged chain. /// Messages bridge with Bridged chain.
type MessagesBridge: MessagesBridge< type MessagesBridge: MessagesBridge<
Self::Origin,
Self::AccountId, Self::AccountId,
<Self::ThisCurrency as Currency<Self::AccountId>>::Balance, <Self::ThisCurrency as Currency<Self::AccountId>>::Balance,
MessagePayloadOf<Self, I>, MessagePayloadOf<Self, I>,
@@ -189,6 +210,7 @@ pub mod pallet {
impl<T: Config<I>, I: 'static> Pallet<T, I> impl<T: Config<I>, I: 'static> Pallet<T, I>
where where
BridgedAccountPublicOf<T, I>: Parameter, BridgedAccountPublicOf<T, I>: Parameter,
Origin<T, I>: Into<T::Origin>,
{ {
/// Start token swap procedure. /// Start token swap procedure.
/// ///
@@ -310,7 +332,11 @@ pub mod pallet {
// `Currency::transfer` call on the bridged chain, but no checks are made - it is // `Currency::transfer` call on the bridged chain, but no checks are made - it is
// the transaction submitter to ensure it is valid. // the transaction submitter to ensure it is valid.
let send_message_result = T::MessagesBridge::send_message( let send_message_result = T::MessagesBridge::send_message(
bp_messages::source_chain::Sender::from(Some(swap_account.clone())), RawOrigin::TokenSwap {
source_account_at_this_chain: swap.source_account_at_this_chain.clone(),
swap_account_at_this_chain: swap_account.clone(),
}
.into(),
T::OutboundMessageLaneId::get(), T::OutboundMessageLaneId::get(),
bp_message_dispatch::MessagePayload { bp_message_dispatch::MessagePayload {
spec_version: bridged_chain_spec_version, spec_version: bridged_chain_spec_version,
@@ -513,6 +539,10 @@ pub mod pallet {
InvalidClaimant, InvalidClaimant,
} }
/// Origin for the token swap pallet.
#[pallet::origin]
pub type Origin<T, I = ()> = RawOrigin<<T as frame_system::Config>::AccountId, I>;
/// Pending token swaps states. /// Pending token swaps states.
#[pallet::storage] #[pallet::storage]
pub type PendingSwaps<T: Config<I>, I: 'static = ()> = pub type PendingSwaps<T: Config<I>, I: 'static = ()> =
@@ -685,7 +715,7 @@ mod tests {
fn start_test_swap() { fn start_test_swap() {
assert_ok!(Pallet::<TestRuntime>::create_swap( assert_ok!(Pallet::<TestRuntime>::create_swap(
Origin::signed(THIS_CHAIN_ACCOUNT), mock::Origin::signed(THIS_CHAIN_ACCOUNT),
test_swap(), test_swap(),
Box::new(TokenSwapCreation { Box::new(TokenSwapCreation {
target_public_at_bridged_chain: bridged_chain_account_public(), target_public_at_bridged_chain: bridged_chain_account_public(),
@@ -710,7 +740,7 @@ mod tests {
run_test(|| { run_test(|| {
assert_noop!( assert_noop!(
Pallet::<TestRuntime>::create_swap( Pallet::<TestRuntime>::create_swap(
Origin::signed(THIS_CHAIN_ACCOUNT + 1), mock::Origin::signed(THIS_CHAIN_ACCOUNT + 1),
test_swap(), test_swap(),
Box::new(test_swap_creation()), Box::new(test_swap_creation()),
), ),
@@ -726,7 +756,7 @@ mod tests {
swap.source_balance_at_this_chain = ExistentialDeposit::get() - 1; swap.source_balance_at_this_chain = ExistentialDeposit::get() - 1;
assert_noop!( assert_noop!(
Pallet::<TestRuntime>::create_swap( Pallet::<TestRuntime>::create_swap(
Origin::signed(THIS_CHAIN_ACCOUNT), mock::Origin::signed(THIS_CHAIN_ACCOUNT),
swap, swap,
Box::new(test_swap_creation()), Box::new(test_swap_creation()),
), ),
@@ -742,7 +772,7 @@ mod tests {
swap.source_balance_at_this_chain = THIS_CHAIN_ACCOUNT_BALANCE + 1; swap.source_balance_at_this_chain = THIS_CHAIN_ACCOUNT_BALANCE + 1;
assert_noop!( assert_noop!(
Pallet::<TestRuntime>::create_swap( Pallet::<TestRuntime>::create_swap(
Origin::signed(THIS_CHAIN_ACCOUNT), mock::Origin::signed(THIS_CHAIN_ACCOUNT),
swap, swap,
Box::new(test_swap_creation()), Box::new(test_swap_creation()),
), ),
@@ -760,7 +790,7 @@ mod tests {
swap_creation.bridged_currency_transfer = transfer; swap_creation.bridged_currency_transfer = transfer;
assert_noop!( assert_noop!(
Pallet::<TestRuntime>::create_swap( Pallet::<TestRuntime>::create_swap(
Origin::signed(THIS_CHAIN_ACCOUNT), mock::Origin::signed(THIS_CHAIN_ACCOUNT),
test_swap(), test_swap(),
Box::new(swap_creation), Box::new(swap_creation),
), ),
@@ -773,14 +803,14 @@ mod tests {
fn create_swap_fails_if_swap_is_active() { fn create_swap_fails_if_swap_is_active() {
run_test(|| { run_test(|| {
assert_ok!(Pallet::<TestRuntime>::create_swap( assert_ok!(Pallet::<TestRuntime>::create_swap(
Origin::signed(THIS_CHAIN_ACCOUNT), mock::Origin::signed(THIS_CHAIN_ACCOUNT),
test_swap(), test_swap(),
Box::new(test_swap_creation()), Box::new(test_swap_creation()),
)); ));
assert_noop!( assert_noop!(
Pallet::<TestRuntime>::create_swap( Pallet::<TestRuntime>::create_swap(
Origin::signed(THIS_CHAIN_ACCOUNT), mock::Origin::signed(THIS_CHAIN_ACCOUNT),
test_swap(), test_swap(),
Box::new(test_swap_creation()), Box::new(test_swap_creation()),
), ),
@@ -795,7 +825,7 @@ mod tests {
frame_system::Pallet::<TestRuntime>::set_block_number(CAN_START_BLOCK_NUMBER + 1); frame_system::Pallet::<TestRuntime>::set_block_number(CAN_START_BLOCK_NUMBER + 1);
assert_noop!( assert_noop!(
Pallet::<TestRuntime>::create_swap( Pallet::<TestRuntime>::create_swap(
Origin::signed(THIS_CHAIN_ACCOUNT), mock::Origin::signed(THIS_CHAIN_ACCOUNT),
test_swap(), test_swap(),
Box::new(test_swap_creation()), Box::new(test_swap_creation()),
), ),
@@ -809,7 +839,7 @@ mod tests {
run_test(|| { run_test(|| {
frame_system::Pallet::<TestRuntime>::set_block_number(CAN_START_BLOCK_NUMBER); frame_system::Pallet::<TestRuntime>::set_block_number(CAN_START_BLOCK_NUMBER);
assert_ok!(Pallet::<TestRuntime>::create_swap( assert_ok!(Pallet::<TestRuntime>::create_swap(
Origin::signed(THIS_CHAIN_ACCOUNT), mock::Origin::signed(THIS_CHAIN_ACCOUNT),
test_swap(), test_swap(),
Box::new(test_swap_creation()), Box::new(test_swap_creation()),
)); ));
@@ -823,7 +853,7 @@ mod tests {
frame_system::Pallet::<TestRuntime>::reset_events(); frame_system::Pallet::<TestRuntime>::reset_events();
assert_ok!(Pallet::<TestRuntime>::create_swap( assert_ok!(Pallet::<TestRuntime>::create_swap(
Origin::signed(THIS_CHAIN_ACCOUNT), mock::Origin::signed(THIS_CHAIN_ACCOUNT),
test_swap(), test_swap(),
Box::new(test_swap_creation()), Box::new(test_swap_creation()),
)); ));
@@ -855,7 +885,7 @@ mod tests {
run_test(|| { run_test(|| {
assert_noop!( assert_noop!(
Pallet::<TestRuntime>::claim_swap( Pallet::<TestRuntime>::claim_swap(
Origin::signed( mock::Origin::signed(
1 + target_account_at_this_chain::<TestRuntime, ()>(&test_swap()) 1 + target_account_at_this_chain::<TestRuntime, ()>(&test_swap())
), ),
test_swap(), test_swap(),
@@ -872,7 +902,9 @@ mod tests {
assert_noop!( assert_noop!(
Pallet::<TestRuntime>::claim_swap( Pallet::<TestRuntime>::claim_swap(
Origin::signed(target_account_at_this_chain::<TestRuntime, ()>(&test_swap())), mock::Origin::signed(target_account_at_this_chain::<TestRuntime, ()>(
&test_swap()
)),
test_swap(), test_swap(),
), ),
Error::<TestRuntime, ()>::SwapIsPending Error::<TestRuntime, ()>::SwapIsPending
@@ -887,7 +919,9 @@ mod tests {
assert_noop!( assert_noop!(
Pallet::<TestRuntime>::claim_swap( Pallet::<TestRuntime>::claim_swap(
Origin::signed(target_account_at_this_chain::<TestRuntime, ()>(&test_swap())), mock::Origin::signed(target_account_at_this_chain::<TestRuntime, ()>(
&test_swap()
)),
test_swap(), test_swap(),
), ),
Error::<TestRuntime, ()>::SwapIsFailed Error::<TestRuntime, ()>::SwapIsFailed
@@ -900,7 +934,9 @@ mod tests {
run_test(|| { run_test(|| {
assert_noop!( assert_noop!(
Pallet::<TestRuntime>::claim_swap( Pallet::<TestRuntime>::claim_swap(
Origin::signed(target_account_at_this_chain::<TestRuntime, ()>(&test_swap())), mock::Origin::signed(target_account_at_this_chain::<TestRuntime, ()>(
&test_swap()
)),
test_swap(), test_swap(),
), ),
Error::<TestRuntime, ()>::SwapIsInactive Error::<TestRuntime, ()>::SwapIsInactive
@@ -916,7 +952,9 @@ mod tests {
assert_noop!( assert_noop!(
Pallet::<TestRuntime>::claim_swap( Pallet::<TestRuntime>::claim_swap(
Origin::signed(target_account_at_this_chain::<TestRuntime, ()>(&test_swap())), mock::Origin::signed(target_account_at_this_chain::<TestRuntime, ()>(
&test_swap()
)),
test_swap(), test_swap(),
), ),
Error::<TestRuntime, ()>::FailedToTransferFromSwapAccount Error::<TestRuntime, ()>::FailedToTransferFromSwapAccount
@@ -934,7 +972,9 @@ mod tests {
assert_noop!( assert_noop!(
Pallet::<TestRuntime>::claim_swap( Pallet::<TestRuntime>::claim_swap(
Origin::signed(target_account_at_this_chain::<TestRuntime, ()>(&test_swap())), mock::Origin::signed(target_account_at_this_chain::<TestRuntime, ()>(
&test_swap()
)),
test_swap(), test_swap(),
), ),
Error::<TestRuntime, ()>::SwapIsTemporaryLocked Error::<TestRuntime, ()>::SwapIsTemporaryLocked
@@ -952,7 +992,7 @@ mod tests {
frame_system::Pallet::<TestRuntime>::reset_events(); frame_system::Pallet::<TestRuntime>::reset_events();
assert_ok!(Pallet::<TestRuntime>::claim_swap( assert_ok!(Pallet::<TestRuntime>::claim_swap(
Origin::signed(target_account_at_this_chain::<TestRuntime, ()>(&test_swap())), mock::Origin::signed(target_account_at_this_chain::<TestRuntime, ()>(&test_swap())),
test_swap(), test_swap(),
)); ));
@@ -988,7 +1028,7 @@ mod tests {
assert_noop!( assert_noop!(
Pallet::<TestRuntime>::cancel_swap( Pallet::<TestRuntime>::cancel_swap(
Origin::signed(THIS_CHAIN_ACCOUNT + 1), mock::Origin::signed(THIS_CHAIN_ACCOUNT + 1),
test_swap() test_swap()
), ),
Error::<TestRuntime, ()>::MismatchedSwapSourceOrigin Error::<TestRuntime, ()>::MismatchedSwapSourceOrigin
@@ -1002,7 +1042,10 @@ mod tests {
start_test_swap(); start_test_swap();
assert_noop!( assert_noop!(
Pallet::<TestRuntime>::cancel_swap(Origin::signed(THIS_CHAIN_ACCOUNT), test_swap()), Pallet::<TestRuntime>::cancel_swap(
mock::Origin::signed(THIS_CHAIN_ACCOUNT),
test_swap()
),
Error::<TestRuntime, ()>::SwapIsPending Error::<TestRuntime, ()>::SwapIsPending
); );
}); });
@@ -1015,7 +1058,10 @@ mod tests {
receive_test_swap_confirmation(true); receive_test_swap_confirmation(true);
assert_noop!( assert_noop!(
Pallet::<TestRuntime>::cancel_swap(Origin::signed(THIS_CHAIN_ACCOUNT), test_swap()), Pallet::<TestRuntime>::cancel_swap(
mock::Origin::signed(THIS_CHAIN_ACCOUNT),
test_swap()
),
Error::<TestRuntime, ()>::SwapIsConfirmed Error::<TestRuntime, ()>::SwapIsConfirmed
); );
}); });
@@ -1025,7 +1071,10 @@ mod tests {
fn cancel_swap_fails_if_swap_is_inactive() { fn cancel_swap_fails_if_swap_is_inactive() {
run_test(|| { run_test(|| {
assert_noop!( assert_noop!(
Pallet::<TestRuntime>::cancel_swap(Origin::signed(THIS_CHAIN_ACCOUNT), test_swap()), Pallet::<TestRuntime>::cancel_swap(
mock::Origin::signed(THIS_CHAIN_ACCOUNT),
test_swap()
),
Error::<TestRuntime, ()>::SwapIsInactive Error::<TestRuntime, ()>::SwapIsInactive
); );
}); });
@@ -1042,7 +1091,10 @@ mod tests {
); );
assert_noop!( assert_noop!(
Pallet::<TestRuntime>::cancel_swap(Origin::signed(THIS_CHAIN_ACCOUNT), test_swap()), Pallet::<TestRuntime>::cancel_swap(
mock::Origin::signed(THIS_CHAIN_ACCOUNT),
test_swap()
),
Error::<TestRuntime, ()>::FailedToTransferFromSwapAccount Error::<TestRuntime, ()>::FailedToTransferFromSwapAccount
); );
}); });
@@ -1058,7 +1110,7 @@ mod tests {
frame_system::Pallet::<TestRuntime>::reset_events(); frame_system::Pallet::<TestRuntime>::reset_events();
assert_ok!(Pallet::<TestRuntime>::cancel_swap( assert_ok!(Pallet::<TestRuntime>::cancel_swap(
Origin::signed(THIS_CHAIN_ACCOUNT), mock::Origin::signed(THIS_CHAIN_ACCOUNT),
test_swap() test_swap()
)); ));
+9 -4
View File
@@ -56,7 +56,7 @@ frame_support::construct_runtime! {
{ {
System: frame_system::{Pallet, Call, Config, Storage, Event<T>}, System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
Balances: pallet_balances::{Pallet, Call, Event<T>}, Balances: pallet_balances::{Pallet, Call, Event<T>},
TokenSwap: pallet_bridge_token_swap::{Pallet, Call, Event<T>}, TokenSwap: pallet_bridge_token_swap::{Pallet, Call, Event<T>, Origin<T>},
} }
} }
@@ -154,18 +154,23 @@ impl bp_runtime::Chain for BridgedChain {
pub struct TestMessagesBridge; pub struct TestMessagesBridge;
impl MessagesBridge<AccountId, Balance, MessagePayloadOf<TestRuntime, ()>> for TestMessagesBridge { impl MessagesBridge<Origin, AccountId, Balance, MessagePayloadOf<TestRuntime, ()>>
for TestMessagesBridge
{
type Error = (); type Error = ();
fn send_message( fn send_message(
sender: frame_system::RawOrigin<AccountId>, sender: Origin,
lane: LaneId, lane: LaneId,
message: MessagePayloadOf<TestRuntime, ()>, message: MessagePayloadOf<TestRuntime, ()>,
delivery_and_dispatch_fee: Balance, delivery_and_dispatch_fee: Balance,
) -> Result<SendMessageArtifacts, Self::Error> { ) -> Result<SendMessageArtifacts, Self::Error> {
assert_ne!(sender, frame_system::RawOrigin::Signed(THIS_CHAIN_ACCOUNT));
assert_eq!(lane, OutboundMessageLaneId::get()); assert_eq!(lane, OutboundMessageLaneId::get());
assert_eq!(delivery_and_dispatch_fee, SWAP_DELIVERY_AND_DISPATCH_FEE); assert_eq!(delivery_and_dispatch_fee, SWAP_DELIVERY_AND_DISPATCH_FEE);
match sender.caller {
OriginCaller::TokenSwap(_) => (),
_ => panic!("unexpected origin"),
}
match message.call[0] { match message.call[0] {
OK_TRANSFER_CALL => Ok(SendMessageArtifacts { nonce: MESSAGE_NONCE, weight: 0 }), OK_TRANSFER_CALL => Ok(SendMessageArtifacts { nonce: MESSAGE_NONCE, weight: 0 }),
BAD_TRANSFER_CALL => Err(()), BAD_TRANSFER_CALL => Err(()),
+1
View File
@@ -31,6 +31,7 @@ hex-literal = "0.3"
[features] [features]
default = ["std"] default = ["std"]
std = [ std = [
"bitvec/std",
"bp-runtime/std", "bp-runtime/std",
"codec/std", "codec/std",
"frame-support/std", "frame-support/std",
+30 -16
View File
@@ -28,7 +28,21 @@ use sp_std::{
}; };
/// The sender of the message on the source chain. /// The sender of the message on the source chain.
pub type Sender<AccountId> = frame_system::RawOrigin<AccountId>; pub trait SenderOrigin<AccountId> {
/// Return id of the account that is sending this message.
///
/// In regular messages configuration, when regular message is sent you'll always get `Some(_)`
/// from this call. This is the account that is paying send costs. However, there are some
/// examples when `None` may be returned from the call:
///
/// - if the send-message call origin is either `frame_system::RawOrigin::Root` or
/// `frame_system::RawOrigin::None` and your configuration forbids such messages;
/// - if your configuration allows 'unpaid' messages sent by pallets. Then the pallet may just
/// use its own defined origin (not linked to any account) and the message will be accepted.
/// This may be useful for pallets that are sending important system-wide information (like
/// update of runtime version).
fn linked_account(&self) -> Option<AccountId>;
}
/// Relayers rewards, grouped by relayer account id. /// Relayers rewards, grouped by relayer account id.
pub type RelayersRewards<AccountId, Balance> = BTreeMap<AccountId, RelayerRewards<Balance>>; pub type RelayersRewards<AccountId, Balance> = BTreeMap<AccountId, RelayerRewards<Balance>>;
@@ -82,14 +96,14 @@ pub trait TargetHeaderChain<Payload, AccountId> {
/// Lane3 until some block, ...), then it may be built using this verifier. /// Lane3 until some block, ...), then it may be built using this verifier.
/// ///
/// Any fee requirements should also be enforced here. /// Any fee requirements should also be enforced here.
pub trait LaneMessageVerifier<Submitter, Payload, Fee> { pub trait LaneMessageVerifier<SenderOrigin, Submitter, Payload, Fee> {
/// Error type. /// Error type.
type Error: Debug + Into<&'static str>; type Error: Debug + Into<&'static str>;
/// Verify message payload and return Ok(()) if message is valid and allowed to be sent over the /// Verify message payload and return Ok(()) if message is valid and allowed to be sent over the
/// lane. /// lane.
fn verify_message( fn verify_message(
submitter: &Sender<Submitter>, submitter: &SenderOrigin,
delivery_and_dispatch_fee: &Fee, delivery_and_dispatch_fee: &Fee,
lane: &LaneId, lane: &LaneId,
outbound_data: &OutboundLaneData, outbound_data: &OutboundLaneData,
@@ -110,14 +124,14 @@ pub trait LaneMessageVerifier<Submitter, Payload, Fee> {
/// So to be sure that any non-altruist relayer would agree to deliver message, submitter /// So to be sure that any non-altruist relayer would agree to deliver message, submitter
/// should set `delivery_and_dispatch_fee` to at least (equivalent of): sum of fees from (2) /// should set `delivery_and_dispatch_fee` to at least (equivalent of): sum of fees from (2)
/// to (4) above, plus some interest for the relayer. /// to (4) above, plus some interest for the relayer.
pub trait MessageDeliveryAndDispatchPayment<AccountId, Balance> { pub trait MessageDeliveryAndDispatchPayment<SenderOrigin, AccountId, Balance> {
/// Error type. /// Error type.
type Error: Debug + Into<&'static str>; type Error: Debug + Into<&'static str>;
/// Withhold/write-off delivery_and_dispatch_fee from submitter account to /// Withhold/write-off delivery_and_dispatch_fee from submitter account to
/// some relayers-fund account. /// some relayers-fund account.
fn pay_delivery_and_dispatch_fee( fn pay_delivery_and_dispatch_fee(
submitter: &Sender<AccountId>, submitter: &SenderOrigin,
fee: &Balance, fee: &Balance,
relayer_fund_account: &AccountId, relayer_fund_account: &AccountId,
) -> Result<(), Self::Error>; ) -> Result<(), Self::Error>;
@@ -145,7 +159,7 @@ pub struct SendMessageArtifacts {
} }
/// Messages bridge API to be used from other pallets. /// Messages bridge API to be used from other pallets.
pub trait MessagesBridge<AccountId, Balance, Payload> { pub trait MessagesBridge<SenderOrigin, AccountId, Balance, Payload> {
/// Error type. /// Error type.
type Error: Debug; type Error: Debug;
@@ -153,7 +167,7 @@ pub trait MessagesBridge<AccountId, Balance, Payload> {
/// ///
/// Returns unique message nonce or error if send has failed. /// Returns unique message nonce or error if send has failed.
fn send_message( fn send_message(
sender: Sender<AccountId>, sender: SenderOrigin,
lane: LaneId, lane: LaneId,
message: Payload, message: Payload,
delivery_and_dispatch_fee: Balance, delivery_and_dispatch_fee: Balance,
@@ -164,13 +178,13 @@ pub trait MessagesBridge<AccountId, Balance, Payload> {
#[derive(RuntimeDebug, PartialEq)] #[derive(RuntimeDebug, PartialEq)]
pub struct NoopMessagesBridge; pub struct NoopMessagesBridge;
impl<AccountId, Balance, Payload> MessagesBridge<AccountId, Balance, Payload> impl<SenderOrigin, AccountId, Balance, Payload>
for NoopMessagesBridge MessagesBridge<SenderOrigin, AccountId, Balance, Payload> for NoopMessagesBridge
{ {
type Error = &'static str; type Error = &'static str;
fn send_message( fn send_message(
_sender: Sender<AccountId>, _sender: SenderOrigin,
_lane: LaneId, _lane: LaneId,
_message: Payload, _message: Payload,
_delivery_and_dispatch_fee: Balance, _delivery_and_dispatch_fee: Balance,
@@ -245,13 +259,13 @@ impl<Payload, AccountId> TargetHeaderChain<Payload, AccountId> for ForbidOutboun
} }
} }
impl<Submitter, Payload, Fee> LaneMessageVerifier<Submitter, Payload, Fee> impl<SenderOrigin, Submitter, Payload, Fee>
for ForbidOutboundMessages LaneMessageVerifier<SenderOrigin, Submitter, Payload, Fee> for ForbidOutboundMessages
{ {
type Error = &'static str; type Error = &'static str;
fn verify_message( fn verify_message(
_submitter: &Sender<Submitter>, _submitter: &SenderOrigin,
_delivery_and_dispatch_fee: &Fee, _delivery_and_dispatch_fee: &Fee,
_lane: &LaneId, _lane: &LaneId,
_outbound_data: &OutboundLaneData, _outbound_data: &OutboundLaneData,
@@ -261,13 +275,13 @@ impl<Submitter, Payload, Fee> LaneMessageVerifier<Submitter, Payload, Fee>
} }
} }
impl<AccountId, Balance> MessageDeliveryAndDispatchPayment<AccountId, Balance> impl<SenderOrigin, AccountId, Balance>
for ForbidOutboundMessages MessageDeliveryAndDispatchPayment<SenderOrigin, AccountId, Balance> for ForbidOutboundMessages
{ {
type Error = &'static str; type Error = &'static str;
fn pay_delivery_and_dispatch_fee( fn pay_delivery_and_dispatch_fee(
_submitter: &Sender<AccountId>, _submitter: &SenderOrigin,
_fee: &Balance, _fee: &Balance,
_relayer_fund_account: &AccountId, _relayer_fund_account: &AccountId,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {