Skip to content

Commit

Permalink
Nits
Browse files Browse the repository at this point in the history
  • Loading branch information
bkontur committed Nov 5, 2024
1 parent 13280f8 commit d821c08
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 33 deletions.
33 changes: 30 additions & 3 deletions bridges/modules/xcm-bridge-hub-router/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
#![cfg_attr(not(feature = "std"), no_std)]

pub use bp_xcm_bridge_hub_router::XcmChannelStatusProvider;
use bp_xcm_bridge_hub_router::{BridgeState, ResolveBridgeId};
use codec::Encode;
use frame_support::traits::Get;
Expand Down Expand Up @@ -76,9 +75,35 @@ pub mod pallet {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;

#[pallet::config]
/// Default implementations of [`DefaultConfig`], which can be used to implement [`Config`].
pub mod config_preludes {
use super::*;
use frame_support::{derive_impl, traits::ConstU128};

/// A type providing default configurations for this pallet in testing environment.
pub struct TestDefaultConfig;

#[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
impl frame_system::DefaultConfig for TestDefaultConfig {}

#[frame_support::register_default_impl(TestDefaultConfig)]
impl DefaultConfig for TestDefaultConfig {
#[inject_runtime_type]
type RuntimeEvent = ();
type WeightInfo = ();
type DestinationVersion = AlwaysLatest;

// We don't need (optional) message_size fees.
type ByteFee = ConstU128<0>;
// We don't need (optional) message_size fees.
type FeeAsset = ();
}
}

#[pallet::config(with_default)]
pub trait Config<I: 'static = ()>: frame_system::Config {
/// The overarching event type.
#[pallet::no_default_bounds]
type RuntimeEvent: From<Event<Self, I>>
+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
/// Benchmarks results from runtime we're plugged into.
Expand All @@ -94,10 +119,12 @@ pub mod pallet {
/// - The local chain, in which case we need an implementation for `T::ToBridgeHubSender`
/// that does not use `ExportMessage` but instead directly calls the `ExportXcm`
/// implementation.
#[pallet::no_default]
type ToBridgeHubSender: SendXcm;

/// Resolves a specific `BridgeId` for `dest`, used for identifying the bridge in cases of congestion and dynamic fees.
/// If it resolves to `None`, it means no congestion or dynamic fees are handled for `dest`.
#[pallet::no_default]
type BridgeIdResolver: ResolveBridgeId;

/// Additional fee that is paid for every byte of the outbound message.
Expand Down Expand Up @@ -179,7 +206,7 @@ pub mod pallet {

/// Stores `BridgeState` for congestion control and dynamic fees for each resolved bridge ID associated with a destination.
#[pallet::storage]
pub type Bridges<T: Config<I>, I: 'static = ()> = StorageMap<_, Blake2_128Concat, BridgeIdOf<T, I>, BridgeState, OptionQuery>;
pub type Bridges<T: Config<I>, I: 'static = ()> = StorageMap<_, Identity, BridgeIdOf<T, I>, BridgeState, OptionQuery>;

impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// Called when new message is sent to the `dest` (queued to local outbound XCM queue).
Expand Down
4 changes: 1 addition & 3 deletions bridges/modules/xcm-bridge-hub-router/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,8 @@ impl ResolveBridgeId for EveryDestinationToSameBridgeIdResolver {
}

/// An instance of `pallet_xcm_bridge_hub_router` configured to use a remote exporter with the `ExportMessage` instruction, which will be delivered to a sibling parachain using `SiblingBridgeHubLocation`.
#[derive_impl(pallet_xcm_bridge_hub_router::config_preludes::TestDefaultConfig)]
impl pallet_xcm_bridge_hub_router::Config<()> for TestRuntime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();

type DestinationVersion =
LatestOrNoneForLocationVersionChecker<Equals<UnknownXcmVersionForRoutableLocation>>;

Expand Down
68 changes: 66 additions & 2 deletions bridges/modules/xcm-bridge-hub/src/exporter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ mod tests {

use bp_runtime::RangeInclusiveExt;
use bp_xcm_bridge_hub::{Bridge, BridgeLocations, BridgeState};
use bp_xcm_bridge_hub_router::ResolveBridgeId;
use frame_support::{
assert_ok,
traits::{Contains, EnsureOrigin},
Expand Down Expand Up @@ -632,7 +633,16 @@ mod tests {
let origin = OpenBridgeOrigin::sibling_parachain_origin();
let origin_as_location =
OpenBridgeOriginOf::<TestRuntime, ()>::try_origin(origin.clone()).unwrap();
let (_, expected_lane_id) = open_lane(origin);
let (bridge, expected_lane_id) = open_lane(origin);

// we need to set `UniversalLocation` for `sibling_parachain_origin` for `XcmOverBridgeWrappedWithExportMessageRouterInstance`.
ExportMessageOriginUniversalLocation::set(Some(SiblingUniversalLocation::get()));

// check compatible bridge_id
assert_eq!(
bridge.bridge_id(),
&<TestRuntime as pallet_xcm_bridge_hub_router::Config<XcmOverBridgeWrappedWithExportMessageRouterInstance>>::BridgeIdResolver::resolve_for_dest(&dest).unwrap()
);

// check before - no messages
assert_eq!(
Expand Down Expand Up @@ -683,7 +693,13 @@ mod tests {

// open bridge as a root on the local chain, which should be converted as
// `Location::here()`
let (_, expected_lane_id) = open_lane(RuntimeOrigin::root());
let (bridge, expected_lane_id) = open_lane(RuntimeOrigin::root());

// check compatible bridge_id
assert_eq!(
bridge.bridge_id(),
&<TestRuntime as pallet_xcm_bridge_hub_router::Config<XcmOverBridgeByExportXcmRouterInstance>>::BridgeIdResolver::resolve_for_dest(&dest).unwrap()
);

// check before - no messages
assert_eq!(
Expand Down Expand Up @@ -804,4 +820,52 @@ mod tests {
assert_eq!(None, dest_wrapper);
});
}

#[test]
fn congestion_with_pallet_xcm_bridge_hub_router_works() {
run_test(|| {
// valid routable destination
let dest = Location::new(2, BridgedUniversalDestination::get());

fn router_bridge_state<T: pallet_xcm_bridge_hub_router::Config<I>, I: 'static>(dest: &Location) -> Option<bp_xcm_bridge_hub_router::BridgeState> {
let bridge_id = <T::BridgeIdResolver as ResolveBridgeId>::resolve_for_dest(dest).unwrap();
pallet_xcm_bridge_hub_router::Bridges::<T, I>::get(&bridge_id)
}

// open two bridges
let origin = OpenBridgeOrigin::sibling_parachain_origin();
let origin_as_location = OpenBridgeOriginOf::<TestRuntime, ()>::try_origin(origin.clone()).unwrap();
let (bridge_1, expected_lane_id_1) = open_lane(origin);
let (bridge_2, expected_lane_id_2) = open_lane(RuntimeOrigin::root());
assert_ne!(expected_lane_id_1, expected_lane_id_2);
assert_ne!(bridge_1.bridge_id(), bridge_2.bridge_id());

// we need to set `UniversalLocation` for `sibling_parachain_origin` for `XcmOverBridgeWrappedWithExportMessageRouterInstance`.
ExportMessageOriginUniversalLocation::set(Some(SiblingUniversalLocation::get()));

// check before
assert_eq!(XcmOverBridge::bridge(bridge_1.bridge_id()).unwrap().state, BridgeState::Opened);
assert_eq!(XcmOverBridge::bridge(bridge_2.bridge_id()).unwrap().state, BridgeState::Opened);
// both routers are uncongested
assert!(!router_bridge_state::<TestRuntime, XcmOverBridgeWrappedWithExportMessageRouterInstance>(&dest).map(|bs| bs.is_congested).unwrap_or(false));
assert!(!router_bridge_state::<TestRuntime, XcmOverBridgeByExportXcmRouterInstance>(&dest).map(|bs| bs.is_congested).unwrap_or(false));

// make bridges congested with sending too much messages
for _ in 1..(OUTBOUND_LANE_CONGESTED_THRESHOLD + 2) {
// send `ExportMessage(message)` by `pallet_xcm_bridge_hub_router`.
ExecuteXcmOverSendXcm::set_origin_for_execute(origin_as_location.clone());
assert_ok!(send_xcm::<XcmOverBridgeWrappedWithExportMessageRouter>(dest.clone(), Xcm::<()>::default()));

// call direct `ExportXcm` by `pallet_xcm_bridge_hub_router`.
assert_ok!(send_xcm::<XcmOverBridgeByExportXcmRouter>(dest.clone(), Xcm::<()>::default()));
}

// checks after
assert_eq!(XcmOverBridge::bridge(bridge_1.bridge_id()).unwrap().state, BridgeState::Suspended);
assert_eq!(XcmOverBridge::bridge(bridge_2.bridge_id()).unwrap().state, BridgeState::Suspended);
// both routers are congested
assert!(router_bridge_state::<TestRuntime, XcmOverBridgeWrappedWithExportMessageRouterInstance>(&dest).unwrap().is_congested);
assert!(router_bridge_state::<TestRuntime, XcmOverBridgeByExportXcmRouterInstance>(&dest).unwrap().is_congested);
})
}
}
53 changes: 28 additions & 25 deletions bridges/modules/xcm-bridge-hub/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ use polkadot_parachain_primitives::primitives::Sibling;
use sp_core::H256;
use sp_runtime::{
testing::Header as SubstrateHeader,
traits::{BlakeTwo256, ConstU128, ConstU32, IdentityLookup},
traits::{BlakeTwo256, ConstU32, IdentityLookup},
AccountId32, BuildStorage, StateVersion,
};
use sp_std::cell::RefCell;
Expand Down Expand Up @@ -172,7 +172,6 @@ parameter_types! {

// configuration for pallet_xcm_bridge_hub_router
pub BridgeHubLocation: Location = Here.into();
pub BridgeFeeAsset: AssetId = Location::here().into();
pub BridgeTable: Vec<NetworkExportTableItem>
= vec![
NetworkExportTableItem::new(
Expand Down Expand Up @@ -220,12 +219,9 @@ impl pallet_xcm_bridge_hub::Config for TestRuntime {

/// A router instance simulates a scenario where the router is deployed on a different chain than
/// the `MessageExporter`. This means that the router sends an `ExportMessage`.
pub type XcmOverBridgeWrappedWithExportMessageRouterInstance = ();
#[derive_impl(pallet_xcm_bridge_hub_router::config_preludes::TestDefaultConfig)]
impl pallet_xcm_bridge_hub_router::Config<()> for TestRuntime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();

type DestinationVersion = AlwaysLatest;

// We use `SovereignPaidRemoteExporter` here to test and ensure that the `ExportMessage`
// produced by `pallet_xcm_bridge_hub_router` is compatible with the `ExportXcm` implementation
// of `pallet_xcm_bridge_hub`.
Expand All @@ -241,36 +237,27 @@ impl pallet_xcm_bridge_hub_router::Config<()> for TestRuntime {
// calls the `ExportXcm` implementation of `pallet_xcm_bridge_hub` as the
// `MessageExporter`.
ExecuteXcmOverSendXcm,
UniversalLocation,
ExportMessageOriginUniversalLocation,
>;

type BridgeIdResolver = BridgeIdResolver<UniversalLocation>;

type ByteFee = ConstU128<0>;
type FeeAsset = BridgeFeeAsset;
type BridgeIdResolver = EnsureIsRemoteBridgeIdResolver<ExportMessageOriginUniversalLocation>;
}

/// A router instance simulates a scenario where the router is deployed on the same chain as the
/// `MessageExporter`. This means that the router triggers `ExportXcm` trait directly.
impl pallet_xcm_bridge_hub_router::Config<pallet_xcm_bridge_hub_router::Instance2> for TestRuntime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();

type DestinationVersion = AlwaysLatest;

pub type XcmOverBridgeByExportXcmRouterInstance = pallet_xcm_bridge_hub_router::Instance2;
#[derive_impl(pallet_xcm_bridge_hub_router::config_preludes::TestDefaultConfig)]
impl pallet_xcm_bridge_hub_router::Config<XcmOverBridgeByExportXcmRouterInstance> for TestRuntime {
// We use `UnpaidLocalExporter` here to test and ensure that `pallet_xcm_bridge_hub_router` can
// trigger directly `pallet_xcm_bridge_hub` as exporter.
type ToBridgeHubSender = UnpaidLocalExporter<XcmOverBridge, UniversalLocation>;

type BridgeIdResolver = BridgeIdResolver<UniversalLocation>;

type ByteFee = ConstU128<0>;
type FeeAsset = BridgeFeeAsset;
type BridgeIdResolver = EnsureIsRemoteBridgeIdResolver<UniversalLocation>;
}

/// Implementation of `ResolveBridgeId` returning `BridgeId`.
pub struct BridgeIdResolver<UniversalLocation>(PhantomData<UniversalLocation>);
impl<UniversalLocation: Get<InteriorLocation>> ResolveBridgeId for BridgeIdResolver<UniversalLocation> {
/// Implementation of `ResolveBridgeId` returning `BridgeId` based on the configured `UniversalLocation`.
pub struct EnsureIsRemoteBridgeIdResolver<UniversalLocation>(PhantomData<UniversalLocation>);
impl<UniversalLocation: Get<InteriorLocation>> ResolveBridgeId for EnsureIsRemoteBridgeIdResolver<UniversalLocation> {
type BridgeId = BridgeId;

fn resolve_for_dest(dest: &Location) -> Option<Self::BridgeId> {
Expand All @@ -284,6 +271,22 @@ impl<UniversalLocation: Get<InteriorLocation>> ResolveBridgeId for BridgeIdResol
}
}

/// A dynamic way to set different universal location for the origin which sends `ExportMessage`.
pub struct ExportMessageOriginUniversalLocation;
impl ExportMessageOriginUniversalLocation {
pub(crate) fn set(universal_location: Option<InteriorLocation>) {
EXPORT_MESSAGE_ORIGIN_UNIVERSAL_LOCATION.with(|o| *o.borrow_mut() = universal_location);
}
}
impl Get<InteriorLocation> for ExportMessageOriginUniversalLocation {
fn get() -> InteriorLocation {
EXPORT_MESSAGE_ORIGIN_UNIVERSAL_LOCATION.with(|o| o.borrow().clone().expect("`EXPORT_MESSAGE_ORIGIN_UNIVERSAL_LOCATION` is not set!"))
}
}
thread_local! {
pub static EXPORT_MESSAGE_ORIGIN_UNIVERSAL_LOCATION: RefCell<Option<InteriorLocation>> = RefCell::new(None);
}

pub struct XcmConfig;
impl xcm_executor::Config for XcmConfig {
type RuntimeCall = RuntimeCall;
Expand Down

0 comments on commit d821c08

Please sign in to comment.