Skip to content
This repository has been archived by the owner on Feb 29, 2024. It is now read-only.

Commit

Permalink
Integrate fee-market to FromThisChainMessageVerifier (#189)
Browse files Browse the repository at this point in the history
* Run ignored crate

* Fix tests

* Add features

* Fix compile
  • Loading branch information
boundless-forest committed Sep 28, 2022
1 parent 4c0f658 commit 2d6356a
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 208 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
- bp-pangolin-parachain
- bp-rococo
- bp-darwinia-core
- bridge-runtime-common
steps:
- uses: actions/checkout@v2
- name: Check ${{ matrix.package }}
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions bin/runtime-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ bp-runtime = { path = "../../primitives/runtime", default-features = false }
pallet-bridge-dispatch = { path = "../../modules/dispatch", default-features = false }
pallet-bridge-grandpa = { path = "../../modules/grandpa", default-features = false }
pallet-bridge-messages = { path = "../../modules/messages", default-features = false }
pallet-fee-market = { path = "../../modules/fee-market", default-features = false }

# Substrate dependencies

Expand Down Expand Up @@ -50,6 +51,7 @@ std = [
"pallet-bridge-dispatch/std",
"pallet-bridge-grandpa/std",
"pallet-bridge-messages/std",
"pallet-fee-market/std",
"pallet-transaction-payment/std",
"scale-info/std",
"sp-api/std",
Expand Down
240 changes: 32 additions & 208 deletions bin/runtime-common/src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ pub mod source {
/// dispatch origin;
/// - check that the sender has paid enough funds for both message delivery and dispatch.
#[derive(RuntimeDebug)]
pub struct FromThisChainMessageVerifier<B>(PhantomData<B>);
pub struct FromThisChainMessageVerifier<B, F, I>(PhantomData<(B, F, I)>);

/// The error message returned from LaneMessageVerifier when outbound lane is disabled.
pub const MESSAGE_REJECTED_BY_OUTBOUND_LANE: &str =
Expand All @@ -282,23 +282,27 @@ pub mod source {
/// The error message returned from LaneMessageVerifier when the message fee is too low.
pub const TOO_LOW_FEE: &str = "Provided fee is below minimal threshold required by the lane.";

impl<B>
impl<B, F, I>
LaneMessageVerifier<
OriginOf<ThisChain<B>>,
AccountIdOf<ThisChain<B>>,
FromThisChainMessagePayload<B>,
BalanceOf<ThisChain<B>>,
> for FromThisChainMessageVerifier<B>
> for FromThisChainMessageVerifier<B, F, I>
where
B: MessageBridge,
F: pallet_fee_market::Config<I>,
I: 'static,
// 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,
pallet_fee_market::BalanceOf<F, I>: From<BalanceOf<ThisChain<B>>>,
{
type Error = &'static str;

#[allow(clippy::single_match)]
#[cfg(not(feature = "runtime-benchmarks"))]
fn verify_message(
submitter: &OriginOf<ThisChain<B>>,
delivery_and_dispatch_fee: &BalanceOf<ThisChain<B>>,
Expand Down Expand Up @@ -338,19 +342,35 @@ pub mod source {
// is valid, or not.
};

let minimal_fee_in_this_tokens = estimate_message_dispatch_and_delivery_fee::<B>(
payload,
B::RELAYER_FEE_PERCENT,
None,
)?;
// Do the delivery_and_dispatch_fee. We assume that the delivery and dispatch fee always
// greater than the fee market provided fee.
if let Some(market_fee) = pallet_fee_market::Pallet::<F, I>::market_fee() {
let message_fee: pallet_fee_market::BalanceOf<F, I> =
(*delivery_and_dispatch_fee).into();

// compare with actual fee paid
if *delivery_and_dispatch_fee < minimal_fee_in_this_tokens {
return Err(TOO_LOW_FEE);
// compare with actual fee paid
if message_fee < market_fee {
return Err(TOO_LOW_FEE);
}
} else {
const NO_MARKET_FEE: &str = "The fee market are not ready for accepting messages.";

return Err(NO_MARKET_FEE);
}

Ok(())
}

#[cfg(feature = "runtime-benchmarks")]
fn verify_message(
_submitter: &OriginOf<ThisChain<B>>,
_delivery_and_dispatch_fee: &BalanceOf<ThisChain<B>>,
_lane: &LaneId,
_lane_outbound_data: &OutboundLaneData,
_payload: &FromThisChainMessagePayload<B>,
) -> Result<(), Self::Error> {
Ok(())
}
}

/// Return maximal message size of This -> Bridged chain message.
Expand Down Expand Up @@ -813,15 +833,14 @@ pub mod target {
#[cfg(test)]
mod tests {
use super::*;
use bp_runtime::messages::DispatchFeePayment;
use codec::{Decode, Encode};
use frame_support::weights::Weight;
use std::ops::RangeInclusive;

const DELIVERY_TRANSACTION_WEIGHT: Weight = 100;
const DELIVERY_CONFIRMATION_TRANSACTION_WEIGHT: Weight = 100;
const THIS_CHAIN_WEIGHT_TO_BALANCE_RATE: Weight = 2;
const BRIDGED_CHAIN_WEIGHT_TO_BALANCE_RATE: Weight = 4;
const BRIDGED_CHAIN_TO_THIS_CHAIN_BALANCE_RATE: u32 = 6;
const BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT: Weight = 2048;
const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024;

Expand Down Expand Up @@ -1107,10 +1126,6 @@ mod tests {
}
}

fn test_lane_outbound_data() -> OutboundLaneData {
OutboundLaneData::default()
}

#[test]
fn message_from_bridged_chain_is_decoded() {
// the message is encoded on the bridged chain
Expand Down Expand Up @@ -1148,180 +1163,6 @@ mod tests {
const TEST_LANE_ID: &LaneId = b"test";
const MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE: MessageNonce = 32;

fn regular_outbound_message_payload() -> source::FromThisChainMessagePayload<OnThisChainBridge>
{
source::FromThisChainMessagePayload::<OnThisChainBridge> {
spec_version: 1,
weight: 100,
origin: bp_message_dispatch::CallOrigin::SourceRoot,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
call: vec![42],
}
}

#[test]
fn message_fee_is_checked_by_verifier() {
const EXPECTED_MINIMAL_FEE: u32 = 5500;

// payload of the This -> Bridged chain message
let payload = regular_outbound_message_payload();

// let's check if estimation matching hardcoded value
assert_eq!(
source::estimate_message_dispatch_and_delivery_fee::<OnThisChainBridge>(
&payload,
OnThisChainBridge::RELAYER_FEE_PERCENT,
None,
),
Ok(ThisChainBalance(EXPECTED_MINIMAL_FEE)),
);

// let's check if estimation is less than hardcoded, if dispatch is paid at target chain
let mut payload_with_pay_on_target = regular_outbound_message_payload();
payload_with_pay_on_target.dispatch_fee_payment = DispatchFeePayment::AtTargetChain;
let fee_at_source =
source::estimate_message_dispatch_and_delivery_fee::<OnThisChainBridge>(
&payload_with_pay_on_target,
OnThisChainBridge::RELAYER_FEE_PERCENT,
None,
)
.expect(
"estimate_message_dispatch_and_delivery_fee failed for pay-at-target-chain message",
);
assert!(
fee_at_source < EXPECTED_MINIMAL_FEE.into(),
"Computed fee {:?} without prepaid dispatch must be less than the fee with prepaid dispatch {}",
fee_at_source,
EXPECTED_MINIMAL_FEE,
);

// and now check that the verifier checks the fee
assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&ThisChainOrigin(Ok(frame_system::RawOrigin::Root)),
&ThisChainBalance(1),
TEST_LANE_ID,
&test_lane_outbound_data(),
&payload,
),
Err(source::TOO_LOW_FEE)
);
assert!(source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&ThisChainOrigin(Ok(frame_system::RawOrigin::Root)),
&ThisChainBalance(1_000_000),
TEST_LANE_ID,
&test_lane_outbound_data(),
&payload,
)
.is_ok(),);
}

#[test]
fn should_disallow_root_calls_from_regular_accounts() {
// payload of the This -> Bridged chain message
let payload = source::FromThisChainMessagePayload::<OnThisChainBridge> {
spec_version: 1,
weight: 100,
origin: bp_message_dispatch::CallOrigin::SourceRoot,
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
call: vec![42],
};

// and now check that the verifier checks the fee
assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&ThisChainOrigin(Ok(frame_system::RawOrigin::Signed(ThisChainAccountId(0)))),
&ThisChainBalance(1_000_000),
TEST_LANE_ID,
&test_lane_outbound_data(),
&payload,
),
Err(source::BAD_ORIGIN)
);
assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&ThisChainOrigin(Ok(frame_system::RawOrigin::None)),
&ThisChainBalance(1_000_000),
TEST_LANE_ID,
&test_lane_outbound_data(),
&payload,
),
Err(source::BAD_ORIGIN)
);
assert!(source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&ThisChainOrigin(Ok(frame_system::RawOrigin::Root)),
&ThisChainBalance(1_000_000),
TEST_LANE_ID,
&test_lane_outbound_data(),
&payload,
)
.is_ok(),);
}

#[test]
fn should_verify_source_and_target_origin_matching() {
// payload of the This -> Bridged chain message
let payload = source::FromThisChainMessagePayload::<OnThisChainBridge> {
spec_version: 1,
weight: 100,
origin: bp_message_dispatch::CallOrigin::SourceAccount(ThisChainAccountId(1)),
dispatch_fee_payment: DispatchFeePayment::AtSourceChain,
call: vec![42],
};

// and now check that the verifier checks the fee
assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&ThisChainOrigin(Ok(frame_system::RawOrigin::Signed(ThisChainAccountId(0)))),
&ThisChainBalance(1_000_000),
TEST_LANE_ID,
&test_lane_outbound_data(),
&payload,
),
Err(source::BAD_ORIGIN)
);
assert!(source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&ThisChainOrigin(Ok(frame_system::RawOrigin::Signed(ThisChainAccountId(1)))),
&ThisChainBalance(1_000_000),
TEST_LANE_ID,
&test_lane_outbound_data(),
&payload,
)
.is_ok(),);
}

#[test]
fn message_is_rejected_when_sent_using_disabled_lane() {
assert_eq!(
source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message(
&ThisChainOrigin(Ok(frame_system::RawOrigin::Root)),
&ThisChainBalance(1_000_000),
b"dsbl",
&test_lane_outbound_data(),
&regular_outbound_message_payload(),
),
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(
&ThisChainOrigin(Ok(frame_system::RawOrigin::Root)),
&ThisChainBalance(1_000_000),
TEST_LANE_ID,
&OutboundLaneData {
latest_received_nonce: 100,
latest_generated_nonce: 100 + MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE + 1,
..Default::default()
},
&regular_outbound_message_payload(),
),
Err(source::TOO_MANY_PENDING_MESSAGES)
);
}

#[test]
fn verify_chain_message_rejects_message_with_too_small_declared_weight() {
assert!(source::verify_chain_message::<OnThisChainBridge>(
Expand Down Expand Up @@ -1650,21 +1491,4 @@ mod tests {
100 + 50 * 10 + 777,
);
}

#[test]
fn conversion_rate_override_works() {
let payload = regular_outbound_message_payload();
let regular_fee = source::estimate_message_dispatch_and_delivery_fee::<OnThisChainBridge>(
&payload,
OnThisChainBridge::RELAYER_FEE_PERCENT,
None,
);
let overrided_fee = source::estimate_message_dispatch_and_delivery_fee::<OnThisChainBridge>(
&payload,
OnThisChainBridge::RELAYER_FEE_PERCENT,
Some(FixedU128::from_float((BRIDGED_CHAIN_TO_THIS_CHAIN_BALANCE_RATE * 2) as f64)),
);

assert!(regular_fee < overrided_fee);
}
}

0 comments on commit 2d6356a

Please sign in to comment.