diff --git a/bin/millau/runtime/src/lib.rs b/bin/millau/runtime/src/lib.rs index 56c2e7fefc..9fe25bf15f 100644 --- a/bin/millau/runtime/src/lib.rs +++ b/bin/millau/runtime/src/lib.rs @@ -55,6 +55,7 @@ use sp_std::prelude::*; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; +use xcm_builder::NetworkExportTable; // to be able to use Millau runtime in `bridge-runtime-common` tests pub use bridge_runtime_common; @@ -546,17 +547,20 @@ impl pallet_utility::Config for Runtime { // this config is totally incorrect - the pallet is not actually used at this runtime. We need // it only to be able to run benchmarks and make required traits (and default weights for tests). +parameter_types! { + pub BridgeTable: Vec<(xcm::prelude::NetworkId, xcm::prelude::MultiLocation, Option)> + = vec![(xcm_config::RialtoNetwork::get(), xcm_config::TokenLocation::get(), Some((xcm_config::TokenAssetId::get(), 1_000_000_000_u128).into()))]; +} impl pallet_xcm_bridge_hub_router::Config for Runtime { type WeightInfo = (); type UniversalLocation = xcm_config::UniversalLocation; - type SiblingBridgeHubLocation = xcm_config::TokenLocation; type BridgedNetworkId = xcm_config::RialtoNetwork; + type Bridges = NetworkExportTable; type ToBridgeHubSender = xcm_config::XcmRouter; type WithBridgeHubChannel = xcm_config::EmulatedSiblingXcmpChannel; - type BaseFee = ConstU128<1_000_000_000>; type ByteFee = ConstU128<1_000>; type FeeAsset = xcm_config::TokenAssetId; } diff --git a/modules/xcm-bridge-hub-router/src/lib.rs b/modules/xcm-bridge-hub-router/src/lib.rs index eae89c6be9..8fee167296 100644 --- a/modules/xcm-bridge-hub-router/src/lib.rs +++ b/modules/xcm-bridge-hub-router/src/lib.rs @@ -73,10 +73,14 @@ pub mod pallet { /// Universal location of this runtime. type UniversalLocation: Get; - /// Relative location of the sibling bridge hub. - type SiblingBridgeHubLocation: Get; - /// The bridged network that this config is for. - type BridgedNetworkId: Get; + /// The bridged network that this config is for if specified. + /// Also used for filtering `Bridges` by `BridgedNetworkId`. + /// If not specified, allows all networks pass through. + type BridgedNetworkId: Get>; + /// Configuration for supported **bridged networks/locations** with **bridge location** and + /// **possible fee**. Allows to externalize better control over allowed **bridged + /// networks/locations**. + type Bridges: ExporterFor; /// Actual message sender (`HRMP` or `DMP`) to the sibling bridge hub location. type ToBridgeHubSender: SendXcm; @@ -84,8 +88,6 @@ pub mod pallet { /// by the `Self::ToBridgeHubSender`. type WithBridgeHubChannel: LocalXcmChannel; - /// Base bridge fee that is paid for every outbound message. - type BaseFee: Get; /// Additional fee that is paid for every byte of the outbound message. type ByteFee: Get; /// Asset that is used to paid bridge fee. @@ -177,32 +179,75 @@ type ViaBridgeHubExporter = SovereignPaidRemoteExporter< impl, I: 'static> ExporterFor for Pallet { fn exporter_for( network: &NetworkId, - _remote_location: &InteriorMultiLocation, + remote_location: &InteriorMultiLocation, message: &Xcm<()>, ) -> Option<(MultiLocation, Option)> { - // ensure that the message is sent to the expected bridged network - if *network != T::BridgedNetworkId::get() { - return None + // ensure that the message is sent to the expected bridged network (if specified). + if let Some(bridged_network) = T::BridgedNetworkId::get() { + if *network != bridged_network { + log::trace!( + target: LOG_TARGET, + "Router with bridged_network_id {:?} does not support bridging to network {:?}!", + bridged_network, + network, + ); + return None + } } + // ensure that the message is sent to the expected bridged network and location. + let Some((bridge_hub_location, maybe_payment)) = T::Bridges::exporter_for(network, remote_location, message) else { + log::trace!( + target: LOG_TARGET, + "Router with bridged_network_id {:?} does not support bridging to network {:?} and remote_location {:?}!", + T::BridgedNetworkId::get(), + network, + remote_location, + ); + return None + }; + + // take `base_fee` from `T::Brides`, but it has to be the same `T::FeeAsset` + let base_fee = match maybe_payment { + Some(payment) => match payment { + MultiAsset { fun: Fungible(amount), id } if id.eq(&T::FeeAsset::get()) => amount, + invalid_asset @ _ => { + log::error!( + target: LOG_TARGET, + "Router with bridged_network_id {:?} is configured for `T::FeeAsset` {:?} which is not compatible with {:?} for bridge_hub_location: {:?} for bridging to {:?}/{:?}!", + T::BridgedNetworkId::get(), + T::FeeAsset::get(), + invalid_asset, + bridge_hub_location, + network, + remote_location, + ); + return None + }, + }, + None => 0, + }; + // compute fee amount. Keep in mind that this is only the bridge fee. The fee for sending // message from this chain to child/sibling bridge hub is determined by the // `Config::ToBridgeHubSender` let message_size = message.encoded_size(); let message_fee = (message_size as u128).saturating_mul(T::ByteFee::get()); - let fee_sum = T::BaseFee::get().saturating_add(message_fee); + let fee_sum = base_fee.saturating_add(message_fee); let fee_factor = Self::delivery_fee_factor(); let fee = fee_factor.saturating_mul_int(fee_sum); + let fee = if fee > 0 { Some((T::FeeAsset::get(), fee).into()) } else { None }; + log::info!( target: LOG_TARGET, - "Going to send message ({} bytes) over bridge. Computed bridge fee {} using fee factor {}", - fee, - fee_factor, + "Going to send message ({} bytes) over bridge. Computed bridge fee {:?} using fee factor {}", message_size, + fee, + fee_factor ); - Some((T::SiblingBridgeHubLocation::get(), Some((T::FeeAsset::get(), fee).into()))) + Some((bridge_hub_location, fee)) } } diff --git a/modules/xcm-bridge-hub-router/src/mock.rs b/modules/xcm-bridge-hub-router/src/mock.rs index b585ee76ba..9be1dd1f52 100644 --- a/modules/xcm-bridge-hub-router/src/mock.rs +++ b/modules/xcm-bridge-hub-router/src/mock.rs @@ -26,6 +26,7 @@ use sp_runtime::{ BuildStorage, }; use xcm::prelude::*; +use xcm_builder::NetworkExportTable; pub type AccountId = u64; type Block = frame_system::mocking::MockBlock; @@ -51,6 +52,8 @@ parameter_types! { pub UniversalLocation: InteriorMultiLocation = X2(GlobalConsensus(ThisNetworkId::get()), Parachain(1000)); pub SiblingBridgeHubLocation: MultiLocation = ParentThen(X1(Parachain(1002))).into(); pub BridgeFeeAsset: AssetId = MultiLocation::parent().into(); + pub BridgeTable: Vec<(NetworkId, MultiLocation, Option)> + = vec![(BridgedNetworkId::get(), SiblingBridgeHubLocation::get(), Some((BridgeFeeAsset::get(), BASE_FEE).into()))]; } impl frame_system::Config for TestRuntime { @@ -83,13 +86,12 @@ impl pallet_xcm_bridge_hub_router::Config<()> for TestRuntime { type WeightInfo = (); type UniversalLocation = UniversalLocation; - type SiblingBridgeHubLocation = SiblingBridgeHubLocation; type BridgedNetworkId = BridgedNetworkId; + type Bridges = NetworkExportTable; type ToBridgeHubSender = TestToBridgeHubSender; type WithBridgeHubChannel = TestWithBridgeHubChannel; - type BaseFee = ConstU128; type ByteFee = ConstU128; type FeeAsset = BridgeFeeAsset; }