diff --git a/Cargo.lock b/Cargo.lock index 64c68d81d42b..eae6db6d9b26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -965,6 +965,7 @@ dependencies = [ "bp-asset-hub-westend", "bp-bridge-hub-rococo", "bp-bridge-hub-westend", + "bp-xcm-bridge", "cumulus-pallet-aura-ext 0.7.0", "cumulus-pallet-parachain-system 0.7.0", "cumulus-pallet-session-benchmarking 9.0.0", @@ -1007,7 +1008,7 @@ dependencies = [ "pallet-utility 28.0.0", "pallet-xcm 7.0.0", "pallet-xcm-benchmarks 7.0.0", - "pallet-xcm-bridge-hub-router 0.5.0", + "pallet-xcm-bridge-router", "parachains-common 7.0.0", "parachains-runtimes-test-utils 7.0.0", "parity-scale-codec", @@ -1102,6 +1103,7 @@ dependencies = [ "bp-asset-hub-westend", "bp-bridge-hub-rococo", "bp-bridge-hub-westend", + "bp-xcm-bridge", "cumulus-pallet-aura-ext 0.7.0", "cumulus-pallet-parachain-system 0.7.0", "cumulus-pallet-session-benchmarking 9.0.0", @@ -1147,7 +1149,7 @@ dependencies = [ "pallet-utility 28.0.0", "pallet-xcm 7.0.0", "pallet-xcm-benchmarks 7.0.0", - "pallet-xcm-bridge-hub-router 0.5.0", + "pallet-xcm-bridge-router", "parachains-common 7.0.0", "parachains-runtimes-test-utils 7.0.0", "parity-scale-codec", @@ -1198,7 +1200,7 @@ dependencies = [ "pallet-session 28.0.0", "pallet-timestamp 27.0.0", "pallet-xcm 7.0.0", - "pallet-xcm-bridge-hub-router 0.5.0", + "pallet-xcm-bridge-router", "parachains-common 7.0.0", "parachains-runtimes-test-utils 7.0.0", "parity-scale-codec", @@ -2071,7 +2073,7 @@ dependencies = [ name = "bp-asset-hub-rococo" version = "0.4.0" dependencies = [ - "bp-xcm-bridge-hub-router 0.6.0", + "bp-xcm-bridge-router", "frame-support 28.0.0", "parity-scale-codec", "scale-info", @@ -2083,7 +2085,7 @@ dependencies = [ name = "bp-asset-hub-westend" version = "0.3.0" dependencies = [ - "bp-xcm-bridge-hub-router 0.6.0", + "bp-xcm-bridge-router", "frame-support 28.0.0", "parity-scale-codec", "scale-info", @@ -2155,7 +2157,7 @@ dependencies = [ "bp-bridge-hub-cumulus", "bp-messages 0.7.0", "bp-runtime 0.7.0", - "bp-xcm-bridge-hub 0.2.0", + "bp-xcm-bridge", "frame-support 28.0.0", "parity-scale-codec", "sp-api 26.0.0", @@ -2170,7 +2172,7 @@ dependencies = [ "bp-bridge-hub-cumulus", "bp-messages 0.7.0", "bp-runtime 0.7.0", - "bp-xcm-bridge-hub 0.2.0", + "bp-xcm-bridge", "frame-support 28.0.0", "parity-scale-codec", "sp-api 26.0.0", @@ -2523,6 +2525,23 @@ dependencies = [ "sp-std 14.0.0", ] +[[package]] +name = "bp-xcm-bridge" +version = "0.2.0" +dependencies = [ + "bp-messages 0.7.0", + "bp-runtime 0.7.0", + "frame-support 28.0.0", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "serde", + "sp-core 28.0.0", + "sp-io 30.0.0", + "sp-std 14.0.0", + "staging-xcm 7.0.0", +] + [[package]] name = "bp-xcm-bridge-hub" version = "0.2.0" @@ -2581,6 +2600,17 @@ dependencies = [ "staging-xcm 14.2.0", ] +[[package]] +name = "bp-xcm-bridge-router" +version = "0.1.0" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-core 28.0.0", + "sp-runtime 31.0.1", + "staging-xcm 7.0.0", +] + [[package]] name = "bridge-hub-common" version = "0.1.0" @@ -2645,7 +2675,7 @@ dependencies = [ "pallet-bridge-messages 0.7.0", "pallet-message-queue 31.0.0", "pallet-xcm 7.0.0", - "pallet-xcm-bridge-hub 0.2.0", + "pallet-xcm-bridge", "parachains-common 7.0.0", "parity-scale-codec", "rococo-system-emulated-network", @@ -2681,7 +2711,7 @@ dependencies = [ "bp-rococo", "bp-runtime 0.7.0", "bp-westend", - "bp-xcm-bridge-hub-router 0.6.0", + "bp-xcm-bridge-router", "bridge-hub-common 0.1.0", "bridge-hub-test-utils 0.7.0", "bridge-runtime-common 0.7.0", @@ -2721,7 +2751,7 @@ dependencies = [ "pallet-utility 28.0.0", "pallet-xcm 7.0.0", "pallet-xcm-benchmarks 7.0.0", - "pallet-xcm-bridge-hub 0.2.0", + "pallet-xcm-bridge", "parachains-common 7.0.0", "parachains-runtimes-test-utils 7.0.0", "parity-scale-codec", @@ -2778,6 +2808,7 @@ dependencies = [ "bp-relayers 0.7.0", "bp-runtime 0.7.0", "bp-test-utils 0.7.0", + "bp-xcm-bridge", "bp-xcm-bridge-hub 0.2.0", "cumulus-pallet-parachain-system 0.7.0", "cumulus-pallet-xcmp-queue 0.7.0", @@ -2793,7 +2824,7 @@ dependencies = [ "pallet-timestamp 27.0.0", "pallet-utility 28.0.0", "pallet-xcm 7.0.0", - "pallet-xcm-bridge-hub 0.2.0", + "pallet-xcm-bridge", "parachains-common 7.0.0", "parachains-runtimes-test-utils 7.0.0", "parity-scale-codec", @@ -2884,7 +2915,7 @@ dependencies = [ "pallet-bridge-messages 0.7.0", "pallet-message-queue 31.0.0", "pallet-xcm 7.0.0", - "pallet-xcm-bridge-hub 0.2.0", + "pallet-xcm-bridge", "parachains-common 7.0.0", "parity-scale-codec", "rococo-westend-system-emulated-network", @@ -2919,7 +2950,7 @@ dependencies = [ "bp-rococo", "bp-runtime 0.7.0", "bp-westend", - "bp-xcm-bridge-hub-router 0.6.0", + "bp-xcm-bridge-router", "bridge-hub-common 0.1.0", "bridge-hub-test-utils 0.7.0", "bridge-runtime-common 0.7.0", @@ -2959,7 +2990,7 @@ dependencies = [ "pallet-utility 28.0.0", "pallet-xcm 7.0.0", "pallet-xcm-benchmarks 7.0.0", - "pallet-xcm-bridge-hub 0.2.0", + "pallet-xcm-bridge", "parachains-common 7.0.0", "parachains-runtimes-test-utils 7.0.0", "parity-scale-codec", @@ -5158,7 +5189,7 @@ name = "cumulus-pallet-xcmp-queue" version = "0.7.0" dependencies = [ "bounded-collections", - "bp-xcm-bridge-hub-router 0.6.0", + "bp-xcm-bridge", "cumulus-pallet-parachain-system 0.7.0", "cumulus-primitives-core 0.7.0", "frame-benchmarking 28.0.0", @@ -6412,7 +6443,7 @@ version = "3.0.0" dependencies = [ "asset-test-utils 7.0.0", "bp-messages 0.7.0", - "bp-xcm-bridge-hub 0.2.0", + "bp-xcm-bridge", "cumulus-pallet-parachain-system 0.7.0", "cumulus-pallet-xcmp-queue 0.7.0", "cumulus-primitives-core 0.7.0", @@ -6422,6 +6453,7 @@ dependencies = [ "pallet-bridge-messages 0.7.0", "pallet-message-queue 31.0.0", "pallet-xcm 7.0.0", + "pallet-xcm-bridge", "pallet-xcm-bridge-hub 0.2.0", "parachains-common 7.0.0", "parity-scale-codec", @@ -16137,6 +16169,34 @@ dependencies = [ "staging-xcm-executor 17.0.0", ] +[[package]] +name = "pallet-xcm-bridge" +version = "0.1.0" +dependencies = [ + "bp-header-chain 0.7.0", + "bp-messages 0.7.0", + "bp-runtime 0.7.0", + "bp-xcm-bridge", + "bp-xcm-bridge-router", + "frame-benchmarking 28.0.0", + "frame-support 28.0.0", + "frame-system 28.0.0", + "log", + "pallet-balances 28.0.0", + "pallet-bridge-messages 0.7.0", + "pallet-xcm-bridge-router", + "parity-scale-codec", + "polkadot-parachain-primitives 6.0.0", + "scale-info", + "sp-core 28.0.0", + "sp-io 30.0.0", + "sp-runtime 31.0.1", + "sp-std 14.0.0", + "staging-xcm 7.0.0", + "staging-xcm-builder 7.0.0", + "staging-xcm-executor 7.0.0", +] + [[package]] name = "pallet-xcm-bridge-hub" version = "0.2.0" @@ -16226,6 +16286,26 @@ dependencies = [ "staging-xcm-builder 17.0.1", ] +[[package]] +name = "pallet-xcm-bridge-router" +version = "0.1.0" +dependencies = [ + "bp-xcm-bridge", + "bp-xcm-bridge-router", + "frame-benchmarking 28.0.0", + "frame-support 28.0.0", + "frame-system 28.0.0", + "log", + "parity-scale-codec", + "scale-info", + "sp-core 28.0.0", + "sp-io 30.0.0", + "sp-runtime 31.0.1", + "sp-std 14.0.0", + "staging-xcm 7.0.0", + "staging-xcm-builder 7.0.0", +] + [[package]] name = "parachain-template" version = "0.0.0" @@ -18641,8 +18721,10 @@ dependencies = [ "bp-relayers 0.7.0", "bp-runtime 0.7.0", "bp-test-utils 0.7.0", + "bp-xcm-bridge", "bp-xcm-bridge-hub 0.2.0", "bp-xcm-bridge-hub-router 0.6.0", + "bp-xcm-bridge-router", "bridge-hub-common 0.1.0", "bridge-hub-test-utils 0.7.0", "bridge-runtime-common 0.7.0", @@ -18808,8 +18890,10 @@ dependencies = [ "pallet-whitelist 27.0.0", "pallet-xcm 7.0.0", "pallet-xcm-benchmarks 7.0.0", + "pallet-xcm-bridge", "pallet-xcm-bridge-hub 0.2.0", "pallet-xcm-bridge-hub-router 0.5.0", + "pallet-xcm-bridge-router", "parachains-common 7.0.0", "parachains-runtimes-test-utils 7.0.0", "polkadot-approval-distribution", diff --git a/Cargo.toml b/Cargo.toml index 7a906e7c0d64..fb16d0802858 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,8 +27,10 @@ members = [ "bridges/modules/messages", "bridges/modules/parachains", "bridges/modules/relayers", + "bridges/modules/xcm-bridge", "bridges/modules/xcm-bridge-hub", "bridges/modules/xcm-bridge-hub-router", + "bridges/modules/xcm-bridge-router", "bridges/primitives/beefy", "bridges/primitives/header-chain", "bridges/primitives/messages", @@ -37,8 +39,10 @@ members = [ "bridges/primitives/relayers", "bridges/primitives/runtime", "bridges/primitives/test-utils", + "bridges/primitives/xcm-bridge", "bridges/primitives/xcm-bridge-hub", "bridges/primitives/xcm-bridge-hub-router", + "bridges/primitives/xcm-bridge-router", "bridges/relays/client-substrate", "bridges/relays/equivocation", "bridges/relays/finality", @@ -661,8 +665,10 @@ bp-rococo = { path = "bridges/chains/chain-rococo", default-features = false } bp-runtime = { path = "bridges/primitives/runtime", default-features = false } bp-test-utils = { path = "bridges/primitives/test-utils", default-features = false } bp-westend = { path = "bridges/chains/chain-westend", default-features = false } +bp-xcm-bridge = { path = "bridges/primitives/xcm-bridge", default-features = false } bp-xcm-bridge-hub = { path = "bridges/primitives/xcm-bridge-hub", default-features = false } bp-xcm-bridge-hub-router = { path = "bridges/primitives/xcm-bridge-hub-router", default-features = false } +bp-xcm-bridge-router = { path = "bridges/primitives/xcm-bridge-router", default-features = false } bridge-hub-common = { path = "cumulus/parachains/runtimes/bridge-hubs/common", default-features = false } bridge-hub-rococo-emulated-chain = { path = "cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo" } bridge-hub-rococo-runtime = { path = "cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo", default-features = false } @@ -1015,8 +1021,10 @@ pallet-vesting = { path = "substrate/frame/vesting", default-features = false } pallet-whitelist = { path = "substrate/frame/whitelist", default-features = false } pallet-xcm = { path = "polkadot/xcm/pallet-xcm", default-features = false } pallet-xcm-benchmarks = { path = "polkadot/xcm/pallet-xcm-benchmarks", default-features = false } +pallet-xcm-bridge = { path = "bridges/modules/xcm-bridge", default-features = false } pallet-xcm-bridge-hub = { path = "bridges/modules/xcm-bridge-hub", default-features = false } pallet-xcm-bridge-hub-router = { path = "bridges/modules/xcm-bridge-hub-router", default-features = false } +pallet-xcm-bridge-router = { path = "bridges/modules/xcm-bridge-router", default-features = false } parachain-info = { path = "cumulus/parachains/pallets/parachain-info", default-features = false, package = "staging-parachain-info" } parachain-template-runtime = { path = "templates/parachain/runtime" } parachains-common = { path = "cumulus/parachains/common", default-features = false } diff --git a/bridges/chains/chain-asset-hub-rococo/Cargo.toml b/bridges/chains/chain-asset-hub-rococo/Cargo.toml index 4eb93ab52bc9..994621e01891 100644 --- a/bridges/chains/chain-asset-hub-rococo/Cargo.toml +++ b/bridges/chains/chain-asset-hub-rococo/Cargo.toml @@ -22,7 +22,7 @@ frame-support = { workspace = true } sp-core = { workspace = true } # Bridge Dependencies -bp-xcm-bridge-hub-router = { workspace = true } +bp-xcm-bridge-router = { workspace = true } # Polkadot dependencies xcm = { workspace = true } @@ -30,7 +30,7 @@ xcm = { workspace = true } [features] default = ["std"] std = [ - "bp-xcm-bridge-hub-router/std", + "bp-xcm-bridge-router/std", "codec/std", "frame-support/std", "scale-info/std", diff --git a/bridges/chains/chain-asset-hub-rococo/src/lib.rs b/bridges/chains/chain-asset-hub-rococo/src/lib.rs index 4ff7b391acd0..26ce2d60d7a5 100644 --- a/bridges/chains/chain-asset-hub-rococo/src/lib.rs +++ b/bridges/chains/chain-asset-hub-rococo/src/lib.rs @@ -23,7 +23,7 @@ extern crate alloc; use codec::{Decode, Encode}; use scale_info::TypeInfo; -pub use bp_xcm_bridge_hub_router::XcmBridgeHubRouterCall; +pub use bp_xcm_bridge_router::XcmBridgeHubRouterCall; use xcm::latest::prelude::*; /// `AssetHubRococo` Runtime `Call` enum. @@ -39,7 +39,7 @@ use xcm::latest::prelude::*; pub enum Call { /// `ToWestendXcmRouter` bridge pallet. #[codec(index = 45)] - ToWestendXcmRouter(XcmBridgeHubRouterCall), + ToWestendXcmRouter(XcmBridgeHubRouterCall), } frame_support::parameter_types! { diff --git a/bridges/chains/chain-asset-hub-westend/Cargo.toml b/bridges/chains/chain-asset-hub-westend/Cargo.toml index 22071399f4d1..2fd2b0098335 100644 --- a/bridges/chains/chain-asset-hub-westend/Cargo.toml +++ b/bridges/chains/chain-asset-hub-westend/Cargo.toml @@ -22,7 +22,7 @@ frame-support = { workspace = true } sp-core = { workspace = true } # Bridge Dependencies -bp-xcm-bridge-hub-router = { workspace = true } +bp-xcm-bridge-router = { workspace = true } # Polkadot dependencies xcm = { workspace = true } @@ -30,7 +30,7 @@ xcm = { workspace = true } [features] default = ["std"] std = [ - "bp-xcm-bridge-hub-router/std", + "bp-xcm-bridge-router/std", "codec/std", "frame-support/std", "scale-info/std", diff --git a/bridges/chains/chain-asset-hub-westend/src/lib.rs b/bridges/chains/chain-asset-hub-westend/src/lib.rs index 9d245e08f7cc..468fedb35570 100644 --- a/bridges/chains/chain-asset-hub-westend/src/lib.rs +++ b/bridges/chains/chain-asset-hub-westend/src/lib.rs @@ -23,7 +23,7 @@ extern crate alloc; use codec::{Decode, Encode}; use scale_info::TypeInfo; -pub use bp_xcm_bridge_hub_router::XcmBridgeHubRouterCall; +pub use bp_xcm_bridge_router::XcmBridgeHubRouterCall; use xcm::latest::prelude::*; /// `AssetHubWestend` Runtime `Call` enum. @@ -39,7 +39,7 @@ use xcm::latest::prelude::*; pub enum Call { /// `ToRococoXcmRouter` bridge pallet. #[codec(index = 34)] - ToRococoXcmRouter(XcmBridgeHubRouterCall), + ToRococoXcmRouter(XcmBridgeHubRouterCall), } frame_support::parameter_types! { diff --git a/bridges/chains/chain-bridge-hub-rococo/Cargo.toml b/bridges/chains/chain-bridge-hub-rococo/Cargo.toml index 08a704add2b7..f967d833bc8a 100644 --- a/bridges/chains/chain-bridge-hub-rococo/Cargo.toml +++ b/bridges/chains/chain-bridge-hub-rococo/Cargo.toml @@ -20,7 +20,7 @@ codec = { features = ["derive"], workspace = true } bp-bridge-hub-cumulus = { workspace = true } bp-messages = { workspace = true } bp-runtime = { workspace = true } -bp-xcm-bridge-hub = { workspace = true } +bp-xcm-bridge = { workspace = true } # Substrate Based Dependencies frame-support = { workspace = true } @@ -34,7 +34,7 @@ std = [ "bp-bridge-hub-cumulus/std", "bp-messages/std", "bp-runtime/std", - "bp-xcm-bridge-hub/std", + "bp-xcm-bridge/std", "codec/std", "frame-support/std", "sp-api/std", diff --git a/bridges/chains/chain-bridge-hub-rococo/src/lib.rs b/bridges/chains/chain-bridge-hub-rococo/src/lib.rs index fda6a5f0b722..689b501aa517 100644 --- a/bridges/chains/chain-bridge-hub-rococo/src/lib.rs +++ b/bridges/chains/chain-bridge-hub-rococo/src/lib.rs @@ -105,7 +105,7 @@ frame_support::parameter_types! { /// The XCM fee that is paid for executing XCM program (with `ExportMessage` instruction) at the Rococo /// BridgeHub. /// (initially was calculated by test `BridgeHubRococo::can_calculate_weight_for_paid_export_message_with_reserve_transfer` + `33%`) - pub const BridgeHubRococoBaseXcmFeeInRocs: u128 = 57_145_832; + pub const BridgeHubRococoBaseXcmFeeInRocs: u128 = 57_325_000; /// Transaction fee that is paid at the Rococo BridgeHub for delivering single inbound message. /// (initially was calculated by test `BridgeHubRococo::can_calculate_fee_for_standalone_message_delivery_transaction` + `33%`) @@ -121,5 +121,5 @@ frame_support::parameter_types! { pub enum RuntimeCall { /// Points to the `pallet_xcm_bridge_hub` pallet instance for `BridgeHubWestend`. #[codec(index = 52)] - XcmOverBridgeHubWestend(bp_xcm_bridge_hub::XcmBridgeHubCall), + XcmOverBridgeHubWestend(bp_xcm_bridge::XcmBridgeCall), } diff --git a/bridges/chains/chain-bridge-hub-westend/Cargo.toml b/bridges/chains/chain-bridge-hub-westend/Cargo.toml index 35932371d0a9..fa23128a202e 100644 --- a/bridges/chains/chain-bridge-hub-westend/Cargo.toml +++ b/bridges/chains/chain-bridge-hub-westend/Cargo.toml @@ -20,7 +20,7 @@ codec = { features = ["derive"], workspace = true } bp-bridge-hub-cumulus = { workspace = true } bp-messages = { workspace = true } bp-runtime = { workspace = true } -bp-xcm-bridge-hub = { workspace = true } +bp-xcm-bridge = { workspace = true } # Substrate Based Dependencies frame-support = { workspace = true } @@ -34,7 +34,7 @@ std = [ "bp-bridge-hub-cumulus/std", "bp-messages/std", "bp-runtime/std", - "bp-xcm-bridge-hub/std", + "bp-xcm-bridge/std", "codec/std", "frame-support/std", "sp-api/std", diff --git a/bridges/chains/chain-bridge-hub-westend/src/lib.rs b/bridges/chains/chain-bridge-hub-westend/src/lib.rs index e941b5840238..9658d098aea7 100644 --- a/bridges/chains/chain-bridge-hub-westend/src/lib.rs +++ b/bridges/chains/chain-bridge-hub-westend/src/lib.rs @@ -94,7 +94,7 @@ frame_support::parameter_types! { /// The XCM fee that is paid for executing XCM program (with `ExportMessage` instruction) at the Westend /// BridgeHub. /// (initially was calculated by test `BridgeHubWestend::can_calculate_weight_for_paid_export_message_with_reserve_transfer` + `33%`) - pub const BridgeHubWestendBaseXcmFeeInWnds: u128 = 18_191_740_000; + pub const BridgeHubWestendBaseXcmFeeInWnds: u128 = 18_248_930_000; /// Transaction fee that is paid at the Westend BridgeHub for delivering single inbound message. /// (initially was calculated by test `BridgeHubWestend::can_calculate_fee_for_standalone_message_delivery_transaction` + `33%`) @@ -110,5 +110,5 @@ frame_support::parameter_types! { pub enum RuntimeCall { /// Points to the `pallet_xcm_bridge_hub` pallet instance for `BridgeHubRococo`. #[codec(index = 45)] - XcmOverBridgeHubRococo(bp_xcm_bridge_hub::XcmBridgeHubCall), + XcmOverBridgeHubRococo(bp_xcm_bridge::XcmBridgeCall), } diff --git a/bridges/modules/messages/src/lanes_manager.rs b/bridges/modules/messages/src/lanes_manager.rs index 27cab48535d7..175540bcac32 100644 --- a/bridges/modules/messages/src/lanes_manager.rs +++ b/bridges/modules/messages/src/lanes_manager.rs @@ -154,7 +154,7 @@ impl, I: 'static> RuntimeInboundLaneStorage { // apart from the explicit closure, the lane may be unable to receive any messages. // Right now we do an additional check here, but it may be done later (e.g. by // explicitly closing the lane and reopening it from - // `pallet-xcm-bridge-hub::on-initialize`) + // `pallet-xcm-bridge::on-initialize`) // // The fact that we only check it here, means that the `MessageDispatch` may switch // to inactive state during some message dispatch in the middle of message delivery diff --git a/bridges/modules/xcm-bridge-router/Cargo.toml b/bridges/modules/xcm-bridge-router/Cargo.toml new file mode 100644 index 000000000000..2aa585c8b557 --- /dev/null +++ b/bridges/modules/xcm-bridge-router/Cargo.toml @@ -0,0 +1,67 @@ +[package] +name = "pallet-xcm-bridge-router" +description = "Bridge hub interface for sibling/parent chains with dynamic fees support." +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +repository.workspace = true + +[lints] +workspace = true + +[dependencies] +codec = { workspace = true } +log = { workspace = true } +scale-info = { features = ["bit-vec", "derive", "serde"], workspace = true } + +# Bridge dependencies +bp-xcm-bridge = { workspace = true } +bp-xcm-bridge-router = { workspace = true } + +# Substrate Dependencies +frame-benchmarking = { optional = true, workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +# Polkadot Dependencies +xcm = { workspace = true } +xcm-builder = { workspace = true } + +[dev-dependencies] +sp-io = { workspace = true, default-features = true } +sp-std = { workspace = true, default-features = true } + +[features] +default = ["std"] +std = [ + "bp-xcm-bridge-router/std", + "bp-xcm-bridge/std", + "codec/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", + "xcm-builder/std", + "xcm/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", + "xcm/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/bridges/modules/xcm-bridge-router/src/benchmarking.rs b/bridges/modules/xcm-bridge-router/src/benchmarking.rs new file mode 100644 index 000000000000..075d0fbeefd6 --- /dev/null +++ b/bridges/modules/xcm-bridge-router/src/benchmarking.rs @@ -0,0 +1,63 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! XCM bridge hub router pallet benchmarks. + +#![cfg(feature = "runtime-benchmarks")] + +use crate::{BridgeState, Bridges, Call, ResolveBridgeId, MINIMAL_DELIVERY_FEE_FACTOR}; +use frame_benchmarking::v2::*; +use frame_support::traits::EnsureOriginWithArg; +use xcm::prelude::*; + +/// Pallet we're benchmarking here. +pub struct Pallet, I: 'static = ()>(crate::Pallet); + +/// Trait that must be implemented by runtime to be able to benchmark pallet properly. +pub trait Config: crate::Config { + /// Returns destination which is valid for this router instance. + fn ensure_bridged_target_destination() -> Result; + /// Returns valid origin for `update_bridge_status` (if `T::UpdateBridgeStatusOrigin` is supported). + fn update_bridge_status_origin() -> Option; +} + +#[instance_benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn update_bridge_status() -> Result<(), BenchmarkError> { + let bridge_id = + T::BridgeIdResolver::resolve_for_dest(&T::ensure_bridged_target_destination()?) + .ok_or(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?; + let origin = T::update_bridge_status_origin() + .ok_or(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?; + let _ = T::UpdateBridgeStatusOrigin::try_origin(origin.clone(), &bridge_id) + .map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?; + let is_congested = true; + + #[extrinsic_call] + update_bridge_status(origin as T::RuntimeOrigin, bridge_id.clone(), is_congested); + + assert_eq!( + Bridges::::get(&bridge_id), + Some(BridgeState { delivery_fee_factor: MINIMAL_DELIVERY_FEE_FACTOR, is_congested }) + ); + Ok(()) + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime); +} diff --git a/bridges/modules/xcm-bridge-router/src/impls.rs b/bridges/modules/xcm-bridge-router/src/impls.rs new file mode 100644 index 000000000000..9266f7e8f3e6 --- /dev/null +++ b/bridges/modules/xcm-bridge-router/src/impls.rs @@ -0,0 +1,365 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Various implementations supporting easier configuration of the pallet. + +use crate::{BridgeIdOf, Bridges, Config, Pallet, LOG_TARGET}; +use bp_xcm_bridge_router::ResolveBridgeId; +use codec::Encode; +use frame_support::{ensure, pallet_prelude::PhantomData, traits::Get}; +use xcm::prelude::*; +use xcm_builder::{ensure_is_remote, ExporterFor}; + +/// Implementation of [`bp_xcm_bridge::LocalXcmChannelManager`] which tracks and updates +/// `is_congested` for a given `BridgeId`. This implementation is useful for managing congestion and +/// dynamic fees with the local `ExportXcm` implementation. +impl, I: 'static> bp_xcm_bridge::LocalXcmChannelManager> + for Pallet +{ + type Error = (); + + /// Suspends the given bridge. + /// + /// This function ensures that the `local_origin` matches the expected `Location::here()`. If + /// the check passes, it updates the bridge status to congested. + fn suspend_bridge( + local_origin: &Location, + bridge: BridgeIdOf, + ) -> Result<(), Self::Error> { + log::trace!( + target: LOG_TARGET, + "LocalXcmChannelManager::suspend_bridge(local_origin: {local_origin:?}, bridge: {bridge:?})", + ); + ensure!(local_origin.eq(&Location::here()), ()); + + // update status + Self::do_update_bridge_status(bridge, true); + + Ok(()) + } + + /// Resumes the given bridge. + /// + /// This function ensures that the `local_origin` matches the expected `Location::here()`. If + /// the check passes, it updates the bridge status to not congested. + fn resume_bridge(local_origin: &Location, bridge: BridgeIdOf) -> Result<(), Self::Error> { + log::trace!( + target: LOG_TARGET, + "LocalXcmChannelManager::resume_bridge(local_origin: {local_origin:?}, bridge: {bridge:?})", + ); + ensure!(local_origin.eq(&Location::here()), ()); + + // update status + Self::do_update_bridge_status(bridge, false); + + Ok(()) + } +} + +/// Adapter implementation for [`ExporterFor`] that allows exporting message size fee and/or dynamic +/// fees based on the `BridgeId` resolved by the `T::BridgeIdResolver` resolver, if and only if the +/// `E` exporter supports bridging. This adapter acts as an [`ExporterFor`], for example, for the +/// [`xcm_builder::SovereignPaidRemoteExporter`], enabling it to compute message and/or dynamic fees +/// using a fee factor. +pub struct ViaRemoteBridgeHubExporter(PhantomData<(T, I, E, BNF, BHLF)>); +impl, I: 'static, E, BridgedNetworkIdFilter, BridgeHubLocationFilter> ExporterFor + for ViaRemoteBridgeHubExporter +where + E: ExporterFor, + BridgedNetworkIdFilter: Get>, + BridgeHubLocationFilter: Get>, +{ + fn exporter_for( + network: &NetworkId, + remote_location: &InteriorLocation, + message: &Xcm<()>, + ) -> Option<(Location, Option)> { + log::trace!( + target: LOG_TARGET, + "exporter_for - network: {network:?}, remote_location: {remote_location:?}, msg: {message:?}", + ); + // ensure that the message is sent to the expected bridged network (if specified). + if let Some(bridged_network) = BridgedNetworkIdFilter::get() { + if *network != bridged_network { + log::trace!( + target: LOG_TARGET, + "Router with bridged_network_id filter({bridged_network:?}) does not support bridging to network {network:?}!", + ); + return None + } + } + + // ensure that the message is sent to the expected bridged network and location. + let (bridge_hub_location, maybe_payment) = match E::exporter_for( + network, + remote_location, + message, + ) { + Some((bridge_hub_location, maybe_payment)) => match BridgeHubLocationFilter::get() { + Some(expected_bridge_hub_location) + if expected_bridge_hub_location.eq(&bridge_hub_location) => + (bridge_hub_location, maybe_payment), + None => (bridge_hub_location, maybe_payment), + _ => { + log::trace!( + target: LOG_TARGET, + "Resolved bridge_hub_location: {:?} does not match expected one: {:?} for bridging to network {:?} and remote_location {:?}!", + bridge_hub_location, + BridgeHubLocationFilter::get(), + network, + remote_location, + ); + return None + }, + }, + _ => { + log::trace!( + target: LOG_TARGET, + "Inner `E` router does not support bridging to network {:?} and remote_location {:?}!", + network, + remote_location, + ); + return None + }, + }; + + // calculate message size fees (if configured) + let maybe_message_size_fees = + Pallet::::calculate_message_size_fee(|| message.encoded_size() as _); + + // compute actual fees - sum(actual payment, message size fees) if possible + let mut fees = match (maybe_payment, maybe_message_size_fees) { + (Some(payment), None) => Some(payment), + (None, Some(message_size_fees)) => Some(message_size_fees), + (None, None) => None, + ( + Some(Asset { id: payment_asset_id, fun: Fungible(payment_amount) }), + Some(Asset { + id: message_size_fees_asset_id, + fun: Fungible(message_size_fees_amount), + }), + ) if payment_asset_id.eq(&message_size_fees_asset_id) => { + // we can subsume two assets with the same asset_id and fungibility. + Some( + (payment_asset_id, payment_amount.saturating_add(message_size_fees_amount)) + .into(), + ) + }, + (Some(payment), Some(message_size_fees)) => { + log::error!( + target: LOG_TARGET, + "Router is configured for `T::FeeAsset` {:?} \ + but we have two different assets which cannot be calculated as one result asset: payment: {:?} and message_size_fees: {:?} for bridge_hub_location: {:?} for bridging to {:?}/{:?}!", + T::FeeAsset::get(), + payment, + message_size_fees, + bridge_hub_location, + network, + remote_location, + ); + return None + }, + }; + + // `fees` is populated with base bridge fees, now let's apply congestion/dynamic fees if required. + if let Some(bridge_id) = T::BridgeIdResolver::resolve_for(network, remote_location) { + if let Some(bridge_state) = Bridges::::get(bridge_id) { + fees = fees.map(|f| Pallet::::calculate_dynamic_fee(&bridge_state, f)); + } + } + + Some((bridge_hub_location, fees)) + } +} + +/// Adapter implementation for [`SendXcm`] that allows adding a message size fee and/or dynamic fees +/// based on the `BridgeId` resolved by the `T::BridgeIdResolver` resolver, if and only if `E` +/// supports routing. This adapter can be used, for example, as a wrapper over +/// [`xcm_builder::LocalExporter`], enabling it to compute message and/or dynamic fees using a +/// fee factor. +pub struct ViaLocalBridgeHubExporter(PhantomData<(T, I, E)>); +impl, I: 'static, E: SendXcm> SendXcm for ViaLocalBridgeHubExporter { + type Ticket = E::Ticket; + + fn validate( + destination: &mut Option, + message: &mut Option>, + ) -> SendResult { + let dest_clone = destination.clone().ok_or(SendError::MissingArgument)?; + let message_size = message.as_ref().map_or(0, |message| message.encoded_size()) as _; + + match E::validate(destination, message) { + Ok((ticket, mut fees)) => { + // calculate message size fees (if configured) + let maybe_message_size_fees = + Pallet::::calculate_message_size_fee(|| message_size); + if let Some(message_size_fees) = maybe_message_size_fees { + fees.push(message_size_fees); + } + + // Here, we have the actual result fees covering bridge fees, so now we need to + // check/apply the congestion and dynamic_fees features (if possible). + if let Some(bridge_id) = T::BridgeIdResolver::resolve_for_dest(&dest_clone) { + if let Some(bridge_state) = Bridges::::get(bridge_id) { + let mut dynamic_fees = sp_std::vec::Vec::with_capacity(fees.len()); + for fee in fees.into_inner() { + dynamic_fees + .push(Pallet::::calculate_dynamic_fee(&bridge_state, fee)); + } + fees = Assets::from(dynamic_fees); + } + } + + // return original ticket with possibly extended fees + Ok((ticket, fees)) + }, + error => error, + } + } + + fn deliver(ticket: Self::Ticket) -> Result { + E::deliver(ticket) + } +} + +/// Implementation of [`ResolveBridgeId`] returning [`bp_xcm_bridge::BridgeId`] based on the +/// configured `UniversalLocation` and remote universal location. +pub struct EnsureIsRemoteBridgeIdResolver(PhantomData); +impl> ResolveBridgeId + for EnsureIsRemoteBridgeIdResolver +{ + type BridgeId = bp_xcm_bridge::BridgeId; + + fn resolve_for_dest(dest: &Location) -> Option { + let Ok((remote_network, remote_dest)) = + ensure_is_remote(UniversalLocation::get(), dest.clone()) + else { + log::trace!( + target: LOG_TARGET, + "EnsureIsRemoteBridgeIdResolver - does not recognize a remote destination for: {dest:?}!" + ); + return None + }; + Self::resolve_for(&remote_network, &remote_dest) + } + + fn resolve_for( + bridged_network: &NetworkId, + bridged_dest: &InteriorLocation, + ) -> Option { + let bridged_universal_location = if let Ok(network) = bridged_dest.global_consensus() { + if network.ne(bridged_network) { + log::error!( + target: LOG_TARGET, + "EnsureIsRemoteBridgeIdResolver - bridged_dest: {bridged_dest:?} contains invalid network: {network:?}, expected bridged_network: {bridged_network:?}!" + ); + return None + } else { + bridged_dest.clone() + } + } else { + // if `bridged_dest` does not contain `GlobalConsensus`, let's prepend one + match bridged_dest.clone().pushed_front_with(*bridged_network) { + Ok(bridged_universal_location) => bridged_universal_location, + Err((original, prepend_with)) => { + log::error!( + target: LOG_TARGET, + "EnsureIsRemoteBridgeIdResolver - bridged_dest: {original:?} cannot be prepended with: {prepend_with:?}!" + ); + return None + }, + } + }; + + match ( + UniversalLocation::get().global_consensus(), + bridged_universal_location.global_consensus(), + ) { + (Ok(local), Ok(remote)) if local != remote => (), + (local, remote) => { + log::error!( + target: LOG_TARGET, + "EnsureIsRemoteBridgeIdResolver - local: {local:?} and remote: {remote:?} must be different!" + ); + return None + }, + } + + // calculate `BridgeId` from universal locations + Some(Self::BridgeId::new(&UniversalLocation::get(), &bridged_universal_location)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn ensure_is_remote_bridge_id_resolver_works() { + frame_support::parameter_types! { + pub ThisNetwork: NetworkId = NetworkId::ByGenesis([0; 32]); + pub BridgedNetwork: NetworkId = NetworkId::ByGenesis([1; 32]); + pub UniversalLocation: InteriorLocation = [GlobalConsensus(ThisNetwork::get()), Parachain(1000)].into(); + } + assert_ne!(ThisNetwork::get(), BridgedNetwork::get()); + + type Resolver = EnsureIsRemoteBridgeIdResolver; + + // not remote dest + assert!(Resolver::resolve_for_dest(&Location::new(1, Here)).is_none()); + // not a valid remote dest + assert!(Resolver::resolve_for_dest(&Location::new(2, Here)).is_none()); + // the same network for remote dest + assert!(Resolver::resolve_for_dest(&Location::new(2, GlobalConsensus(ThisNetwork::get()))) + .is_none()); + assert!(Resolver::resolve_for(&ThisNetwork::get(), &Here.into()).is_none()); + + // ok + assert!(Resolver::resolve_for_dest(&Location::new( + 2, + GlobalConsensus(BridgedNetwork::get()) + )) + .is_some()); + assert!(Resolver::resolve_for_dest(&Location::new( + 2, + [GlobalConsensus(BridgedNetwork::get()), Parachain(2013)] + )) + .is_some()); + + // ok - resolves the same + assert_eq!( + Resolver::resolve_for_dest(&Location::new(2, GlobalConsensus(BridgedNetwork::get()))), + Resolver::resolve_for(&BridgedNetwork::get(), &Here.into()), + ); + assert_eq!( + Resolver::resolve_for_dest(&Location::new( + 2, + [GlobalConsensus(BridgedNetwork::get()), Parachain(2013)] + )), + Resolver::resolve_for(&BridgedNetwork::get(), &Parachain(2013).into()), + ); + assert_eq!( + Resolver::resolve_for_dest(&Location::new( + 2, + [GlobalConsensus(BridgedNetwork::get()), Parachain(2013)] + )), + Resolver::resolve_for( + &BridgedNetwork::get(), + &[GlobalConsensus(BridgedNetwork::get()), Parachain(2013)].into() + ), + ); + } +} diff --git a/bridges/modules/xcm-bridge-router/src/lib.rs b/bridges/modules/xcm-bridge-router/src/lib.rs new file mode 100644 index 000000000000..76c34c1ee983 --- /dev/null +++ b/bridges/modules/xcm-bridge-router/src/lib.rs @@ -0,0 +1,737 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! A pallet that can be used as an alternative in the XCM router configuration — see the `SendXcm` +//! implementation for details. +//! +//! ## Features +//! +//! This pallet offers several optional features to customize functionality: +//! +//! ### Message Size Fee +//! An optional fee based on `T::FeeAsset` and `T::ByteFee`. If `T::FeeAsset` is not specified, this +//! fee is not calculated. +//! +//! ### Dynamic Fees and Congestion +//! +//! This pallet supports storing the congestion status of bridge outbound queues. The fee increases +//! exponentially if the queue between this chain and a sibling or child bridge hub becomes +//! congested. All other bridge hub queues provide backpressure mechanisms, so if any of these +//! queues are congested, it will eventually lead to increased queuing on this chain. +//! +//! There are two methods for storing congestion status: +//! 1. A dedicated extrinsic `update_bridge_status`, which relies on `T::UpdateBridgeStatusOrigin`. This +//! allows the message exporter to send, for example, an XCM `Transact`. +//! 2. An implementation of `bp_xcm_bridge::LocalXcmChannelManager`. +//! +//! ## Usage +//! +//! This pallet provides several implementations, such as `ViaLocalBridgeHubExporter` and +//! `ViaRemoteBridgeHubExporter`, which can expose or access these features. +//! +//! This router can be used in two main scenarios, depending on where the router and message +//! exporter (e.g., `pallet_xcm_bridge_hub` or another pallet with an `ExportXcm` implementation) +//! are deployed: +//! +//! ### On the Same Chain as the Message Exporter +//! In this setup, the router directly calls an `ExportXcm` implementation. In this case, +//! `ViaLocalBridgeHubExporter` can be used as a wrapper with `T::MessageExporter`. +//! +//! ### On a Different Chain than the Message Exporter +//! In this setup, we need to provide a `SendXcm` implementation for `T::MessageExporter`, which +//! sends `ExportMessage`. For example, `SovereignPaidRemoteExporter` can be used with +//! `ViaRemoteBridgeHubExporter`. +//! +//! **Note on Terminology**: When we refer to the bridge hub, we mean the chain that has the +//! `pallet-bridge-messages` with an `ExportXcm` implementation deployed, such as +//! `pallet-xcm-bridge`. Depending on the deployment setup, `T::MessageExporter` can be +//! configured accordingly — see `T::MessageExporter` for additional documentation. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use bp_xcm_bridge_router::{BridgeState, ResolveBridgeId}; +use codec::Encode; +use frame_support::traits::{EnsureOriginWithArg, Get}; +use sp_runtime::{FixedPointNumber, FixedU128, Saturating}; +use sp_std::vec::Vec; +use xcm::prelude::*; +use xcm_builder::InspectMessageQueues; + +pub use pallet::*; +pub use weights::WeightInfo; + +pub mod benchmarking; +pub mod impls; +pub mod weights; + +mod mock; + +/// Minimal delivery fee factor. +pub const MINIMAL_DELIVERY_FEE_FACTOR: FixedU128 = FixedU128::from_u32(1); + +/// The factor that is used to increase current message fee factor when bridge experiencing +/// some lags. +const EXPONENTIAL_FEE_BASE: FixedU128 = FixedU128::from_rational(105, 100); // 1.05 +/// The factor that is used to increase current message fee factor for every sent kilobyte. +const MESSAGE_SIZE_FEE_BASE: FixedU128 = FixedU128::from_rational(1, 1000); // 0.001 + +/// Maximal size of the XCM message that may be sent over bridge. +/// +/// This should be less than the maximal size, allowed by the messages pallet, because +/// the message itself is wrapped in other structs and is double encoded. +pub const HARD_MESSAGE_SIZE_LIMIT: u32 = 32 * 1024; + +/// The target that will be used when publishing logs related to this pallet. +pub const LOG_TARGET: &str = "xcm::bridge-router"; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + /// 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: frame_system::Config { + /// The overarching event type. + #[pallet::no_default_bounds] + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + /// Benchmarks results from runtime we're plugged into. + type WeightInfo: WeightInfo; + + /// Checks the XCM version for the destination. + type DestinationVersion: GetVersion; + + /// The bridge hub may be: + /// - A system (sibling) bridge hub parachain (or another chain), in which case we need an + /// implementation for `T::MessageExporter` that sends `ExportMessage`, e.g., + /// `SovereignPaidRemoteExporter`. + /// - The local chain, in which case we need an implementation for `T::MessageExporter` + /// that does not use `ExportMessage` but instead directly calls the `ExportXcm` + /// implementation. + #[pallet::no_default] + type MessageExporter: 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; + + /// Origin that is allowed to update bridge status, + /// e.g. the sibling bridge hub or governance as root. + #[pallet::no_default] + type UpdateBridgeStatusOrigin: EnsureOriginWithArg>; + + /// Additional fee that is paid for every byte of the outbound message. + /// See `calculate_message_size_fee` for more details. + type ByteFee: Get; + /// Asset used to pay the `ByteFee`. + /// If not specified, the `ByteFee` is ignored. + /// See `calculate_fees` for more details. + type FeeAsset: Get>; + } + + /// An alias for the `BridgeId` of configured `T::BridgeIdResolver`. + pub type BridgeIdOf = <>::BridgeIdResolver as ResolveBridgeId>::BridgeId; + + #[pallet::pallet] + pub struct Pallet(PhantomData<(T, I)>); + + #[pallet::call] + impl, I: 'static> Pallet { + /// Notification about congested bridge queue. + #[pallet::call_index(0)] + #[pallet::weight(T::WeightInfo::update_bridge_status())] + pub fn update_bridge_status( + origin: OriginFor, + bridge_id: BridgeIdOf, + is_congested: bool, + ) -> DispatchResult { + let _ = T::UpdateBridgeStatusOrigin::ensure_origin(origin, &bridge_id)?; + + log::info!( + target: LOG_TARGET, + "Received bridge status from {:?}: congested = {}", + bridge_id, + is_congested, + ); + + // update status + Self::do_update_bridge_status(bridge_id, is_congested); + + Ok(()) + } + } + + /// Stores `BridgeState` for congestion control and dynamic fees for each resolved bridge ID + /// associated with a destination. + #[pallet::storage] + pub type Bridges, I: 'static = ()> = + StorageMap<_, Blake2_128Concat, BridgeIdOf, BridgeState, OptionQuery>; + + impl, I: 'static> Pallet { + /// Called when new message is sent to the `dest` (queued to local outbound XCM queue). + pub(crate) fn on_message_sent_to(message_size: u32, dest: Location) { + let Some(bridge_id) = T::BridgeIdResolver::resolve_for_dest(&dest) else { + // not supported bridge id, so do nothing + return + }; + + // handle congestion and fee factor (if detected) + Bridges::::mutate_exists(&bridge_id, |bridge_state| match bridge_state { + Some(ref mut bridge_state) if bridge_state.is_congested => { + // found congested bridge + // ok - we need to increase the fee factor, let's do that + let message_size_factor = + FixedU128::from_u32(message_size.saturating_div(1024)) + .saturating_mul(MESSAGE_SIZE_FEE_BASE); + let total_factor = EXPONENTIAL_FEE_BASE.saturating_add(message_size_factor); + + let previous_factor = bridge_state.delivery_fee_factor; + bridge_state.delivery_fee_factor = + bridge_state.delivery_fee_factor.saturating_mul(total_factor); + + log::info!( + target: LOG_TARGET, + "Bridge channel with id {:?} is congested. Increased fee factor from {} to {} for {:?}", + bridge_id, + previous_factor, + bridge_state.delivery_fee_factor, + dest + ); + Self::deposit_event(Event::DeliveryFeeFactorIncreased { + previous_value: previous_factor, + new_value: bridge_state.delivery_fee_factor, + bridge_id: bridge_id.clone(), + }); + }, + _ => { + // not congested, do nothing + }, + }); + } + + /// Returns the recalculated dynamic fee for a given asset based on the bridge state. + /// + /// This function adjusts the amount of a fungible asset according to the delivery fee + /// factor specified in the `bridge_state`. If the asset is fungible, the + /// `delivery_fee_factor` is applied to the asset’s amount, potentially altering its + /// value. + pub(crate) fn calculate_dynamic_fee(bridge_state: &BridgeState, asset: Asset) -> Asset { + match asset.fun { + Fungible(amount) => { + let adjusted_amount = bridge_state.delivery_fee_factor.saturating_mul_int(amount); + Asset { + fun: Fungible(adjusted_amount), + ..asset + } + } + _ => asset, + } + } + + /// Calculates an (optional) fee for message size based on `T::ByteFee` and `T::FeeAsset`. + pub(crate) fn calculate_message_size_fee( + message_size: impl FnOnce() -> u32, + ) -> Option { + // Apply message size `T::ByteFee/T::FeeAsset` feature (if configured). + if let Some(asset_id) = T::FeeAsset::get() { + let message_fee = (message_size() as u128).saturating_mul(T::ByteFee::get()); + if message_fee > 0 { + return Some((asset_id, message_fee).into()); + } + } + None + } + + /// Updates the congestion status of a bridge for a given `bridge_id`. + /// + /// If the bridge does not exist and: + /// - `is_congested` is true, a new `BridgeState` is created with a default + /// `delivery_fee_factor`. + /// - `is_congested` is false, does nothing and no `BridgeState` is created. + pub(crate) fn do_update_bridge_status(bridge_id: BridgeIdOf, is_congested: bool) { + Bridges::::mutate_exists(&bridge_id, |maybe_bridge| match maybe_bridge.take() { + Some(mut bridge) => if is_congested { + bridge.is_congested = is_congested; + *maybe_bridge = Some(bridge); + } else { + Self::deposit_event(Event::DeliveryFeeFactorDecreased { + previous_value: bridge.delivery_fee_factor, + new_value: 0.into(), + bridge_id: bridge_id.clone(), + }); + }, + None => + if is_congested { + *maybe_bridge = Some(BridgeState { + delivery_fee_factor: MINIMAL_DELIVERY_FEE_FACTOR, + is_congested, + }); + + Self::deposit_event(Event::DeliveryFeeFactorIncreased { + previous_value: 0.into(), + new_value: MINIMAL_DELIVERY_FEE_FACTOR, + bridge_id: bridge_id.clone(), + }); + }, + }); + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// Delivery fee factor has been decreased. + DeliveryFeeFactorDecreased { + /// Previous value of the `DeliveryFeeFactor`. + previous_value: FixedU128, + /// New value of the `DeliveryFeeFactor`. + new_value: FixedU128, + /// Bridge identifier. + bridge_id: BridgeIdOf, + }, + /// Delivery fee factor has been increased. + DeliveryFeeFactorIncreased { + /// Previous value of the `DeliveryFeeFactor`. + previous_value: FixedU128, + /// New value of the `DeliveryFeeFactor`. + new_value: FixedU128, + /// Bridge identifier. + bridge_id: BridgeIdOf, + }, + } +} + +// This pallet acts as the `SendXcm` to the sibling/child bridge hub instead of regular +// XCMP/DMP transport. This allows injecting dynamic message fees into XCM programs that +// are going to the bridged network. +impl, I: 'static> SendXcm for Pallet { + type Ticket = (u32, Location, ::Ticket); + + fn validate( + dest: &mut Option, + xcm: &mut Option>, + ) -> SendResult { + log::trace!(target: LOG_TARGET, "validate - msg: {xcm:?}, destination: {dest:?}"); + + // In case of success, the `T::MessageExporter` can modify XCM instructions and consume + // `dest` / `xcm`, so we retain the clone of original message and the destination for later + // `DestinationVersion` validation. + let xcm_to_dest_clone = xcm.clone(); + let dest_clone = dest.clone(); + + // First, use the inner exporter to validate the destination to determine if it is even + // routable. If it is not, return an error. If it is, then the XCM is extended with + // instructions to pay the message fee at the sibling/child bridge hub. The cost will + // include both the cost of (1) delivery to the sibling bridge hub (returned by + // `Config::MessageExporter`) and (2) delivery to the bridged bridge hub (returned by + // `Self::exporter_for`). + match T::MessageExporter::validate(dest, xcm) { + Ok((ticket, cost)) => { + // If the ticket is ok, it means we are routing with this router, so we need to + // apply more validations to the cloned `dest` and `xcm`, which are required here. + let xcm_to_dest_clone = xcm_to_dest_clone.ok_or(SendError::MissingArgument)?; + let dest_clone = dest_clone.ok_or(SendError::MissingArgument)?; + + // We won't have access to `dest` and `xcm` in the `deliver` method, so we need to + // precompute everything required here. However, `dest` and `xcm` were consumed by + // `T::MessageExporter`, so we need to use their clones. + let message_size = xcm_to_dest_clone.encoded_size() as _; + + // The bridge doesn't support oversized or overweight messages. Therefore, it's + // better to drop such messages here rather than at the bridge hub. Let's check the + // message size. + if message_size > HARD_MESSAGE_SIZE_LIMIT { + return Err(SendError::ExceedsMaxMessageSize) + } + + // We need to ensure that the known `dest`'s XCM version can comprehend the current + // `xcm` program. This may seem like an additional, unnecessary check, but it is + // not. A similar check is probably performed by the `T::MessageExporter`, which + // attempts to send a versioned message to the sibling bridge hub. However, the + // local bridge hub may have a higher XCM version than the remote `dest`. Once + // again, it is better to discard such messages here than at the bridge hub (e.g., + // to avoid losing funds). + let destination_version = T::DestinationVersion::get_version_for(&dest_clone) + .ok_or(SendError::DestinationUnsupported)?; + let _ = VersionedXcm::from(xcm_to_dest_clone) + .into_version(destination_version) + .map_err(|()| SendError::DestinationUnsupported)?; + + log::info!( + target: LOG_TARGET, + "Going to send message to {dest_clone:?} ({message_size:?} bytes) with actual cost: {cost:?}" + ); + + Ok(((message_size, dest_clone, ticket), cost)) + }, + Err(e) => { + log::trace!(target: LOG_TARGET, "`T::MessageExporter` validates for dest: {dest_clone:?} with error: {e:?}"); + Err(e) + }, + } + } + + fn deliver(ticket: Self::Ticket) -> Result { + // use router to enqueue message to the sibling/child bridge hub. This also should handle + // payment for passing through this queue. + let (message_size, dest, ticket) = ticket; + let xcm_hash = T::MessageExporter::deliver(ticket)?; + + log::trace!( + target: LOG_TARGET, + "deliver - message (size: {message_size:?}) sent to the dest: {dest:?}, xcm_hash: {xcm_hash:?}" + ); + + // increase delivery fee factor (if required) + Self::on_message_sent_to(message_size, dest); + + Ok(xcm_hash) + } +} + +impl, I: 'static> InspectMessageQueues for Pallet { + fn clear_messages() {} + + /// This router needs to implement `InspectMessageQueues` but doesn't have to + /// return any messages, since it just reuses the `XcmpQueue` router. + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + Vec::new() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use frame_support::assert_ok; + use mock::*; + + use frame_system::{EventRecord, Phase}; + use sp_runtime::traits::Dispatchable; + + #[test] + fn not_applicable_if_destination_is_within_other_network() { + run_test(|| { + // unroutable dest + let dest = Location::new(2, [GlobalConsensus(ByGenesis([0; 32])), Parachain(1000)]); + let xcm: Xcm<()> = vec![ClearOrigin].into(); + + // check that router does not consume when `NotApplicable` + let mut xcm_wrapper = Some(xcm.clone()); + assert_eq!( + XcmBridgeHubRouter::validate(&mut Some(dest.clone()), &mut xcm_wrapper), + Err(SendError::NotApplicable), + ); + // XCM is NOT consumed and untouched + assert_eq!(Some(xcm.clone()), xcm_wrapper); + + // check the full `send_xcm` + assert_eq!(send_xcm::(dest, xcm,), Err(SendError::NotApplicable),); + }); + } + + #[test] + fn exceeds_max_message_size_if_size_is_above_hard_limit() { + run_test(|| { + // routable dest with XCM version + let dest = + Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)]); + // oversized XCM + let xcm: Xcm<()> = vec![ClearOrigin; HARD_MESSAGE_SIZE_LIMIT as usize].into(); + + // dest is routable with the inner router + assert_ok!(>::MessageExporter::validate( + &mut Some(dest.clone()), + &mut Some(xcm.clone()) + )); + + // check for oversized message + let mut xcm_wrapper = Some(xcm.clone()); + assert_eq!( + XcmBridgeHubRouter::validate(&mut Some(dest.clone()), &mut xcm_wrapper), + Err(SendError::ExceedsMaxMessageSize), + ); + // XCM is consumed by the inner router + assert!(xcm_wrapper.is_none()); + + // check the full `send_xcm` + assert_eq!( + send_xcm::(dest, xcm,), + Err(SendError::ExceedsMaxMessageSize), + ); + }); + } + + #[test] + fn destination_unsupported_if_wrap_version_fails() { + run_test(|| { + // routable dest but we don't know XCM version + let dest = UnknownXcmVersionForRoutableLocation::get(); + let xcm: Xcm<()> = vec![ClearOrigin].into(); + + // dest is routable with the inner router + assert_ok!(>::MessageExporter::validate( + &mut Some(dest.clone()), + &mut Some(xcm.clone()) + )); + + // check that it does not pass XCM version check + let mut xcm_wrapper = Some(xcm.clone()); + assert_eq!( + XcmBridgeHubRouter::validate(&mut Some(dest.clone()), &mut xcm_wrapper), + Err(SendError::DestinationUnsupported), + ); + // XCM is consumed by the inner router + assert!(xcm_wrapper.is_none()); + + // check the full `send_xcm` + assert_eq!( + send_xcm::(dest, xcm,), + Err(SendError::DestinationUnsupported), + ); + }); + } + + #[test] + fn returns_proper_delivery_price() { + run_test(|| { + let dest = Location::new(2, [GlobalConsensus(BridgedNetworkId::get())]); + let xcm: Xcm<()> = vec![ClearOrigin].into(); + let msg_size = xcm.encoded_size(); + + // `BASE_FEE + BYTE_FEE * msg_size` (without `HRMP_FEE`) + let base_cost_formula = || BASE_FEE + BYTE_FEE * (msg_size as u128); + + // initially the base fee is used + let expected_fee = base_cost_formula() + HRMP_FEE; + assert_eq!( + XcmBridgeHubRouter::validate(&mut Some(dest.clone()), &mut Some(xcm.clone())) + .unwrap() + .1 + .get(0), + Some(&(BridgeFeeAsset::get(), expected_fee).into()), + ); + + // but when factor is larger than one, it increases the fee, so it becomes: + // `base_cost_formula() * F` + let factor = FixedU128::from_rational(125, 100); + + // make bridge congested + update fee factor + set_bridge_state_for::( + &dest, + Some(BridgeState { delivery_fee_factor: factor, is_congested: true }), + ); + + let expected_fee = + (FixedU128::saturating_from_integer(base_cost_formula()) * factor).into_inner() / + FixedU128::DIV + HRMP_FEE; + assert_eq!( + XcmBridgeHubRouter::validate(&mut Some(dest), &mut Some(xcm)).unwrap().1.get(0), + Some(&(BridgeFeeAsset::get(), expected_fee).into()), + ); + }); + } + + #[test] + fn sent_message_doesnt_increase_factor_if_bridge_is_uncongested() { + run_test(|| { + let dest = + Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)]); + + // make bridge congested + update fee factor + let old_delivery_fee_factor = FixedU128::from_rational(125, 100); + set_bridge_state_for::( + &dest, + Some(BridgeState { + delivery_fee_factor: old_delivery_fee_factor, + is_congested: false, + }), + ); + + assert_eq!( + send_xcm::(dest.clone(), vec![ClearOrigin].into(),).map(drop), + Ok(()), + ); + + assert!(TestXcmRouter::is_message_sent()); + assert_eq!( + old_delivery_fee_factor, + get_bridge_state_for::(&dest).unwrap().delivery_fee_factor + ); + + assert_eq!(System::events(), vec![]); + }); + } + + #[test] + fn sent_message_increases_factor_if_bridge_is_congested() { + run_test(|| { + let dest = + Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)]); + + // make bridge congested + update fee factor + let old_delivery_fee_factor = FixedU128::from_rational(125, 100); + set_bridge_state_for::( + &dest, + Some(BridgeState { + delivery_fee_factor: old_delivery_fee_factor, + is_congested: true, + }), + ); + + assert_ok!( + send_xcm::(dest.clone(), vec![ClearOrigin].into(),).map(drop) + ); + + assert!(TestXcmRouter::is_message_sent()); + assert!( + old_delivery_fee_factor < + get_bridge_state_for::(&dest).unwrap().delivery_fee_factor + ); + + // check emitted event + let first_system_event = System::events().first().cloned(); + assert!(matches!( + first_system_event, + Some(EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmBridgeHubRouter( + Event::DeliveryFeeFactorIncreased { .. } + ), + .. + }) + )); + }); + } + + #[test] + fn get_messages_does_not_return_anything() { + run_test(|| { + assert_ok!(send_xcm::( + (Parent, Parent, GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)).into(), + vec![ClearOrigin].into() + )); + assert_eq!(XcmBridgeHubRouter::get_messages(), vec![]); + }); + } + + #[test] + fn update_bridge_status_works() { + run_test(|| { + let dest = + Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)]); + let bridge_id = + bp_xcm_bridge::BridgeId::new(&UniversalLocation::get(), dest.interior()); + let update_bridge_status = |bridge_id, is_congested| { + let call = RuntimeCall::XcmBridgeHubRouter(Call::update_bridge_status { + bridge_id, + is_congested, + }); + assert_ok!(call.dispatch(RuntimeOrigin::root())); + }; + + assert!(get_bridge_state_for::(&dest).is_none()); + update_bridge_status(bridge_id, false); + assert!(get_bridge_state_for::(&dest).is_none()); + + // make congested + update_bridge_status(bridge_id, true); + assert_eq!( + get_bridge_state_for::(&dest), + Some(BridgeState { + delivery_fee_factor: MINIMAL_DELIVERY_FEE_FACTOR, + is_congested: true, + }) + ); + + // make uncongested + update_bridge_status(bridge_id, false); + assert_eq!( + get_bridge_state_for::(&dest), + None + ); + }); + } + + #[test] + fn do_update_bridge_status_works() { + run_test(|| { + let dest = Location::new(2, [GlobalConsensus(BridgedNetworkId::get())]); + let bridge_id = + bp_xcm_bridge::BridgeId::new(&UniversalLocation::get(), dest.interior()); + assert!(get_bridge_state_for::(&dest).is_none()); + + // update as is_congested=false when `None` + Pallet::::do_update_bridge_status(bridge_id, false); + assert!(get_bridge_state_for::(&dest).is_none()); + + // update as is_congested=true + Pallet::::do_update_bridge_status(bridge_id, true); + assert_eq!( + get_bridge_state_for::(&dest), + Some(BridgeState { + delivery_fee_factor: MINIMAL_DELIVERY_FEE_FACTOR, + is_congested: true, + }) + ); + + // increase fee factor when congested + Pallet::::on_message_sent_to(5, dest.clone()); + assert!( + get_bridge_state_for::(&dest).unwrap().delivery_fee_factor > MINIMAL_DELIVERY_FEE_FACTOR + ); + // update as is_congested=true - should not reset fee factor + Pallet::::do_update_bridge_status(bridge_id, true); + assert!( + get_bridge_state_for::(&dest).unwrap().delivery_fee_factor > MINIMAL_DELIVERY_FEE_FACTOR + ); + + // update as is_congested=false when `Some(..)` + Pallet::::do_update_bridge_status(bridge_id, false); + assert_eq!( + get_bridge_state_for::(&dest), + None, + ); + }) + } +} diff --git a/bridges/modules/xcm-bridge-router/src/mock.rs b/bridges/modules/xcm-bridge-router/src/mock.rs new file mode 100644 index 000000000000..c5d8a6eac019 --- /dev/null +++ b/bridges/modules/xcm-bridge-router/src/mock.rs @@ -0,0 +1,231 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +#![cfg(test)] + +use crate as pallet_xcm_bridge_router; + +use crate::impls::EnsureIsRemoteBridgeIdResolver; +use bp_xcm_bridge_router::{BridgeState, ResolveBridgeId}; +use codec::Encode; +use frame_support::{ + construct_runtime, derive_impl, parameter_types, + traits::{Contains, Equals}, +}; +use frame_system::EnsureRoot; +use sp_runtime::{traits::ConstU128, BuildStorage}; +use sp_std::cell::RefCell; +use xcm::prelude::*; +use xcm_builder::{ + InspectMessageQueues, NetworkExportTable, NetworkExportTableItem, SovereignPaidRemoteExporter, +}; + +type Block = frame_system::mocking::MockBlock; + +/// HRMP fee. +pub const HRMP_FEE: u128 = 500; +/// Base bridge fee. +pub const BASE_FEE: u128 = 1_000_000; +/// Byte bridge fee. +pub const BYTE_FEE: u128 = 1_000; + +construct_runtime! { + pub enum TestRuntime + { + System: frame_system, + XcmBridgeHubRouter: pallet_xcm_bridge_router, + } +} + +parameter_types! { + pub ThisNetworkId: NetworkId = Polkadot; + pub BridgedNetworkId: NetworkId = Kusama; + pub UniversalLocation: InteriorLocation = [GlobalConsensus(ThisNetworkId::get()), Parachain(1000)].into(); + pub SiblingBridgeHubLocation: Location = ParentThen([Parachain(1002)].into()).into(); + pub BridgeFeeAsset: AssetId = Location::parent().into(); + pub BridgeTable: Vec + = vec![ + NetworkExportTableItem::new( + BridgedNetworkId::get(), + None, + SiblingBridgeHubLocation::get(), + Some((BridgeFeeAsset::get(), BASE_FEE).into()) + ) + ]; + pub UnknownXcmVersionForRoutableLocation: Location = Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(9999)]); +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for TestRuntime { + type Block = Block; +} + +/// Simple implementation where every dest resolves to the exact one `BridgeId`. +pub struct EveryDestinationToSameBridgeIdResolver; +impl ResolveBridgeId for EveryDestinationToSameBridgeIdResolver { + type BridgeId = (); + + fn resolve_for_dest(_dest: &Location) -> Option { + Some(()) + } + + fn resolve_for( + _bridged_network: &NetworkId, + _bridged_dest: &InteriorLocation, + ) -> Option { + Some(()) + } +} + +/// 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_router::config_preludes::TestDefaultConfig)] +impl pallet_xcm_bridge_router::Config<()> for TestRuntime { + type DestinationVersion = + LatestOrNoneForLocationVersionChecker>; + + type MessageExporter = SovereignPaidRemoteExporter< + pallet_xcm_bridge_router::impls::ViaRemoteBridgeHubExporter< + TestRuntime, + (), + NetworkExportTable, + BridgedNetworkId, + SiblingBridgeHubLocation, + >, + TestXcmRouter, + UniversalLocation, + >; + + type BridgeIdResolver = EnsureIsRemoteBridgeIdResolver; + type UpdateBridgeStatusOrigin = EnsureRoot; + + type ByteFee = ConstU128; + type FeeAsset = BridgeFeeAsset; +} + +pub struct LatestOrNoneForLocationVersionChecker(sp_std::marker::PhantomData); +impl> GetVersion + for LatestOrNoneForLocationVersionChecker +{ + fn get_version_for(dest: &Location) -> Option { + if LocationValue::contains(dest) { + return None + } + Some(XCM_VERSION) + } +} + +pub struct TestXcmRouter; + +impl TestXcmRouter { + pub fn is_message_sent() -> bool { + !Self::get_messages().is_empty() + } +} + +thread_local! { + pub static SENT_XCM: RefCell)>> = RefCell::new(Vec::new()); +} + +impl SendXcm for TestXcmRouter { + type Ticket = (Location, Xcm<()>); + + fn validate( + destination: &mut Option, + message: &mut Option>, + ) -> SendResult { + let pair = (destination.take().unwrap(), message.take().unwrap()); + Ok((pair, (BridgeFeeAsset::get(), HRMP_FEE).into())) + } + + fn deliver(pair: Self::Ticket) -> Result { + let hash = fake_message_hash(&pair.1); + SENT_XCM.with(|q| q.borrow_mut().push(pair)); + Ok(hash) + } +} + +impl InspectMessageQueues for TestXcmRouter { + fn clear_messages() { + SENT_XCM.with(|q| q.borrow_mut().clear()); + } + + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + SENT_XCM.with(|q| { + (*q.borrow()) + .clone() + .iter() + .map(|(location, message)| { + ( + VersionedLocation::from(location.clone()), + vec![VersionedXcm::from(message.clone())], + ) + }) + .collect() + }) + } +} + +/// Return test externalities to use in tests. +pub fn new_test_ext() -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + sp_io::TestExternalities::new(t) +} + +/// Run pallet test. +pub fn run_test(test: impl FnOnce() -> T) -> T { + new_test_ext().execute_with(|| { + System::set_block_number(1); + System::reset_events(); + + test() + }) +} + +pub(crate) fn fake_message_hash(message: &Xcm) -> XcmHash { + message.using_encoded(sp_io::hashing::blake2_256) +} + +pub(crate) fn set_bridge_state_for, I: 'static>( + dest: &Location, + bridge_state: Option, +) -> pallet_xcm_bridge_router::BridgeIdOf { + let bridge_id = ::resolve_for_dest(dest).unwrap(); + if let Some(bridge_state) = bridge_state { + pallet_xcm_bridge_router::Bridges::::insert(&bridge_id, bridge_state); + } else { + pallet_xcm_bridge_router::Bridges::::remove(&bridge_id); + } + bridge_id +} + +pub(crate) fn get_bridge_state_for, I: 'static>( + dest: &Location, +) -> Option { + let bridge_id = ::resolve_for_dest(dest).unwrap(); + pallet_xcm_bridge_router::Bridges::::get(bridge_id) +} + +#[cfg(feature = "runtime-benchmarks")] +impl crate::benchmarking::Config<()> for TestRuntime { + fn ensure_bridged_target_destination() -> Result { + Ok(Location::new(2, [GlobalConsensus(BridgedNetworkId::get())])) + } + fn update_bridge_status_origin() -> Option { + Some(RuntimeOrigin::root()) + } +} diff --git a/bridges/modules/xcm-bridge-router/src/weights.rs b/bridges/modules/xcm-bridge-router/src/weights.rs new file mode 100644 index 000000000000..9455653c6958 --- /dev/null +++ b/bridges/modules/xcm-bridge-router/src/weights.rs @@ -0,0 +1,71 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Autogenerated weights for pallet_xcm_bridge_hub_router +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-08-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/rip-bridge-node +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_xcm_bridge_hub_router +// --extrinsic=* +// --execution=wasm +// --wasm-execution=Compiled +// --heap-pages=4096 +// --output=./modules/xcm-bridge-hub-router/src/weights.rs +// --template=./.maintain/bridge-weight-template.hbs + +#![allow(clippy::all)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_xcm_bridge_hub_router. +pub trait WeightInfo { + fn update_bridge_status() -> Weight; +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: `ToUnknownXcmRouter::Bridges` (r:1 w:1) + /// Proof: `ToUnknownXcmRouter::Bridges` (`max_values`: None, `max_size`: Some(65), added: 2540, + /// mode: `MaxEncodedLen`) + fn update_bridge_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `3530` + // Minimum execution time: 12_179_000 picoseconds. + Weight::from_parts(12_679_000, 0) + .saturating_add(Weight::from_parts(0, 3530)) + .saturating_add(RocksDbWeight::get().reads(1)) + .saturating_add(RocksDbWeight::get().writes(1)) + } +} diff --git a/bridges/modules/xcm-bridge/Cargo.toml b/bridges/modules/xcm-bridge/Cargo.toml new file mode 100644 index 000000000000..3bcff067015d --- /dev/null +++ b/bridges/modules/xcm-bridge/Cargo.toml @@ -0,0 +1,92 @@ +[package] +name = "pallet-xcm-bridge" +description = "Module that adds dynamic bridges/lanes support to XCM infrastructure at the bridge hub." +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +repository.workspace = true + +[lints] +workspace = true + +[dependencies] +codec = { workspace = true } +log = { workspace = true } +scale-info = { features = ["derive"], workspace = true } + +# Bridge Dependencies +bp-messages = { workspace = true } +bp-runtime = { workspace = true } +bp-xcm-bridge = { workspace = true } +pallet-bridge-messages = { workspace = true } + +# Substrate Dependencies +frame-benchmarking = { optional = true, workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +# Polkadot Dependencies +xcm = { workspace = true } +xcm-builder = { workspace = true } +xcm-executor = { workspace = true } + +[dev-dependencies] +bp-header-chain = { workspace = true } +bp-runtime = { workspace = true } +bp-xcm-bridge-router = { workspace = true } +pallet-balances = { workspace = true } +pallet-xcm-bridge-router = { workspace = true } +polkadot-parachain-primitives = { workspace = true } +sp-io = { workspace = true } + +[features] +default = ["std"] +std = [ + "bp-header-chain/std", + "bp-messages/std", + "bp-runtime/std", + "bp-xcm-bridge-router/std", + "bp-xcm-bridge/std", + "codec/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "log/std", + "pallet-balances/std", + "pallet-bridge-messages/std", + "pallet-xcm-bridge-router/std", + "polkadot-parachain-primitives/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "xcm-builder/std", + "xcm-executor/std", + "xcm/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-bridge-messages/runtime-benchmarks", + "pallet-xcm-bridge-router/runtime-benchmarks", + "polkadot-parachain-primitives/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", + "xcm/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-balances/try-runtime", + "pallet-bridge-messages/try-runtime", + "pallet-xcm-bridge-router/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/bridges/modules/xcm-bridge/src/benchmarking.rs b/bridges/modules/xcm-bridge/src/benchmarking.rs new file mode 100644 index 000000000000..aec23581c03c --- /dev/null +++ b/bridges/modules/xcm-bridge/src/benchmarking.rs @@ -0,0 +1,147 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! XCM bridge hub pallet benchmarks. + +#![cfg(feature = "runtime-benchmarks")] + +use crate::{Call, Receiver, ThisChainOf}; +use bp_runtime::BalanceOf; +use bp_xcm_bridge::BridgeLocations; +use frame_benchmarking::v2::*; +use frame_support::{ + assert_ok, + traits::{fungible::Unbalanced, tokens::Precision, Contains, Get}, +}; +use sp_std::boxed::Box; +use xcm_executor::traits::ConvertLocation; + +use sp_runtime::Saturating; +use xcm::prelude::*; + +/// Pallet we're benchmarking here. +pub struct Pallet, I: 'static = ()>(crate::Pallet); + +/// Trait that must be implemented by runtime to be able to benchmark pallet properly. +pub trait Config: crate::Config { + /// Returns a valid origin along with the initial balance (e.g., existential deposit), + /// required for operation `open_bridge`. + /// If `None`, that means that `open_bridge` is not supported. + fn open_bridge_origin() -> Option<(Self::RuntimeOrigin, BalanceOf>)>; +} + +#[instance_benchmarks] +mod benchmarks { + use super::*; + + fn prepare_for_open_bridge, I: 'static>( + ) -> Result<(T::RuntimeOrigin, Box), BenchmarkError> { + let (origin, initial_balance) = T::open_bridge_origin() + .ok_or(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?; + let bridge_destination_universal_location: Box = + Box::new([GlobalConsensus(crate::Pallet::::bridged_network_id()?)].into()); + let expected = crate::Pallet::::bridge_locations_from_origin( + origin.clone(), + bridge_destination_universal_location, + )?; + + if !T::AllowWithoutBridgeDeposit::contains(expected.bridge_origin_relative_location()) { + // fund origin's sovereign account + let bridge_owner_account = T::BridgeOriginAccountIdConverter::convert_location( + expected.bridge_origin_relative_location(), + ) + .ok_or(BenchmarkError::Stop("InvalidBridgeOriginAccount"))?; + + T::Currency::increase_balance( + &bridge_owner_account, + initial_balance.saturating_add(T::BridgeDeposit::get()), + Precision::BestEffort, + )?; + } + + Ok((origin, expected)) + } + + #[benchmark] + fn open_bridge() -> Result<(), BenchmarkError> { + let (origin, locations) = prepare_for_open_bridge::()?; + let bridge_destination_universal_location: Box = + Box::new(locations.bridge_destination_universal_location().clone().into()); + assert!(crate::Pallet::::bridge(locations.bridge_id()).is_none()); + + #[extrinsic_call] + _(origin as T::RuntimeOrigin, bridge_destination_universal_location, None); + + assert!(crate::Pallet::::bridge(locations.bridge_id()).is_some()); + Ok(()) + } + + #[benchmark] + fn close_bridge() -> Result<(), BenchmarkError> { + let (origin, locations) = prepare_for_open_bridge::()?; + let bridge_destination_universal_location: Box = + Box::new(locations.bridge_destination_universal_location().clone().into()); + assert!(crate::Pallet::::bridge(locations.bridge_id()).is_none()); + + // open bridge + assert_ok!(crate::Pallet::::open_bridge( + origin.clone(), + bridge_destination_universal_location.clone(), + None, + )); + assert!(crate::Pallet::::bridge(locations.bridge_id()).is_some()); + + #[extrinsic_call] + _(origin as T::RuntimeOrigin, bridge_destination_universal_location, 10); + + assert!(crate::Pallet::::bridge(locations.bridge_id()).is_none()); + Ok(()) + } + + #[benchmark] + fn update_notification_receiver() -> Result<(), BenchmarkError> { + let (origin, locations) = prepare_for_open_bridge::()?; + let bridge_destination_universal_location: Box = + Box::new(locations.bridge_destination_universal_location().clone().into()); + assert!(crate::Pallet::::bridge(locations.bridge_id()).is_none()); + + // open bridge with None + assert_ok!(crate::Pallet::::open_bridge( + origin.clone(), + bridge_destination_universal_location.clone(), + None, + )); + assert_eq!( + crate::Pallet::::bridge(locations.bridge_id()).map(|b| b.maybe_notify), + Some(None) + ); + + #[extrinsic_call] + _( + origin as T::RuntimeOrigin, + bridge_destination_universal_location, + Some(Receiver::new(1, 5)), + ); + + assert_eq!( + crate::Pallet::::bridge(locations.bridge_id()).map(|b| b.maybe_notify), + Some(Some(Receiver::new(1, 5))) + ); + Ok(()) + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime); +} diff --git a/bridges/modules/xcm-bridge/src/congestion.rs b/bridges/modules/xcm-bridge/src/congestion.rs new file mode 100644 index 000000000000..32b0ca4889f6 --- /dev/null +++ b/bridges/modules/xcm-bridge/src/congestion.rs @@ -0,0 +1,231 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! The module contains utilities for handling congestion between the bridge hub and routers. + +use crate::{Bridges, Config, DispatchChannelStatusProvider, LOG_TARGET}; +use bp_xcm_bridge::{BridgeId, LocalXcmChannelManager, Receiver}; +use codec::{Decode, Encode}; +use sp_runtime::traits::Convert; +use sp_std::{marker::PhantomData, vec::Vec}; +use xcm::latest::{send_xcm, Location, SendXcm, Xcm}; +use xcm_builder::{DispatchBlob, DispatchBlobError}; + +/// Limits for handling congestion. +#[derive(Debug, Decode, Encode)] +pub struct CongestionLimits { + /// Maximal number of messages in the outbound bridge queue. Once we reach this limit, we + /// suspend a bridge. + pub outbound_lane_congested_threshold: bp_messages::MessageNonce, + /// After we have suspended the bridge, we wait until number of messages in the outbound bridge + /// queue drops to this count, before sending resuming the bridge. + pub outbound_lane_uncongested_threshold: bp_messages::MessageNonce, + /// Maximal number of messages in the outbound bridge queue after we have suspended the bridge. + /// Once we reach this limit, we stop exporting more messages. + pub outbound_lane_stop_threshold: bp_messages::MessageNonce, +} + +impl CongestionLimits { + /// Checks if limits are valid. + pub fn is_valid(&self) -> bool { + self.outbound_lane_uncongested_threshold < self.outbound_lane_congested_threshold && + self.outbound_lane_stop_threshold > self.outbound_lane_congested_threshold + } +} + +impl Default for CongestionLimits { + fn default() -> Self { + Self { + outbound_lane_congested_threshold: 8_192, + outbound_lane_uncongested_threshold: 1_024, + outbound_lane_stop_threshold: 12_288, + } + } +} + +/// Switches the implementation of [`LocalXcmChannelManager`] based on the `local_origin`. +/// +/// - `HereXcmChannelManager` is applied when the origin is `Here`. +/// - Otherwise, `LocalConsensusXcmChannelManager` is used. +/// +/// This is useful when the `pallet-xcm-bridge` needs to support both: +/// - A local router deployed on the same chain as the `pallet-xcm-bridge`. +/// - A remote router deployed on a different chain than the `pallet-xcm-bridge`. +pub struct HereOrLocalConsensusXcmChannelManager< + Bridge, + HereXcmChannelManager, + LocalConsensusXcmChannelManager, +>(PhantomData<(Bridge, HereXcmChannelManager, LocalConsensusXcmChannelManager)>); +impl< + Bridge: Encode + sp_std::fmt::Debug + Copy, + HereXcmChannelManager: LocalXcmChannelManager, + LocalConsensusXcmChannelManager: LocalXcmChannelManager, + > LocalXcmChannelManager + for HereOrLocalConsensusXcmChannelManager< + Bridge, + HereXcmChannelManager, + LocalConsensusXcmChannelManager, + > +{ + type Error = (); + + fn suspend_bridge(local_origin: &Location, bridge: Bridge) -> Result<(), Self::Error> { + if local_origin.eq(&Location::here()) { + HereXcmChannelManager::suspend_bridge(local_origin, bridge).map_err(|e| { + log::error!( + target: LOG_TARGET, + "HereXcmChannelManager::suspend_bridge error: {e:?} for local_origin: {:?} and bridge: {:?}", + local_origin, + bridge, + ); + () + }) + } else { + LocalConsensusXcmChannelManager::suspend_bridge(local_origin, bridge).map_err(|e| { + log::error!( + target: LOG_TARGET, + "LocalConsensusXcmChannelManager::suspend_bridge error: {e:?} for local_origin: {:?} and bridge: {:?}", + local_origin, + bridge, + ); + () + }) + } + } + + fn resume_bridge(local_origin: &Location, bridge: Bridge) -> Result<(), Self::Error> { + if local_origin.eq(&Location::here()) { + HereXcmChannelManager::resume_bridge(local_origin, bridge).map_err(|e| { + log::error!( + target: LOG_TARGET, + "HereXcmChannelManager::resume_bridge error: {e:?} for local_origin: {:?} and bridge: {:?}", + local_origin, + bridge, + ); + () + }) + } else { + LocalConsensusXcmChannelManager::resume_bridge(local_origin, bridge).map_err(|e| { + log::error!( + target: LOG_TARGET, + "LocalConsensusXcmChannelManager::resume_bridge error: {e:?} for local_origin: {:?} and bridge: {:?}", + local_origin, + bridge, + ); + () + }) + } + } +} + +/// Manages the local XCM channels by sending XCM messages with the `update_bridge_status` extrinsic +/// to the `local_origin`. The `XcmProvider` type converts the encoded call to `XCM`, which is then +/// sent by `XcmSender` to the `local_origin`. This is useful, for example, when a router with +/// [`xcm::prelude::ExportMessage`] is deployed on a different chain, and we want to control +/// congestion by sending XCMs. +pub struct UpdateBridgeStatusXcmChannelManager( + PhantomData<(T, I, XcmProvider, XcmSender)>, +); +impl, I: 'static, XcmProvider: Convert, Xcm<()>>, XcmSender: SendXcm> + UpdateBridgeStatusXcmChannelManager +{ + fn update_bridge_status( + local_origin: &Location, + bridge_id: BridgeId, + is_congested: bool, + ) -> Result<(), ()> { + // check the bridge and get `maybe_notify` callback. + let bridge = Bridges::::get(&bridge_id).ok_or(())?; + let Some(Receiver { pallet_index, call_index }) = bridge.maybe_notify else { + // `local_origin` did not set `maybe_notify`, so nothing to notify, so it is ok. + return Ok(()) + }; + + // constructing expected call + let remote_runtime_call = (pallet_index, call_index, bridge_id, is_congested); + // construct XCM + let xcm = XcmProvider::convert(remote_runtime_call.encode()); + log::trace!( + target: LOG_TARGET, + "UpdateBridgeStatusXcmChannelManager is going to send status with is_congested: {:?} to the local_origin: {:?} and bridge: {:?} as xcm: {:?}", + is_congested, + local_origin, + bridge, + xcm, + ); + + // send XCM + send_xcm::(local_origin.clone(), xcm) + .map(|result| { + log::warn!( + target: LOG_TARGET, + "UpdateBridgeStatusXcmChannelManager successfully sent status with is_congested: {:?} to the local_origin: {:?} and bridge: {:?} with result: {:?}", + is_congested, + local_origin, + bridge, + result, + ); + () + }) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "UpdateBridgeStatusXcmChannelManager failed to send status with is_congested: {:?} to the local_origin: {:?} and bridge: {:?} with error: {:?}", + is_congested, + local_origin, + bridge, + e, + ); + () + }) + } +} +impl, I: 'static, XcmProvider: Convert, Xcm<()>>, XcmSender: SendXcm> + LocalXcmChannelManager + for UpdateBridgeStatusXcmChannelManager +{ + type Error = (); + + fn suspend_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error> { + Self::update_bridge_status(local_origin, bridge, true) + } + + fn resume_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error> { + Self::update_bridge_status(local_origin, bridge, false) + } +} + +/// Adapter that ties together the [`DispatchBlob`] trait with the [`DispatchChannelStatusProvider`] +/// trait. The idea is that [`DispatchBlob`] triggers message dispatch/delivery on the receiver +/// side, while [`DispatchChannelStatusProvider`] provides a status check to ensure the dispatch +/// channel is active (not congested). +pub struct BlobDispatcherWithChannelStatus( + PhantomData<(ChannelDispatch, ChannelStatus)>, +); +impl DispatchBlob + for BlobDispatcherWithChannelStatus +{ + fn dispatch_blob(blob: Vec) -> Result<(), DispatchBlobError> { + ChannelDispatch::dispatch_blob(blob) + } +} +impl DispatchChannelStatusProvider + for BlobDispatcherWithChannelStatus +{ + fn is_congested(with: &Location) -> bool { + ChannelStatus::is_congested(with) + } +} diff --git a/bridges/modules/xcm-bridge/src/dispatcher.rs b/bridges/modules/xcm-bridge/src/dispatcher.rs new file mode 100644 index 000000000000..ef716ebf3931 --- /dev/null +++ b/bridges/modules/xcm-bridge/src/dispatcher.rs @@ -0,0 +1,266 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! The code that allows to use the pallet (`pallet-xcm-bridge`) as inbound +//! bridge messages dispatcher. Internally, it just forwards inbound blob to the +//! XCM-level blob dispatcher, which pushes message to some other queue (e.g. +//! to HRMP queue with the sibling target chain). +//! +//! This code is executed at the target bridge hub. + +use crate::{Config, DispatchChannelStatusProvider, Pallet, LOG_TARGET}; + +use bp_messages::target_chain::{DispatchMessage, MessageDispatch}; +use bp_runtime::messages::MessageDispatchResult; +use bp_xcm_bridge::XcmAsPlainPayload; +use codec::{Decode, Encode}; +use frame_support::{weights::Weight, CloneNoBound, EqNoBound, PartialEqNoBound}; +use pallet_bridge_messages::{Config as BridgeMessagesConfig, WeightInfoExt}; +use scale_info::TypeInfo; +use sp_runtime::SaturatedConversion; +use xcm::prelude::*; +use xcm_builder::{DispatchBlob, DispatchBlobError}; + +/// Message dispatch result type for single message. +#[derive(CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, Debug, TypeInfo)] +pub enum XcmBlobMessageDispatchResult { + /// We've been unable to decode message payload. + InvalidPayload, + /// Message has been dispatched. + Dispatched, + /// Message has **NOT** been dispatched because of given error. + NotDispatched(#[codec(skip)] Option), +} + +/// An easy way to access associated messages pallet weights. +type MessagesPalletWeights = + >::BridgeMessagesPalletInstance>>::WeightInfo; + +impl, I: 'static> MessageDispatch for Pallet +where + T: BridgeMessagesConfig, +{ + type DispatchPayload = XcmAsPlainPayload; + type DispatchLevelResult = XcmBlobMessageDispatchResult; + type LaneId = T::LaneId; + + fn is_active(lane: Self::LaneId) -> bool { + Pallet::::bridge_by_lane_id(&lane) + .and_then(|(_, bridge)| bridge.bridge_origin_relative_location.try_as().cloned().ok()) + .map(|recipient: Location| !T::BlobDispatcher::is_congested(&recipient)) + .unwrap_or(false) + } + + fn dispatch_weight( + message: &mut DispatchMessage, + ) -> Weight { + match message.data.payload { + Ok(ref payload) => { + let payload_size = payload.encoded_size().saturated_into(); + MessagesPalletWeights::::message_dispatch_weight(payload_size) + }, + Err(_) => Weight::zero(), + } + } + + fn dispatch( + message: DispatchMessage, + ) -> MessageDispatchResult { + let payload = match message.data.payload { + Ok(payload) => payload, + Err(e) => { + log::error!( + target: LOG_TARGET, + "dispatch - payload error: {e:?} for lane_id: {:?} and message_nonce: {:?}", + message.key.lane_id, + message.key.nonce + ); + return MessageDispatchResult { + unspent_weight: Weight::zero(), + dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload, + } + }, + }; + let dispatch_level_result = match T::BlobDispatcher::dispatch_blob(payload) { + Ok(_) => { + log::debug!( + target: LOG_TARGET, + "dispatch - `DispatchBlob::dispatch_blob` was ok for lane_id: {:?} and message_nonce: {:?}", + message.key.lane_id, + message.key.nonce + ); + XcmBlobMessageDispatchResult::Dispatched + }, + Err(e) => { + log::error!( + target: LOG_TARGET, + "dispatch - `DispatchBlob::dispatch_blob` failed with error: {e:?} for lane_id: {:?} and message_nonce: {:?}", + message.key.lane_id, + message.key.nonce + ); + XcmBlobMessageDispatchResult::NotDispatched(Some(e)) + }, + }; + MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{mock::*, Bridges, LaneToBridge, LanesManagerOf}; + + use bp_messages::{target_chain::DispatchMessageData, LaneIdType, MessageKey}; + use bp_xcm_bridge::{Bridge, BridgeLocations, BridgeState}; + use frame_support::assert_ok; + use pallet_bridge_messages::InboundLaneStorage; + + fn bridge() -> (Box, TestLaneIdType) { + let origin = OpenBridgeOrigin::sibling_parachain_origin(); + let with = bridged_asset_hub_universal_location(); + let locations = + XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap(); + let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + (locations, lane_id) + } + + fn run_test_with_opened_bridge(test: impl FnOnce()) { + run_test(|| { + let (bridge, lane_id) = bridge(); + + if !Bridges::::contains_key(bridge.bridge_id()) { + // insert bridge + Bridges::::insert( + bridge.bridge_id(), + Bridge { + bridge_origin_relative_location: Box::new( + bridge.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + bridge.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + bridge.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + deposit: None, + lane_id, + maybe_notify: None, + }, + ); + LaneToBridge::::insert(lane_id, bridge.bridge_id()); + + // create lanes + let lanes_manager = LanesManagerOf::::new(); + if lanes_manager.create_inbound_lane(lane_id).is_ok() { + assert_eq!( + 0, + lanes_manager + .active_inbound_lane(lane_id) + .unwrap() + .storage() + .data() + .last_confirmed_nonce + ); + } + if lanes_manager.create_outbound_lane(lane_id).is_ok() { + assert!(lanes_manager + .active_outbound_lane(lane_id) + .unwrap() + .queued_messages() + .is_empty()); + } + } + assert_ok!(XcmOverBridge::do_try_state()); + + test(); + }); + } + + fn invalid_message() -> DispatchMessage, TestLaneIdType> { + DispatchMessage { + key: MessageKey { lane_id: TestLaneIdType::try_new(1, 2).unwrap(), nonce: 1 }, + data: DispatchMessageData { payload: Err(codec::Error::from("test")) }, + } + } + + fn valid_message() -> DispatchMessage, TestLaneIdType> { + DispatchMessage { + key: MessageKey { lane_id: TestLaneIdType::try_new(1, 2).unwrap(), nonce: 1 }, + data: DispatchMessageData { payload: Ok(vec![42]) }, + } + } + + #[test] + fn dispatcher_is_inactive_when_channel_with_target_chain_is_congested() { + run_test_with_opened_bridge(|| { + let bridge = bridge(); + let with = bridge.0.bridge_origin_relative_location(); + let lane_id = bridge.1; + TestBlobDispatcher::make_congested(with); + assert!(!XcmOverBridge::is_active(lane_id)); + }); + } + + #[test] + fn dispatcher_is_active_when_channel_with_target_chain_is_not_congested() { + run_test_with_opened_bridge(|| { + assert!(XcmOverBridge::is_active(bridge().1)); + }); + } + + #[test] + fn dispatch_weight_is_zero_if_we_have_failed_to_decode_message() { + run_test(|| { + assert_eq!(XcmOverBridge::dispatch_weight(&mut invalid_message()), Weight::zero()); + }); + } + + #[test] + fn dispatch_weight_is_non_zero_if_we_have_decoded_message() { + run_test(|| { + assert_ne!(XcmOverBridge::dispatch_weight(&mut valid_message()), Weight::zero()); + }); + } + + #[test] + fn message_is_not_dispatched_when_we_have_failed_to_decode_message() { + run_test(|| { + assert_eq!( + XcmOverBridge::dispatch(invalid_message()), + MessageDispatchResult { + unspent_weight: Weight::zero(), + dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload, + }, + ); + assert!(!TestBlobDispatcher::is_dispatched()); + }); + } + + #[test] + fn message_is_dispatched_when_we_have_decoded_message() { + run_test(|| { + assert_eq!( + XcmOverBridge::dispatch(valid_message()), + MessageDispatchResult { + unspent_weight: Weight::zero(), + dispatch_level_result: XcmBlobMessageDispatchResult::Dispatched, + }, + ); + assert!(TestBlobDispatcher::is_dispatched()); + }); + } +} diff --git a/bridges/modules/xcm-bridge/src/exporter.rs b/bridges/modules/xcm-bridge/src/exporter.rs new file mode 100644 index 000000000000..065e3b775aae --- /dev/null +++ b/bridges/modules/xcm-bridge/src/exporter.rs @@ -0,0 +1,1163 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! The code that allows to use the pallet (`pallet-xcm-bridge`) as XCM message +//! exporter at the sending bridge hub. Internally, it just enqueues outbound blob +//! in the messages pallet queue. +//! +//! This code is executed at the source bridge hub. + +use crate::{Config, Pallet, LOG_TARGET}; + +use crate::{BridgeOf, Bridges}; + +use bp_messages::{ + source_chain::{MessagesBridge, OnMessagesDelivered}, + MessageNonce, +}; +use bp_xcm_bridge::{BridgeId, BridgeState, LocalXcmChannelManager, XcmAsPlainPayload}; +use frame_support::{ensure, traits::Get}; +use pallet_bridge_messages::{ + Config as BridgeMessagesConfig, Error, Pallet as BridgeMessagesPallet, +}; +use xcm::prelude::*; +use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter}; +use xcm_executor::traits::ExportXcm; + +/// An easy way to access `HaulBlobExporter`. +pub type PalletAsHaulBlobExporter = HaulBlobExporter< + DummyHaulBlob, + >::BridgedNetwork, + >::DestinationVersion, + >::MessageExportPrice, +>; +/// An easy way to access associated messages pallet. +type MessagesPallet = BridgeMessagesPallet>::BridgeMessagesPalletInstance>; + +impl, I: 'static> ExportXcm for Pallet +where + T: BridgeMessagesConfig, +{ + type Ticket = ( + BridgeId, + BridgeOf, + as MessagesBridge>::SendMessageArgs, + XcmHash, + ); + + fn validate( + network: NetworkId, + channel: u32, + universal_source: &mut Option, + destination: &mut Option, + message: &mut Option>, + ) -> Result<(Self::Ticket, Assets), SendError> { + log::trace!( + target: LOG_TARGET, + "Validate for network: {network:?}, channel: {channel:?}, universal_source: {universal_source:?}, destination: {destination:?}" + ); + + // `HaulBlobExporter` may consume the `universal_source` and `destination` arguments, so + // let's save them before + let bridge_origin_universal_location = + universal_source.clone().take().ok_or(SendError::MissingArgument)?; + // Note: watch out this is `ExportMessage::destination`, which is relative to the `network`, + // which means it does not contain `GlobalConsensus`, We need to find `BridgeId` with + // `Self::bridge_locations` which requires **universal** location for destination. + let bridge_destination_universal_location = { + let dest = destination.clone().take().ok_or(SendError::MissingArgument)?; + match dest.global_consensus() { + Ok(dest_network) => { + log::trace!( + target: LOG_TARGET, + "Destination: {dest:?} is already universal, checking dest_network: {dest_network:?} and network: {network:?} if matches: {:?}", + dest_network == network + ); + ensure!(dest_network == network, SendError::NotApplicable); + // ok, `dest` looks like a universal location, so let's use it + dest + }, + Err(_) => { + // `dest` is not a universal location, so we need to prepend it with + // `GlobalConsensus`. + dest.pushed_front_with(GlobalConsensus(network)).map_err(|error_data| { + log::error!( + target: LOG_TARGET, + "Destination: {:?} is not a universal and prepending with {:?} failed!", + error_data.0, + error_data.1, + ); + SendError::NotApplicable + })? + }, + } + }; + + // prepare the origin relative location + let bridge_origin_relative_location = + bridge_origin_universal_location.relative_to(&T::UniversalLocation::get()); + + // then we are able to compute the `BridgeId` and find `LaneId` used to send messages + let locations = Self::bridge_locations( + bridge_origin_relative_location, + bridge_destination_universal_location.into(), + ) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "Validate `bridge_locations` with error: {e:?}", + ); + SendError::NotApplicable + })?; + let bridge = Self::bridge(locations.bridge_id()).ok_or_else(|| { + log::error!( + target: LOG_TARGET, + "No opened bridge for requested bridge_origin_relative_location: {:?} (bridge_origin_universal_location: {:?}) and bridge_destination_universal_location: {:?}", + locations.bridge_origin_relative_location(), + locations.bridge_origin_universal_location(), + locations.bridge_destination_universal_location(), + ); + SendError::NotApplicable + })?; + + // check if we are able to route the message. We use existing `HaulBlobExporter` for that. + // It will make all required changes and will encode message properly, so that the + // `DispatchBlob` at the bridged bridge hub will be able to decode it + let ((blob, id), price) = PalletAsHaulBlobExporter::::validate( + network, + channel, + universal_source, + destination, + message, + )?; + + // Here, we know that the message is relevant to this pallet instance, so let's check for + // congestion defense. + if bridge.state == BridgeState::HardSuspended { + log::error!( + target: LOG_TARGET, + "Bridge for requested bridge_origin_relative_location: {:?} (bridge_origin_universal_location: {:?}) and bridge_destination_universal_location: {:?} \ + is suspended and does not accept more messages!", + locations.bridge_origin_relative_location(), + locations.bridge_origin_universal_location(), + locations.bridge_destination_universal_location(), + ); + return Err(SendError::Transport("Exporter is suspended!")); + } + + let bridge_message = MessagesPallet::::validate_message(bridge.lane_id, &blob) + .map_err(|e| { + match e { + Error::LanesManager(ref ei) => + log::error!(target: LOG_TARGET, "LanesManager: {ei:?}"), + Error::MessageRejectedByPallet(ref ei) => + log::error!(target: LOG_TARGET, "MessageRejectedByPallet: {ei:?}"), + Error::ReceptionConfirmation(ref ei) => + log::error!(target: LOG_TARGET, "ReceptionConfirmation: {ei:?}"), + _ => (), + }; + + log::error!( + target: LOG_TARGET, + "XCM message {:?} cannot be exported because of bridge error: {:?} on bridge {:?} and laneId: {:?}", + id, + e, + locations, + bridge.lane_id, + ); + SendError::Transport("BridgeValidateError") + })?; + + Ok(((*locations.bridge_id(), bridge, bridge_message, id), price)) + } + + fn deliver( + (bridge_id, bridge, bridge_message, id): Self::Ticket, + ) -> Result { + let artifacts = MessagesPallet::::send_message(bridge_message); + + log::info!( + target: LOG_TARGET, + "XCM message {:?} has been enqueued at bridge {:?} and lane_id: {:?} with nonce {}", + id, + bridge_id, + bridge.lane_id, + artifacts.nonce, + ); + + // maybe we need switch to congested state + Self::on_bridge_message_enqueued(bridge_id, bridge, artifacts.enqueued_messages); + + Ok(id) + } +} + +impl, I: 'static> OnMessagesDelivered for Pallet { + fn on_messages_delivered(lane_id: T::LaneId, enqueued_messages: MessageNonce) { + Self::on_bridge_messages_delivered(lane_id, enqueued_messages); + } +} + +impl, I: 'static> Pallet { + /// Called when new message is pushed onto outbound bridge queue. + fn on_bridge_message_enqueued( + bridge_id: BridgeId, + bridge: BridgeOf, + enqueued_messages: MessageNonce, + ) { + // if the bridge queue is not congested, we don't want to do anything + let is_congested = + enqueued_messages > T::CongestionLimits::get().outbound_lane_congested_threshold; + if !is_congested { + return + } + + // check if the lane is already suspended or not. + match bridge.state { + BridgeState::SoftSuspended => { + if enqueued_messages > T::CongestionLimits::get().outbound_lane_stop_threshold { + // If its suspended and reached `outbound_lane_stop_threshold`, we stop + // accepting new messages (a.k.a. start dropping). + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::HardSuspended; + }); + return + } else { + // We still can accept new messages to the suspended bridge, hoping that it'll + // be actually resumed soon + return + } + }, + BridgeState::HardSuspended => { + // We cannot accept new messages and start dropping messages, until the outbound lane goes below the drop limit. + return + }, + _ => { + // otherwise, continue handling the suspension + }, + } + + // else - suspend the bridge + let bridge_origin_relative_location = match bridge.bridge_origin_relative_location.try_as() + { + Ok(bridge_origin_relative_location) => bridge_origin_relative_location, + Err(_) => { + log::error!( + target: LOG_TARGET, + "Failed to convert the bridge {:?} origin location {:?}", + bridge_id, + bridge.bridge_origin_relative_location, + ); + + return + }, + }; + let suspend_result = + T::LocalXcmChannelManager::suspend_bridge(bridge_origin_relative_location, bridge_id); + match suspend_result { + Ok(_) => { + log::debug!( + target: LOG_TARGET, + "Suspended the bridge {:?}, originated by the {:?}", + bridge_id, + bridge.bridge_origin_relative_location, + ); + }, + Err(e) => { + log::error!( + target: LOG_TARGET, + "Failed to suspended the bridge {:?}, originated by the {:?}: {:?}", + bridge_id, + bridge.bridge_origin_relative_location, + e, + ); + + return + }, + } + + // and remember that we have suspended the bridge + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::SoftSuspended; + }); + } + + /// Must be called whenever we receive a message delivery confirmation. + fn on_bridge_messages_delivered(lane_id: T::LaneId, enqueued_messages: MessageNonce) { + // if the bridge queue is still congested, we don't want to do anything + let is_congested = + enqueued_messages > T::CongestionLimits::get().outbound_lane_uncongested_threshold; + if is_congested { + // and if it is bellow the `stop_threshold` + if enqueued_messages < T::CongestionLimits::get().outbound_lane_stop_threshold { + if let Some((bridge_id, bridge)) = Self::bridge_by_lane_id(&lane_id) { + if let BridgeState::HardSuspended = bridge.state { + // we allow exporting again + Bridges::::mutate_extant(bridge_id, |b| { + b.state = BridgeState::SoftSuspended; + }); + } + } + } + return + } + + // if we have not suspended the bridge before (or it is closed), we don't want to do + // anything + let (bridge_id, bridge) = match Self::bridge_by_lane_id(&lane_id) { + Some(bridge) + if bridge.1.state == BridgeState::SoftSuspended || + bridge.1.state == BridgeState::HardSuspended => + bridge, + _ => { + // if there is no bridge, or it has been closed, then we don't need to send resume + // signal to the local origin - it has closed bridge itself, so it should have + // already pruned everything else + return + }, + }; + + // else - resume the bridge + let bridge_origin_relative_location = (*bridge.bridge_origin_relative_location).try_into(); + let bridge_origin_relative_location = match bridge_origin_relative_location { + Ok(bridge_origin_relative_location) => bridge_origin_relative_location, + Err(e) => { + log::error!( + target: LOG_TARGET, + "Failed to convert the bridge {:?} location for lane_id: {:?}, error {:?}", + bridge_id, + lane_id, + e, + ); + + return + }, + }; + + let resume_result = + T::LocalXcmChannelManager::resume_bridge(&bridge_origin_relative_location, bridge_id); + match resume_result { + Ok(_) => { + log::debug!( + target: LOG_TARGET, + "Resumed the bridge {:?} and lane_id: {:?}, originated by the {:?}", + bridge_id, + lane_id, + bridge_origin_relative_location, + ); + }, + Err(e) => { + log::error!( + target: LOG_TARGET, + "Failed to resume the bridge {:?} and lane_id: {:?}, originated by the {:?}: {:?}", + bridge_id, + lane_id, + bridge_origin_relative_location, + e, + ); + + return + }, + } + + // and forget that we have previously suspended the bridge + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::Opened; + }); + } +} + +/// Dummy implementation of the `HaulBlob` trait that is never called. +/// +/// We are using `HaulBlobExporter`, which requires `HaulBlob` implementation. It assumes that +/// there's a single channel between two bridge hubs - `HaulBlob` only accepts the blob and nothing +/// else. But bridge messages pallet may have a dedicated channel (lane) for every pair of bridged +/// chains. So we are using our own `ExportXcm` implementation, but to utilize `HaulBlobExporter` we +/// still need this `DummyHaulBlob`. +pub struct DummyHaulBlob; + +impl HaulBlob for DummyHaulBlob { + fn haul_blob(_blob: XcmAsPlainPayload) -> Result<(), HaulBlobError> { + Err(HaulBlobError::Transport("DummyHaulBlob")) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{mock::*, Bridges, LanesManagerOf}; + + use bp_runtime::RangeInclusiveExt; + use bp_xcm_bridge::{Bridge, BridgeLocations, BridgeState, Receiver}; + use frame_support::{ + assert_err, assert_ok, + traits::{Contains, EnsureOrigin}, + }; + use pallet_xcm_bridge_router::ResolveBridgeId; + use xcm_builder::{NetworkExportTable, UnpaidRemoteExporter}; + use xcm_executor::traits::export_xcm; + + fn universal_source() -> InteriorLocation { + SiblingUniversalLocation::get() + } + + fn bridged_relative_destination() -> InteriorLocation { + BridgedRelativeDestination::get() + } + + fn bridged_universal_destination() -> InteriorLocation { + BridgedUniversalDestination::get() + } + + fn open_lane(origin: RuntimeOrigin) -> (BridgeLocations, TestLaneIdType) { + // open expected outbound lane + let with = bridged_asset_hub_universal_location(); + let locations = + XcmOverBridge::bridge_locations_from_origin(origin.clone(), Box::new(with.into())) + .unwrap(); + let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + + if !Bridges::::contains_key(locations.bridge_id()) { + // fund origin (if needed) + if !>::AllowWithoutBridgeDeposit::contains( + locations.bridge_origin_relative_location(), + ) { + fund_origin_sovereign_account( + &locations, + BridgeDeposit::get() + ExistentialDeposit::get(), + ); + } + + // open bridge + assert_ok!(XcmOverBridge::do_open_bridge(locations.clone(), lane_id, true, None)); + } + assert_ok!(XcmOverBridge::do_try_state()); + + (*locations, lane_id) + } + + fn open_lane_and_send_regular_message( + source_origin: RuntimeOrigin, + ) -> (BridgeId, TestLaneIdType) { + let (locations, lane_id) = open_lane(source_origin); + + // now let's try to enqueue message using our `ExportXcm` implementation + export_xcm::( + BridgedRelayNetwork::get(), + 0, + locations.bridge_origin_universal_location().clone(), + locations.bridge_destination_universal_location().clone(), + vec![Instruction::ClearOrigin].into(), + ) + .unwrap(); + + (*locations.bridge_id(), lane_id) + } + + #[test] + fn exporter_works() { + run_test(|| { + let (_, lane_id) = + open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin()); + + // double check that the message has been pushed to the expected lane + // (it should already been checked during `send_message` call) + assert!(!LanesManagerOf::::new() + .active_outbound_lane(lane_id) + .unwrap() + .queued_messages() + .is_empty()); + }); + } + + #[test] + fn exporter_does_not_suspend_the_bridge_if_outbound_bridge_queue_is_not_congested() { + run_test(|| { + let (bridge_id, _) = + open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin()); + assert!(!TestLocalXcmChannelManager::is_bridge_suspened(&bridge_id)); + assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened); + }); + } + + #[test] + fn exporter_does_not_suspend_the_bridge_if_it_is_already_suspended() { + run_test(|| { + let (bridge_id, _) = + open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin()); + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::SoftSuspended; + }); + for _ in 1..TestCongestionLimits::get().outbound_lane_congested_threshold { + open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin()); + } + + open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin()); + assert!(!TestLocalXcmChannelManager::is_bridge_suspened(&bridge_id)); + }); + } + + #[test] + fn exporter_suspends_the_bridge_if_outbound_bridge_queue_is_congested() { + run_test(|| { + let (bridge_id, _) = + open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin()); + for _ in 1..TestCongestionLimits::get().outbound_lane_congested_threshold { + open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin()); + } + + assert!(!TestLocalXcmChannelManager::is_bridge_suspened(&bridge_id)); + assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened); + + open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin()); + assert!(TestLocalXcmChannelManager::is_bridge_suspened(&bridge_id)); + assert_eq!( + XcmOverBridge::bridge(&bridge_id).unwrap().state, + BridgeState::SoftSuspended + ); + + // send more messages to reach `outbound_lane_stop_threshold` + for _ in TestCongestionLimits::get().outbound_lane_congested_threshold.. + TestCongestionLimits::get().outbound_lane_stop_threshold + { + open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin()); + } + assert_eq!( + XcmOverBridge::bridge(&bridge_id).unwrap().state, + BridgeState::HardSuspended + ); + }); + } + + #[test] + fn bridge_is_not_resumed_if_outbound_bridge_queue_is_still_congested() { + run_test(|| { + let (bridge_id, lane_id) = + open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin()); + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::SoftSuspended; + }); + XcmOverBridge::on_bridge_messages_delivered( + lane_id, + TestCongestionLimits::get().outbound_lane_uncongested_threshold + 1, + ); + + assert!(!TestLocalXcmChannelManager::is_bridge_resumed(&bridge_id)); + assert_eq!( + XcmOverBridge::bridge(&bridge_id).unwrap().state, + BridgeState::SoftSuspended + ); + }); + } + + #[test] + fn bridge_is_not_resumed_if_it_was_not_suspended_before() { + run_test(|| { + let (bridge_id, lane_id) = + open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin()); + XcmOverBridge::on_bridge_messages_delivered( + lane_id, + TestCongestionLimits::get().outbound_lane_uncongested_threshold, + ); + + assert!(!TestLocalXcmChannelManager::is_bridge_resumed(&bridge_id)); + assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened); + }); + } + + #[test] + fn exporter_respects_stop_threshold() { + run_test(|| { + let (bridge_id, lane_id) = + open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin()); + let xcm: Xcm<()> = vec![ClearOrigin].into(); + + // Opened - exporter works + assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened); + assert_ok!(XcmOverBridge::validate( + BridgedRelayNetwork::get(), + 0, + &mut Some(universal_source()), + &mut Some(bridged_relative_destination()), + &mut Some(xcm.clone()), + ),); + + // SoftSuspended - exporter still works + XcmOverBridge::on_bridge_message_enqueued( + bridge_id, + XcmOverBridge::bridge(&bridge_id).unwrap(), + TestCongestionLimits::get().outbound_lane_congested_threshold + 1, + ); + assert_eq!( + XcmOverBridge::bridge(&bridge_id).unwrap().state, + BridgeState::SoftSuspended + ); + assert_ok!(XcmOverBridge::validate( + BridgedRelayNetwork::get(), + 0, + &mut Some(universal_source()), + &mut Some(bridged_relative_destination()), + &mut Some(xcm.clone()), + ),); + + // HardSuspended - exporter stops working + XcmOverBridge::on_bridge_message_enqueued( + bridge_id, + XcmOverBridge::bridge(&bridge_id).unwrap(), + TestCongestionLimits::get().outbound_lane_stop_threshold + 1, + ); + assert_eq!( + XcmOverBridge::bridge(&bridge_id).unwrap().state, + BridgeState::HardSuspended + ); + assert_err!( + XcmOverBridge::validate( + BridgedRelayNetwork::get(), + 0, + &mut Some(universal_source()), + &mut Some(bridged_relative_destination()), + &mut Some(xcm.clone()), + ), + SendError::Transport("Exporter is suspended!"), + ); + + // Back to SoftSuspended - exporter again works + XcmOverBridge::on_bridge_messages_delivered( + lane_id, + TestCongestionLimits::get().outbound_lane_stop_threshold - 1, + ); + assert_eq!( + XcmOverBridge::bridge(&bridge_id).unwrap().state, + BridgeState::SoftSuspended + ); + assert_ok!(XcmOverBridge::validate( + BridgedRelayNetwork::get(), + 0, + &mut Some(universal_source()), + &mut Some(bridged_relative_destination()), + &mut Some(xcm.clone()), + ),); + + // Back to Opened - exporter works + XcmOverBridge::on_bridge_messages_delivered( + lane_id, + TestCongestionLimits::get().outbound_lane_uncongested_threshold - 1, + ); + assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened); + assert_ok!(XcmOverBridge::validate( + BridgedRelayNetwork::get(), + 0, + &mut Some(universal_source()), + &mut Some(bridged_relative_destination()), + &mut Some(xcm.clone()), + ),); + }); + } + + #[test] + fn bridge_is_resumed_when_enough_messages_are_delivered() { + run_test(|| { + let (bridge_id, lane_id) = + open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin()); + Bridges::::mutate_extant(bridge_id, |bridge| { + bridge.state = BridgeState::SoftSuspended; + }); + XcmOverBridge::on_bridge_messages_delivered( + lane_id, + TestCongestionLimits::get().outbound_lane_uncongested_threshold, + ); + + assert!(TestLocalXcmChannelManager::is_bridge_resumed(&bridge_id)); + assert_eq!(XcmOverBridge::bridge(&bridge_id).unwrap().state, BridgeState::Opened); + }); + } + + #[test] + fn export_fails_if_argument_is_missing() { + run_test(|| { + assert_eq!( + XcmOverBridge::validate( + BridgedRelayNetwork::get(), + 0, + &mut None, + &mut Some(bridged_relative_destination()), + &mut Some(Vec::new().into()), + ), + Err(SendError::MissingArgument), + ); + + assert_eq!( + XcmOverBridge::validate( + BridgedRelayNetwork::get(), + 0, + &mut Some(universal_source()), + &mut None, + &mut Some(Vec::new().into()), + ), + Err(SendError::MissingArgument), + ); + }) + } + + #[test] + fn exporter_computes_correct_lane_id() { + run_test(|| { + assert_ne!(bridged_universal_destination(), bridged_relative_destination()); + + let locations = BridgeLocations::bridge_locations( + UniversalLocation::get(), + SiblingLocation::get(), + bridged_universal_destination(), + BridgedRelayNetwork::get(), + ) + .unwrap(); + let expected_bridge_id = locations.bridge_id(); + let expected_lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + + if LanesManagerOf::::new() + .create_outbound_lane(expected_lane_id) + .is_ok() + { + Bridges::::insert( + expected_bridge_id, + Bridge { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + deposit: None, + lane_id: expected_lane_id, + maybe_notify: None, + }, + ); + } + + let ticket = XcmOverBridge::validate( + BridgedRelayNetwork::get(), + 0, + &mut Some(universal_source()), + // Note: The `ExportMessage` expects relative `InteriorLocation` in the + // `BridgedRelayNetwork`. + &mut Some(bridged_relative_destination()), + &mut Some(Vec::new().into()), + ) + .unwrap() + .0; + assert_eq!(&ticket.0, expected_bridge_id); + assert_eq!(ticket.1.lane_id, expected_lane_id); + }); + } + + #[test] + fn pallet_as_exporter_is_compatible_with_pallet_xcm_bridge_hub_router_for_export_message() { + run_test(|| { + // valid routable destination + let dest = Location::new(2, BridgedUniversalDestination::get()); + + // open bridge + let origin = OpenBridgeOrigin::sibling_parachain_origin(); + let origin_as_location = + OpenBridgeOriginOf::::try_origin(origin.clone()).unwrap(); + 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(), + &>::BridgeIdResolver::resolve_for_dest(&dest) + .unwrap() + ); + + // check before - no messages + assert_eq!( + pallet_bridge_messages::Pallet::::outbound_lane_data( + expected_lane_id + ) + .unwrap() + .queued_messages() + .saturating_len(), + 0 + ); + + // send `ExportMessage(message)` by `UnpaidRemoteExporter`. + ExecuteXcmOverSendXcm::set_origin_for_execute(origin_as_location.clone()); + assert_ok!(send_xcm::< + UnpaidRemoteExporter< + NetworkExportTable, + ExecuteXcmOverSendXcm, + UniversalLocation, + >, + >(dest.clone(), Xcm::<()>::default())); + + // we need to set `UniversalLocation` for `sibling_parachain_origin` for + // `XcmOverBridgeWrappedWithExportMessageRouterInstance`. + ExportMessageOriginUniversalLocation::set(Some(SiblingUniversalLocation::get())); + // send `ExportMessage(message)` by `pallet_xcm_bridge_hub_router`. + ExecuteXcmOverSendXcm::set_origin_for_execute(origin_as_location); + assert_ok!(send_xcm::( + dest, + Xcm::<()>::default() + )); + + // check after - a message ready to be relayed + assert_eq!( + pallet_bridge_messages::Pallet::::outbound_lane_data( + expected_lane_id + ) + .unwrap() + .queued_messages() + .saturating_len(), + 2 + ); + }) + } + + #[test] + fn pallet_as_exporter_is_compatible_with_pallet_xcm_bridge_hub_router_for_export_xcm() { + run_test(|| { + // valid routable destination + let dest = Location::new(2, BridgedUniversalDestination::get()); + + // open bridge as a root on the local chain, which should be converted as + // `Location::here()` + let (bridge, expected_lane_id) = open_lane(RuntimeOrigin::root()); + + // check compatible bridge_id + assert_eq!( + bridge.bridge_id(), + &>::BridgeIdResolver::resolve_for_dest(&dest) + .unwrap() + ); + + // check before - no messages + assert_eq!( + pallet_bridge_messages::Pallet::::outbound_lane_data( + expected_lane_id + ) + .unwrap() + .queued_messages() + .saturating_len(), + 0 + ); + + // trigger `ExportXcm` by `pallet_xcm_bridge_hub_router`. + assert_ok!(send_xcm::(dest, Xcm::<()>::default())); + + // check after - a message ready to be relayed + assert_eq!( + pallet_bridge_messages::Pallet::::outbound_lane_data( + expected_lane_id + ) + .unwrap() + .queued_messages() + .saturating_len(), + 1 + ); + }) + } + + #[test] + fn validate_works() { + run_test(|| { + let xcm: Xcm<()> = vec![ClearOrigin].into(); + + // check that router does not consume when `NotApplicable` + let mut xcm_wrapper = Some(xcm.clone()); + let mut universal_source_wrapper = Some(universal_source()); + + // wrong `NetworkId` + let mut dest_wrapper = Some(bridged_relative_destination()); + assert_eq!( + XcmOverBridge::validate( + NetworkId::ByGenesis([0; 32]), + 0, + &mut universal_source_wrapper, + &mut dest_wrapper, + &mut xcm_wrapper, + ), + Err(SendError::NotApplicable), + ); + // dest and xcm is NOT consumed and untouched + assert_eq!(&Some(xcm.clone()), &xcm_wrapper); + assert_eq!(&Some(universal_source()), &universal_source_wrapper); + assert_eq!(&Some(bridged_relative_destination()), &dest_wrapper); + + // dest starts with wrong `NetworkId` + let mut invalid_dest_wrapper = Some( + [GlobalConsensus(NetworkId::ByGenesis([0; 32])), Parachain(BRIDGED_ASSET_HUB_ID)] + .into(), + ); + assert_eq!( + XcmOverBridge::validate( + BridgedRelayNetwork::get(), + 0, + &mut Some(universal_source()), + &mut invalid_dest_wrapper, + &mut xcm_wrapper, + ), + Err(SendError::NotApplicable), + ); + // dest and xcm is NOT consumed and untouched + assert_eq!(&Some(xcm.clone()), &xcm_wrapper); + assert_eq!(&Some(universal_source()), &universal_source_wrapper); + assert_eq!( + &Some( + [ + GlobalConsensus(NetworkId::ByGenesis([0; 32]),), + Parachain(BRIDGED_ASSET_HUB_ID) + ] + .into() + ), + &invalid_dest_wrapper + ); + + // no opened lane for dest + let mut dest_without_lane_wrapper = + Some([GlobalConsensus(BridgedRelayNetwork::get()), Parachain(5679)].into()); + assert_eq!( + XcmOverBridge::validate( + BridgedRelayNetwork::get(), + 0, + &mut Some(universal_source()), + &mut dest_without_lane_wrapper, + &mut xcm_wrapper, + ), + Err(SendError::NotApplicable), + ); + // dest and xcm is NOT consumed and untouched + assert_eq!(&Some(xcm.clone()), &xcm_wrapper); + assert_eq!(&Some(universal_source()), &universal_source_wrapper); + assert_eq!( + &Some([GlobalConsensus(BridgedRelayNetwork::get(),), Parachain(5679)].into()), + &dest_without_lane_wrapper + ); + + // ok + let (locations, _) = open_lane(OpenBridgeOrigin::sibling_parachain_origin()); + let mut dest_wrapper = Some(bridged_relative_destination()); + assert_ok!(XcmOverBridge::validate( + BridgedRelayNetwork::get(), + 0, + &mut Some(universal_source()), + &mut dest_wrapper, + &mut xcm_wrapper, + )); + // dest and xcm IS consumed + assert_eq!(None, xcm_wrapper); + assert_eq!(&Some(universal_source()), &universal_source_wrapper); + assert_eq!(None, dest_wrapper); + + // send more messages to reach `outbound_lane_congested_threshold` + for _ in 0..=TestCongestionLimits::get().outbound_lane_congested_threshold { + open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin()); + } + // bridge is suspended but exporter accepts more messages + assert_eq!( + XcmOverBridge::bridge(locations.bridge_id()).unwrap().state, + BridgeState::SoftSuspended + ); + + // export still can accept more messages + assert_ok!(XcmOverBridge::validate( + BridgedRelayNetwork::get(), + 0, + &mut Some(universal_source()), + &mut Some(bridged_relative_destination()), + &mut Some(xcm.clone()), + )); + + // send more messages to reach `outbound_lane_stop_threshold` + for _ in TestCongestionLimits::get().outbound_lane_congested_threshold.. + TestCongestionLimits::get().outbound_lane_stop_threshold + { + open_lane_and_send_regular_message(OpenBridgeOrigin::sibling_parachain_origin()); + } + + // bridge is suspended but exporter CANNOT accept more messages + assert_eq!( + XcmOverBridge::bridge(locations.bridge_id()).unwrap().state, + BridgeState::HardSuspended + ); + + // export still can accept more messages + assert_err!( + XcmOverBridge::validate( + BridgedRelayNetwork::get(), + 0, + &mut Some(universal_source()), + &mut Some(bridged_relative_destination()), + &mut Some(xcm.clone()), + ), + SendError::Transport("Exporter is suspended!"), + ); + }); + } + + #[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, I: 'static>( + dest: &Location, + ) -> Option { + let bridge_id = + ::resolve_for_dest(dest).unwrap(); + pallet_xcm_bridge_router::Bridges::::get(&bridge_id) + } + + // open two bridges + let origin = OpenBridgeOrigin::sibling_parachain_origin(); + let origin_as_location = + OpenBridgeOriginOf::::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())); + + // we need to update `maybe_notify` for `bridge_1` with `pallet_index` of + // `XcmOverBridgeWrappedWithExportMessageRouter`, + Bridges::::mutate_extant(bridge_1.bridge_id(), |bridge| { + bridge.maybe_notify = Some(Receiver::new(57, 0)); + }); + + // check before + // bridges are opened + 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::( + &dest + ) + .map(|bs| bs.is_congested) + .unwrap_or(false)); + assert!(!TestLocalXcmChannelManager::is_bridge_suspened(bridge_1.bridge_id())); + assert!(!TestLocalXcmChannelManager::is_bridge_suspened(bridge_2.bridge_id())); + assert!(!TestLocalXcmChannelManager::is_bridge_resumed(bridge_1.bridge_id())); + assert!(!TestLocalXcmChannelManager::is_bridge_resumed(bridge_2.bridge_id())); + + // make bridges congested with sending too much messages + for _ in 1..(TestCongestionLimits::get().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::( + dest.clone(), + Xcm::<()>::default() + )); + + // call direct `ExportXcm` by `pallet_xcm_bridge_hub_router`. + assert_ok!(send_xcm::( + dest.clone(), + Xcm::<()>::default() + )); + } + + // checks after + // bridges are suspended + assert_eq!( + XcmOverBridge::bridge(bridge_1.bridge_id()).unwrap().state, + BridgeState::SoftSuspended + ); + assert_eq!( + XcmOverBridge::bridge(bridge_2.bridge_id()).unwrap().state, + BridgeState::SoftSuspended + ); + // both routers are congested + assert!( + router_bridge_state::< + TestRuntime, + XcmOverBridgeWrappedWithExportMessageRouterInstance, + >(&dest) + .unwrap() + .is_congested + ); + assert!( + router_bridge_state::(&dest) + .unwrap() + .is_congested + ); + assert!(TestLocalXcmChannelManager::is_bridge_suspened(bridge_1.bridge_id())); + assert!(TestLocalXcmChannelManager::is_bridge_suspened(bridge_2.bridge_id())); + assert!(!TestLocalXcmChannelManager::is_bridge_resumed(bridge_1.bridge_id())); + assert!(!TestLocalXcmChannelManager::is_bridge_resumed(bridge_2.bridge_id())); + + // make bridges uncongested to trigger resume signal + XcmOverBridge::on_bridge_messages_delivered( + expected_lane_id_1, + TestCongestionLimits::get().outbound_lane_uncongested_threshold, + ); + XcmOverBridge::on_bridge_messages_delivered( + expected_lane_id_2, + TestCongestionLimits::get().outbound_lane_uncongested_threshold, + ); + + // bridges are again opened + 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).is_none() + ); + assert!( + router_bridge_state::< + TestRuntime, + XcmOverBridgeByExportXcmRouterInstance + >(&dest).is_none() + ); + assert!(TestLocalXcmChannelManager::is_bridge_resumed(bridge_1.bridge_id())); + assert!(TestLocalXcmChannelManager::is_bridge_resumed(bridge_2.bridge_id())); + }) + } +} diff --git a/bridges/modules/xcm-bridge/src/lib.rs b/bridges/modules/xcm-bridge/src/lib.rs new file mode 100644 index 000000000000..1ffa64fdf9c4 --- /dev/null +++ b/bridges/modules/xcm-bridge/src/lib.rs @@ -0,0 +1,1892 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Module that adds XCM support to bridge pallets. The pallet allows to dynamically +//! open and close bridges between local (to this pallet location) and remote XCM +//! destinations. +//! +//! The `pallet_xcm_bridge_hub` pallet is used to manage (open, close) bridges between chains from +//! different consensuses. The new extrinsics `fn open_bridge` and `fn close_bridge` are introduced. +//! Other chains can manage channels with different bridged global consensuses. +//! +//! # Concept of `lane` and `LaneId` +//! +//! There is another `pallet_bridge_messages` pallet that handles inbound/outbound lanes for +//! messages. Each lane is a unique connection between two chains from different consensuses and is +//! identified by `LaneId`. `LaneId` is generated once when a new bridge is requested by `fn +//! open_bridge`. It is generated by `BridgeLocations::calculate_lane_id` based on the following +//! parameters: +//! - Source `bridge_origin_universal_location` (latest XCM) +//! - Destination `bridge_destination_universal_location` (latest XCM) +//! - XCM version (both sides of the bridge must use the same parameters to generate the same +//! `LaneId`) +//! - `bridge_origin_universal_location`, `bridge_destination_universal_location` is converted to +//! the `Versioned*` structs +//! +//! `LaneId` is expected to never change because: +//! - We need the same `LaneId` on both sides of the bridge, as `LaneId` is part of the message key +//! proofs. +//! - Runtime upgrades are entirely asynchronous. +//! - We already have a running production Polkadot/Kusama bridge that uses `LaneId([0, 0, 0, 0])`. +//! +//! `LaneId` is backward compatible, meaning it can be encoded/decoded from the older format `[u8; +//! 4]` used for static lanes, as well as the new format `H256` generated by +//! `BridgeLocations::calculate_lane_id`. +//! +//! # Concept of `bridge` and `BridgeId` +//! +//! The `pallet_xcm_bridge_hub` pallet needs to store some metadata about opened bridges. The bridge +//! (or bridge metadata) is stored under the `BridgeId` key. +//! +//! `BridgeId` is generated from `bridge_origin_relative_location` and +//! `bridge_origin_universal_location` using the `latest` XCM structs. `BridgeId` is not transferred +//! over the bridge; it is only important for local consensus. It essentially serves as an index/key +//! to bridge metadata. All the XCM infrastructure around `XcmExecutor`, `SendXcm`, `ExportXcm` use +//! the `latest` XCM, so `BridgeId` must remain compatible with the `latest` XCM. For example, we +//! have an `ExportXcm` implementation in `exporter.rs` that handles the `ExportMessage` instruction +//! with `universal_source` and `destination` (latest XCM), so we need to create `BridgeId` and the +//! corresponding `LaneId`. +//! +//! # Migrations and State +//! +//! This pallet implements `try_state`, ensuring compatibility and checking everything so we know if +//! any migration is needed. `do_try_state` checks for `BridgeId` compatibility, which is +//! recalculated on runtime upgrade. Upgrading to a new XCM version should not break anything, +//! except removing older XCM versions. In such cases, we need to add migration for `BridgeId` and +//! stored `Versioned*` structs and update `LaneToBridge` mapping, but this won't affect `LaneId` +//! over the bridge. +//! +//! # How to Open a Bridge? +//! +//! The `pallet_xcm_bridge_hub` pallet has the extrinsic `fn open_bridge` and an important +//! configuration `pallet_xcm_bridge::Config::OpenBridgeOrigin`, which translates the call's +//! origin to the XCM `Location` and converts it to the `bridge_origin_universal_location`. With the +//! current setup, this origin/location is expected to be either the relay chain or a sibling +//! parachain as one side of the bridge. Another parameter is +//! `bridge_destination_universal_location`, which is the other side of the bridge from a different +//! global consensus. +//! +//! Every bridge between two XCM locations has a dedicated lane in associated +//! messages pallet. Assuming that this pallet is deployed at the bridge hub +//! parachain and there's a similar pallet at the bridged network, the dynamic +//! bridge lifetime is as follows: +//! +//! 1) the sibling parachain opens a XCMP channel with this bridge hub; +//! +//! 2) the sibling parachain funds its sovereign parachain account at this bridge hub. It shall hold +//! enough funds to pay for the bridge (see `BridgeDeposit`); +//! +//! 3) the sibling parachain opens the bridge by sending XCM `Transact` instruction with the +//! `open_bridge` call. The `BridgeDeposit` amount is reserved on the sovereign account of +//! sibling parachain; +//! +//! 4) at the other side of the bridge, the same thing (1, 2, 3) happens. Parachains that need to +//! connect over the bridge need to coordinate the moment when they start sending messages over +//! the bridge. Otherwise they may lose messages and/or bundled assets; +//! +//! 5) when either side wants to close the bridge, it sends the XCM `Transact` with the +//! `close_bridge` call. The bridge is closed immediately if there are no queued messages. +//! Otherwise, the owner must repeat the `close_bridge` call to prune all queued messages first. +//! +//! The pallet doesn't provide any mechanism for graceful closure, because it always involves +//! some contract between two connected chains and the bridge hub knows nothing about that. It +//! is the task for the connected chains to make sure that all required actions are completed +//! before the closure. In the end, the bridge hub can't even guarantee that all messages that +//! are delivered to the destination, are processed in the way their sender expects. So if we +//! can't guarantee that, we shall not care about more complex procedures and leave it to the +//! participating parties. +//! +//! # Example +//! +//! Example of opening a bridge between some random parachains from Polkadot and Kusama: +//! +//! 0. Let's have: +//! - BridgeHubPolkadot with `UniversalLocation` = `[GlobalConsensus(Polkadot), Parachain(1002)]` +//! - BridgeHubKusama with `UniversalLocation` = `[GlobalConsensus(Kusama), Parachain(1002)]` +//! 1. The Polkadot local sibling parachain `Location::new(1, Parachain(1234))` must send some DOTs +//! to its sovereign account on BridgeHubPolkadot to cover `BridgeDeposit`, fees for `Transact`, +//! and the existential deposit. +//! 2. Send a call to the BridgeHubPolkadot from the local sibling parachain: `Location::new(1, +//! Parachain(1234))` ``` xcm::Transact( origin_kind: OriginKind::Xcm, +//! XcmOverBridgeHubKusama::open_bridge( VersionedInteriorLocation::V4([GlobalConsensus(Kusama), +//! Parachain(4567)].into()), ); ) ``` +//! 3. Check the stored bridge metadata and generated `LaneId`. +//! 4. The Kusama local sibling parachain `Location::new(1, Parachain(4567))` must send some KSMs to +//! its sovereign account +//! on BridgeHubKusama to cover `BridgeDeposit`, fees for `Transact`, and the existential deposit. +//! 5. Send a call to the BridgeHubKusama from the local sibling parachain: `Location::new(1, +//! Parachain(4567))` ``` xcm::Transact( origin_kind: OriginKind::Xcm, +//! XcmOverBridgeHubKusama::open_bridge( +//! VersionedInteriorLocation::V4([GlobalConsensus(Polkadot), Parachain(1234)].into()), ); ) ``` +//! 6. Check the stored bridge metadata and generated `LaneId`. +//! 7. Both `LaneId`s from steps 3 and 6 must be the same (see above _Concept of `lane` and +//! `LaneId`_). +//! 8. Run the bridge messages relayer for `LaneId`. +//! 9. Send messages from both sides. +//! +//! The opening bridge holds the configured `BridgeDeposit` from the origin's sovereign account, but +//! this deposit is returned when the bridge is closed with `fn close_bridge`. + +#![warn(missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] + +use bp_messages::{LaneState, MessageNonce}; +use bp_runtime::{AccountIdOf, BalanceOf, RangeInclusiveExt}; +pub use bp_xcm_bridge::{Bridge, BridgeId, BridgeState, Receiver}; +use bp_xcm_bridge::{ + BridgeLocations, BridgeLocationsError, ChannelStatusProvider as DispatchChannelStatusProvider, + Deposit, DepositOf, LocalXcmChannelManager, +}; +use frame_support::{traits::fungible::MutateHold, DefaultNoBound}; +use frame_system::Config as SystemConfig; +use pallet_bridge_messages::{Config as BridgeMessagesConfig, LanesManagerError}; +use sp_std::{boxed::Box, vec::Vec}; +use xcm::prelude::*; +use xcm_builder::DispatchBlob; +use xcm_executor::traits::ConvertLocation; + +pub use bp_xcm_bridge::XcmAsPlainPayload; +pub use dispatcher::XcmBlobMessageDispatchResult; +pub use exporter::PalletAsHaulBlobExporter; +pub use pallet::*; + +pub mod benchmarking; +pub mod congestion; +mod dispatcher; +mod exporter; +pub mod migration; +mod mock; +pub use weights::WeightInfo; +pub mod weights; + +/// The target that will be used when publishing logs related to this pallet. +pub const LOG_TARGET: &str = "runtime::bridge-xcm"; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::{ + pallet_prelude::*, + traits::{tokens::Precision, Contains}, + }; + use frame_system::pallet_prelude::{BlockNumberFor, *}; + + /// The reason for this pallet placing a hold on funds. + #[pallet::composite_enum] + pub enum HoldReason { + /// The funds are held as a deposit for opened bridge. + #[codec(index = 0)] + BridgeDeposit, + } + + #[pallet::config] + #[pallet::disable_frame_system_supertrait_check] + pub trait Config: + BridgeMessagesConfig + { + /// The overarching event type. + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + /// Benchmarks results from runtime we're plugged into. + type WeightInfo: WeightInfo; + + /// Runtime's universal location. + type UniversalLocation: Get; + // TODO: https://github.com/paritytech/parity-bridges-common/issues/1666 remove `ChainId` and + // replace it with the `NetworkId` - then we'll be able to use + // `T as pallet_bridge_messages::Config::BridgedChain::NetworkId` + /// Bridged network as relative location of bridged `GlobalConsensus`. + #[pallet::constant] + type BridgedNetwork: Get; + /// Associated messages pallet instance that bridges us with the + /// `BridgedNetworkId` consensus. + type BridgeMessagesPalletInstance: 'static; + + /// Price of single message export to the bridged consensus (`Self::BridgedNetwork`). + type MessageExportPrice: Get; + /// Checks the XCM version for the destination. + type DestinationVersion: GetVersion; + + /// The origin that is allowed to call privileged operations on the pallet, e.g. open/close + /// bridge for locations. + type ForceOrigin: EnsureOrigin<::RuntimeOrigin>; + /// A set of XCM locations within local consensus system that are allowed to open + /// bridges with remote destinations. + type OpenBridgeOrigin: EnsureOrigin< + ::RuntimeOrigin, + Success = Location, + >; + /// A converter between a location and a sovereign account. + type BridgeOriginAccountIdConverter: ConvertLocation>>; + + /// Amount of this chain native tokens that is reserved on the sibling parachain account + /// when bridge open request is registered. + #[pallet::constant] + type BridgeDeposit: Get>>; + /// Currency used to pay for bridge registration. + type Currency: MutateHold< + AccountIdOf>, + Balance = BalanceOf>, + Reason = Self::RuntimeHoldReason, + >; + /// The overarching runtime hold reason. + type RuntimeHoldReason: From>; + /// Do not hold `Self::BridgeDeposit` for the location of `Self::OpenBridgeOrigin`. + /// For example, it is possible to make an exception for a system parachain or relay. + type AllowWithoutBridgeDeposit: Contains; + + /// Local XCM channel manager. Dedicated to exporting capabilities when handling congestion + /// with the sending side. + type LocalXcmChannelManager: LocalXcmChannelManager; + /// XCM-level dispatcher for inbound bridge messages. + type BlobDispatcher: DispatchBlob + DispatchChannelStatusProvider; + /// Limits for handling congestion. + type CongestionLimits: Get; + } + + /// An alias for the bridge metadata. + pub type BridgeOf = Bridge, LaneIdOf>; + /// An alias for this chain. + pub type ThisChainOf = + pallet_bridge_messages::ThisChainOf>::BridgeMessagesPalletInstance>; + /// An alias for lane identifier type. + pub type LaneIdOf = + >::BridgeMessagesPalletInstance>>::LaneId; + /// An alias for the associated lanes manager. + pub type LanesManagerOf = + pallet_bridge_messages::LanesManager>::BridgeMessagesPalletInstance>; + /// Weight info of the given pallet. + pub type WeightInfoOf = >::WeightInfo; + + #[pallet::pallet] + #[pallet::storage_version(migration::STORAGE_VERSION)] + pub struct Pallet(PhantomData<(T, I)>); + + #[pallet::hooks] + impl, I: 'static> Hooks> for Pallet { + fn integrity_test() { + assert!( + Self::bridged_network_id().is_ok(), + "Configured `T::BridgedNetwork`: {:?} does not contain `GlobalConsensus` junction with `NetworkId`!", + T::BridgedNetwork::get() + ); + assert!( + T::CongestionLimits::get().is_valid(), + "Configured `T::CongestionLimits`: {:?} are not valid!", + T::CongestionLimits::get() + ); + } + + #[cfg(feature = "try-runtime")] + fn try_state(_n: BlockNumberFor) -> Result<(), sp_runtime::TryRuntimeError> { + Self::do_try_state() + } + } + + #[pallet::call] + impl, I: 'static> Pallet { + /// Open a bridge between two locations. + /// + /// The caller must be within the `T::OpenBridgeOrigin` filter (presumably: a sibling + /// parachain or a parent relay chain). The `bridge_destination_universal_location` must be + /// a destination within the consensus of the `T::BridgedNetwork` network. + /// + /// The `BridgeDeposit` amount is reserved on the caller account. This deposit + /// is unreserved after bridge is closed. + /// + /// The states after this call: bridge is `Opened`, outbound lane is `Opened`, inbound lane + /// is `Opened`. + /// + /// Optional `maybe_notify` holds data about the `bridge_origin_relative_location` where + /// notifications can be sent to handle congestion. The notification contains an encoded + /// tuple `(BridgeId, bool)`, where `bool` is the `is_congested` flag. + #[pallet::call_index(0)] + #[pallet::weight(WeightInfoOf::::open_bridge())] + pub fn open_bridge( + origin: OriginFor, + bridge_destination_universal_location: Box, + maybe_notify: Option, + ) -> DispatchResult { + // check and compute required bridge locations and laneId + let xcm_version = bridge_destination_universal_location.identify_version(); + let locations = + Self::bridge_locations_from_origin(origin, bridge_destination_universal_location)?; + let lane_id = locations.calculate_lane_id(xcm_version).map_err(|e| { + log::trace!( + target: LOG_TARGET, + "calculate_lane_id error: {e:?}", + ); + Error::::BridgeLocations(e) + })?; + + Self::do_open_bridge(locations, lane_id, true, maybe_notify) + } + + /// Try to close the bridge. + /// + /// Can only be called by the "owner" of this side of the bridge, meaning that the + /// inbound XCM channel with the local origin chain is working. + /// + /// Closed bridge is a bridge without any traces in the runtime storage. So this method + /// first tries to prune all queued messages at the outbound lane. When there are no + /// outbound messages left, outbound and inbound lanes are purged. After that, funds + /// are returned back to the owner of this side of the bridge. + /// + /// The number of messages that we may prune in a single call is limited by the + /// `may_prune_messages` argument. If there are more messages in the queue, the method + /// prunes exactly `may_prune_messages` and exits early. The caller may call it again + /// until outbound queue is depleted and get his funds back. + /// + /// The states after this call: everything is either `Closed`, or purged from the + /// runtime storage. + #[pallet::call_index(1)] + #[pallet::weight(WeightInfoOf::::close_bridge())] + pub fn close_bridge( + origin: OriginFor, + bridge_destination_universal_location: Box, + may_prune_messages: MessageNonce, + ) -> DispatchResult { + // compute required bridge locations + let locations = + Self::bridge_locations_from_origin(origin, bridge_destination_universal_location)?; + + // TODO: https://github.com/paritytech/parity-bridges-common/issues/1760 - may do refund here, if + // bridge/lanes are already closed + for messages that are not pruned + + // update bridge metadata - this also guarantees that the bridge is in the proper state + let bridge = + Bridges::::try_mutate_exists(locations.bridge_id(), |bridge| match bridge { + Some(bridge) => { + bridge.state = BridgeState::Closed; + Ok(bridge.clone()) + }, + None => Err(Error::::UnknownBridge), + })?; + + // close inbound and outbound lanes + let lanes_manager = LanesManagerOf::::new(); + let mut inbound_lane = lanes_manager + .any_state_inbound_lane(bridge.lane_id) + .map_err(Error::::LanesManager)?; + let mut outbound_lane = lanes_manager + .any_state_outbound_lane(bridge.lane_id) + .map_err(Error::::LanesManager)?; + + // now prune queued messages + let mut pruned_messages = 0; + for _ in outbound_lane.queued_messages() { + if pruned_messages == may_prune_messages { + break + } + + outbound_lane.remove_oldest_unpruned_message(); + pruned_messages += 1; + } + + // if there are outbound messages in the queue, just update states and early exit + if !outbound_lane.queued_messages().is_empty() { + // update lanes state. Under normal circumstances, following calls shall never fail + inbound_lane.set_state(LaneState::Closed); + outbound_lane.set_state(LaneState::Closed); + + // write something to log + let enqueued_messages = outbound_lane.queued_messages().saturating_len(); + log::trace!( + target: LOG_TARGET, + "Bridge {:?} between {:?} and {:?} is closing lane_id: {:?}. {} messages remaining", + locations.bridge_id(), + locations.bridge_origin_universal_location(), + locations.bridge_destination_universal_location(), + bridge.lane_id, + enqueued_messages, + ); + + // deposit the `ClosingBridge` event + Self::deposit_event(Event::::ClosingBridge { + bridge_id: *locations.bridge_id(), + lane_id: bridge.lane_id.into(), + pruned_messages, + enqueued_messages, + }); + + return Ok(()) + } + + // else we have pruned all messages, so lanes and the bridge itself may gone + inbound_lane.purge(); + outbound_lane.purge(); + Bridges::::remove(locations.bridge_id()); + LaneToBridge::::remove(bridge.lane_id); + + // return deposit + let released_deposit = if let Some(deposit) = bridge.deposit { + T::Currency::release( + &HoldReason::BridgeDeposit.into(), + &deposit.account, + deposit.amount, + Precision::BestEffort, + ) + .inspect_err(|e| { + // we can't do anything here - looks like funds have been (partially) unreserved + // before by someone else. Let's not fail, though - it'll be worse for the + // caller + log::error!( + target: LOG_TARGET, + "Failed to unreserve during the bridge {:?} closure with error: {e:?}", + locations.bridge_id(), + ); + }) + .map(|released| Deposit::new(deposit.account, released)) + .ok() + } else { + None + }; + + // write something to log + log::trace!( + target: LOG_TARGET, + "Bridge {:?} between {:?} and {:?} has closed lane_id: {:?}, the bridge deposit {released_deposit:?} was returned", + locations.bridge_id(), + bridge.lane_id, + locations.bridge_origin_universal_location(), + locations.bridge_destination_universal_location(), + ); + + // deposit the `BridgePruned` event + Self::deposit_event(Event::::BridgePruned { + bridge_id: *locations.bridge_id(), + lane_id: bridge.lane_id.into(), + bridge_deposit: released_deposit, + pruned_messages, + }); + + Ok(()) + } + + /// Attempts to update the `maybe_notify` callback for receiving congestion notifications. + /// + /// This can only be called by the "owner" of this side of the bridge, which means that the + /// inbound XCM channel with the local origin chain is functional. + #[pallet::call_index(2)] + #[pallet::weight(WeightInfoOf::::update_notification_receiver())] + pub fn update_notification_receiver( + origin: OriginFor, + bridge_destination_universal_location: Box, + maybe_notify: Option, + ) -> DispatchResult { + let locations = + Self::bridge_locations_from_origin(origin, bridge_destination_universal_location)?; + Bridges::::try_mutate_exists(locations.bridge_id(), |bridge| match bridge { + Some(b) => { + b.maybe_notify = maybe_notify; + + Self::deposit_event(Event::::NotificationReceiverUpdated { + bridge_id: *locations.bridge_id(), + maybe_notify: b.maybe_notify.clone(), + }); + Ok(()) + }, + None => Err(Error::::UnknownBridge.into()), + }) + } + } + + impl, I: 'static> Pallet { + /// Open bridge for lane. + pub fn do_open_bridge( + locations: Box, + lane_id: T::LaneId, + create_lanes: bool, + maybe_notify: Option, + ) -> Result<(), DispatchError> { + // reserve balance (if needed) + let deposit = if T::AllowWithoutBridgeDeposit::contains( + locations.bridge_origin_relative_location(), + ) { + None + } else { + // get origin's sovereign account + let bridge_owner_account = T::BridgeOriginAccountIdConverter::convert_location( + locations.bridge_origin_relative_location(), + ) + .ok_or(Error::::InvalidBridgeOriginAccount)?; + + let deposit = T::BridgeDeposit::get(); + T::Currency::hold( + &HoldReason::BridgeDeposit.into(), + &bridge_owner_account, + deposit, + ) + .map_err(|e| { + log::error!( + target: LOG_TARGET, + "Failed to hold bridge deposit: {deposit:?} \ + from bridge_owner_account: {bridge_owner_account:?} derived from \ + bridge_origin_relative_location: {:?} with error: {e:?}", + locations.bridge_origin_relative_location(), + ); + Error::::FailedToReserveBridgeDeposit + })?; + Some(Deposit::new(bridge_owner_account, deposit)) + }; + + // save bridge metadata + Bridges::::try_mutate(locations.bridge_id(), |bridge| match bridge { + Some(_) => Err(Error::::BridgeAlreadyExists), + None => { + *bridge = Some(BridgeOf:: { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + deposit: deposit.clone(), + lane_id, + maybe_notify, + }); + Ok(()) + }, + })?; + // save lane to bridge mapping + LaneToBridge::::try_mutate(lane_id, |bridge| match bridge { + Some(_) => Err(Error::::BridgeAlreadyExists), + None => { + *bridge = Some(*locations.bridge_id()); + Ok(()) + }, + })?; + + if create_lanes { + // create new lanes. Under normal circumstances, following calls shall never fail + let lanes_manager = LanesManagerOf::::new(); + lanes_manager + .create_inbound_lane(lane_id) + .map_err(Error::::LanesManager)?; + lanes_manager + .create_outbound_lane(lane_id) + .map_err(Error::::LanesManager)?; + } + + // write something to log + log::trace!( + target: LOG_TARGET, + "Bridge {:?} between {:?} and {:?} has been opened using lane_id: {lane_id:?}", + locations.bridge_id(), + locations.bridge_origin_universal_location(), + locations.bridge_destination_universal_location(), + ); + + // deposit `BridgeOpened` event + Self::deposit_event(Event::::BridgeOpened { + bridge_id: *locations.bridge_id(), + bridge_deposit: deposit, + local_endpoint: Box::new(locations.bridge_origin_universal_location().clone()), + remote_endpoint: Box::new( + locations.bridge_destination_universal_location().clone(), + ), + lane_id: lane_id.into(), + }); + + Ok(()) + } + } + + impl, I: 'static> Pallet { + /// Return bridge endpoint locations and dedicated lane identifier. This method converts + /// runtime `origin` argument to relative `Location` using the `T::OpenBridgeOrigin` + /// converter. + pub fn bridge_locations_from_origin( + origin: OriginFor, + bridge_destination_universal_location: Box, + ) -> Result, sp_runtime::DispatchError> { + Self::bridge_locations( + T::OpenBridgeOrigin::ensure_origin(origin)?, + (*bridge_destination_universal_location) + .try_into() + .map_err(|_| Error::::UnsupportedXcmVersion)?, + ) + } + + /// Return bridge endpoint locations and dedicated **bridge** identifier (`BridgeId`). + pub fn bridge_locations( + bridge_origin_relative_location: Location, + bridge_destination_universal_location: InteriorLocation, + ) -> Result, sp_runtime::DispatchError> { + BridgeLocations::bridge_locations( + T::UniversalLocation::get(), + bridge_origin_relative_location, + bridge_destination_universal_location, + Self::bridged_network_id()?, + ) + .map_err(|e| { + log::trace!( + target: LOG_TARGET, + "bridge_locations error: {e:?}", + ); + Error::::BridgeLocations(e).into() + }) + } + + /// Return bridge metadata by bridge_id + pub fn bridge(bridge_id: &BridgeId) -> Option> { + Bridges::::get(bridge_id) + } + + /// Return bridge metadata by lane_id + pub fn bridge_by_lane_id(lane_id: &T::LaneId) -> Option<(BridgeId, BridgeOf)> { + LaneToBridge::::get(lane_id) + .and_then(|bridge_id| Self::bridge(&bridge_id).map(|bridge| (bridge_id, bridge))) + } + } + + impl, I: 'static> Pallet { + /// Returns some `NetworkId` if contains `GlobalConsensus` junction. + pub fn bridged_network_id() -> Result { + match T::BridgedNetwork::get().take_first_interior() { + Some(GlobalConsensus(network)) => Ok(network), + _ => Err(Error::::BridgeLocations( + BridgeLocationsError::InvalidBridgeDestination, + ) + .into()), + } + } + } + + #[cfg(feature = "runtime-benchmarks")] + impl, I: 'static> Pallet { + /// Open bridge for lane. + pub fn open_bridge_for_benchmarks( + lane_id: LaneIdOf, + bridge_origin_relative_location: Location, + bridge_destination_universal_location: InteriorLocation, + create_lanes: bool, + maybe_notify: Option, + initial_balance: impl Fn() -> BalanceOf>, + ) -> Result, sp_runtime::DispatchError> { + let locations = Pallet::::bridge_locations( + bridge_origin_relative_location.into(), + bridge_destination_universal_location.into(), + )?; + + if !T::AllowWithoutBridgeDeposit::contains(locations.bridge_origin_relative_location()) + { + let account_id = T::BridgeOriginAccountIdConverter::convert_location( + locations.bridge_origin_relative_location(), + ) + .ok_or(Error::::InvalidBridgeOriginAccount)?; + + use frame_support::traits::fungible::Unbalanced; + use sp_runtime::Saturating; + T::Currency::increase_balance( + &account_id, + initial_balance().saturating_add(T::BridgeDeposit::get()), + Precision::BestEffort, + )?; + } + + Pallet::::do_open_bridge(locations.clone(), lane_id, create_lanes, maybe_notify)?; + + Ok(locations) + } + } + + #[cfg(any(test, feature = "try-runtime", feature = "std"))] + impl, I: 'static> Pallet { + /// Ensure the correctness of the state of this pallet. + pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> { + use sp_std::collections::btree_set::BTreeSet; + + let mut lanes = BTreeSet::new(); + + // check all known bridge configurations + for (bridge_id, bridge) in Bridges::::iter() { + lanes.insert(Self::do_try_state_for_bridge(bridge_id, bridge)?); + } + ensure!( + lanes.len() == Bridges::::iter().count(), + "Invalid `Bridges` configuration, probably two bridges handle the same laneId!" + ); + ensure!( + lanes.len() == LaneToBridge::::iter().count(), + "Invalid `LaneToBridge` configuration, probably missing or not removed laneId!" + ); + + // check connected `pallet_bridge_messages` state. + Self::do_try_state_for_messages() + } + + /// Ensure the correctness of the state of the bridge. + pub fn do_try_state_for_bridge( + bridge_id: BridgeId, + bridge: BridgeOf, + ) -> Result { + log::info!(target: LOG_TARGET, "Checking `do_try_state_for_bridge` for bridge_id: {bridge_id:?} and bridge: {bridge:?}"); + + // check `BridgeId` points to the same `LaneId` and vice versa. + ensure!( + Some(bridge_id) == LaneToBridge::::get(bridge.lane_id), + "Found `LaneToBridge` inconsistency for bridge_id - missing mapping!" + ); + + // check `pallet_bridge_messages` state for that `LaneId`. + let lanes_manager = LanesManagerOf::::new(); + ensure!( + lanes_manager.any_state_inbound_lane(bridge.lane_id).is_ok(), + "Inbound lane not found!", + ); + ensure!( + lanes_manager.any_state_outbound_lane(bridge.lane_id).is_ok(), + "Outbound lane not found!", + ); + + // check that `locations` are convertible to the `latest` XCM. + let bridge_origin_relative_location_as_latest: &Location = + bridge.bridge_origin_relative_location.try_as().map_err(|_| { + "`bridge.bridge_origin_relative_location` cannot be converted to the `latest` XCM, needs migration!" + })?; + let bridge_origin_universal_location_as_latest: &InteriorLocation = bridge.bridge_origin_universal_location + .try_as() + .map_err(|_| "`bridge.bridge_origin_universal_location` cannot be converted to the `latest` XCM, needs migration!")?; + let bridge_destination_universal_location_as_latest: &InteriorLocation = bridge.bridge_destination_universal_location + .try_as() + .map_err(|_| "`bridge.bridge_destination_universal_location` cannot be converted to the `latest` XCM, needs migration!")?; + + // check `BridgeId` does not change + ensure!( + bridge_id == BridgeId::new(bridge_origin_universal_location_as_latest, bridge_destination_universal_location_as_latest), + "`bridge_id` is different than calculated from `bridge_origin_universal_location_as_latest` and `bridge_destination_universal_location_as_latest`, needs migration!" + ); + + // check bridge account owner + if let Some(deposit) = bridge.deposit { + ensure!( + T::BridgeOriginAccountIdConverter::convert_location(bridge_origin_relative_location_as_latest) == Some(deposit.account), + "`bridge.deposit.account` is different than calculated from `bridge.bridge_origin_relative_location`, needs migration!" + ); + } + + Ok(bridge.lane_id) + } + + /// Ensure the correctness of the state of the connected `pallet_bridge_messages` instance. + pub fn do_try_state_for_messages() -> Result<(), sp_runtime::TryRuntimeError> { + // check that all `InboundLanes` laneIds have mapping to some bridge. + for lane_id in pallet_bridge_messages::InboundLanes::::iter_keys() { + log::info!(target: LOG_TARGET, "Checking `do_try_state_for_messages` for `InboundLanes`'s lane_id: {lane_id:?}..."); + ensure!( + LaneToBridge::::get(lane_id).is_some(), + "Found `LaneToBridge` inconsistency for `InboundLanes`'s lane_id - missing mapping!" + ); + } + + // check that all `OutboundLanes` laneIds have mapping to some bridge. + for lane_id in pallet_bridge_messages::OutboundLanes::::iter_keys() { + log::info!(target: LOG_TARGET, "Checking `do_try_state_for_messages` for `OutboundLanes`'s lane_id: {lane_id:?}..."); + ensure!( + LaneToBridge::::get(lane_id).is_some(), + "Found `LaneToBridge` inconsistency for `OutboundLanes`'s lane_id - missing mapping!" + ); + } + + Ok(()) + } + } + + /// All registered bridges. + #[pallet::storage] + pub type Bridges, I: 'static = ()> = + StorageMap<_, Identity, BridgeId, BridgeOf>; + /// All registered `lane_id` and `bridge_id` mappings. + #[pallet::storage] + pub type LaneToBridge, I: 'static = ()> = + StorageMap<_, Identity, T::LaneId, BridgeId>; + + #[pallet::genesis_config] + #[derive(DefaultNoBound)] + pub struct GenesisConfig, I: 'static = ()> { + /// Opened bridges. + /// + /// Keep in mind that we are **NOT** reserving any amount for the bridges opened at + /// genesis. We are **NOT** opening lanes, used by this bridge. It all must be done using + /// other pallets genesis configuration or some other means. + pub opened_bridges: Vec<(Location, InteriorLocation, Option, Option)>, + /// Dummy marker. + #[serde(skip)] + pub _phantom: sp_std::marker::PhantomData<(T, I)>, + } + + #[pallet::genesis_build] + impl, I: 'static> BuildGenesisConfig for GenesisConfig + where + T: frame_system::Config>>, + { + fn build(&self) { + for ( + bridge_origin_relative_location, + bridge_destination_universal_location, + maybe_lane_id, + maybe_notify, + ) in &self.opened_bridges + { + let locations = Pallet::::bridge_locations( + bridge_origin_relative_location.clone(), + bridge_destination_universal_location.clone().into(), + ) + .expect("Invalid genesis configuration"); + + let lane_id = match maybe_lane_id { + Some(lane_id) => *lane_id, + None => + locations.calculate_lane_id(xcm::latest::VERSION).expect("Valid locations"), + }; + + Pallet::::do_open_bridge(locations, lane_id, true, maybe_notify.clone()) + .expect("Valid opened bridge!"); + } + } + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// The bridge between two locations has been opened. + BridgeOpened { + /// Bridge identifier. + bridge_id: BridgeId, + /// Bridge deposit held. + bridge_deposit: Option>>, + + /// Universal location of local bridge endpoint. + local_endpoint: Box, + /// Universal location of remote bridge endpoint. + remote_endpoint: Box, + /// Lane identifier. + lane_id: T::LaneId, + }, + /// Bridge is going to be closed, but not yet fully pruned from the runtime storage. + ClosingBridge { + /// Bridge identifier. + bridge_id: BridgeId, + /// Lane identifier. + lane_id: T::LaneId, + /// Number of pruned messages during the close call. + pruned_messages: MessageNonce, + /// Number of enqueued messages that need to be pruned in follow up calls. + enqueued_messages: MessageNonce, + }, + /// Bridge has been closed and pruned from the runtime storage. It now may be reopened + /// again by any participant. + BridgePruned { + /// Bridge identifier. + bridge_id: BridgeId, + /// Lane identifier. + lane_id: T::LaneId, + /// Amount of deposit released. + bridge_deposit: Option>>, + /// Number of pruned messages during the close call. + pruned_messages: MessageNonce, + }, + /// The bridge has been updated with a new notification receiver. + NotificationReceiverUpdated { + /// Bridge identifier. + bridge_id: BridgeId, + /// Updated notification receiver. + maybe_notify: Option, + }, + } + + #[pallet::error] + pub enum Error { + /// Bridge locations error. + BridgeLocations(BridgeLocationsError), + /// Invalid local bridge origin account. + InvalidBridgeOriginAccount, + /// The bridge is already registered in this pallet. + BridgeAlreadyExists, + /// The local origin already owns a maximal number of bridges. + TooManyBridgesForLocalOrigin, + /// Trying to close already closed bridge. + BridgeAlreadyClosed, + /// Lanes manager error. + LanesManager(LanesManagerError), + /// Trying to access unknown bridge. + UnknownBridge, + /// The bridge origin can't pay the required amount for opening the bridge. + FailedToReserveBridgeDeposit, + /// The version of XCM location argument is unsupported. + UnsupportedXcmVersion, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use bp_messages::LaneIdType; + use mock::*; + + use frame_support::{assert_err, assert_noop, assert_ok, BoundedVec}; + use frame_system::{EventRecord, Phase}; + use sp_runtime::{traits::Zero, TryRuntimeError}; + + fn mock_open_bridge_from_with( + origin: RuntimeOrigin, + deposit: Option, + with: InteriorLocation, + ) -> (BridgeOf, BridgeLocations) { + let locations = + XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap(); + let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + + let deposit = deposit.map(|deposit| { + let bridge_owner_account = + fund_origin_sovereign_account(&locations, deposit + ExistentialDeposit::get()); + Balances::hold(&HoldReason::BridgeDeposit.into(), &bridge_owner_account, deposit) + .unwrap(); + Deposit::new(bridge_owner_account, deposit) + }); + + let bridge = Bridge { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + deposit, + lane_id, + maybe_notify: None, + }; + Bridges::::insert(locations.bridge_id(), bridge.clone()); + LaneToBridge::::insert(bridge.lane_id, locations.bridge_id()); + + let lanes_manager = LanesManagerOf::::new(); + lanes_manager.create_inbound_lane(bridge.lane_id).unwrap(); + lanes_manager.create_outbound_lane(bridge.lane_id).unwrap(); + + assert_ok!(XcmOverBridge::do_try_state()); + + (bridge, *locations) + } + + fn mock_open_bridge_from( + origin: RuntimeOrigin, + deposit: Option, + ) -> (BridgeOf, BridgeLocations) { + mock_open_bridge_from_with(origin, deposit, bridged_asset_hub_universal_location()) + } + + fn enqueue_message(lane: TestLaneIdType) { + let lanes_manager = LanesManagerOf::::new(); + lanes_manager + .active_outbound_lane(lane) + .unwrap() + .send_message(BoundedVec::try_from(vec![42]).expect("We craft valid messages")); + } + + #[test] + fn open_bridge_fails_if_origin_is_not_allowed() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::disallowed_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + None, + ), + sp_runtime::DispatchError::BadOrigin, + ); + }) + } + + #[test] + fn open_bridge_fails_if_origin_is_not_relative() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::parent_relay_chain_universal_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + None, + ), + Error::::BridgeLocations( + BridgeLocationsError::InvalidBridgeOrigin + ), + ); + + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::sibling_parachain_universal_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + None, + ), + Error::::BridgeLocations( + BridgeLocationsError::InvalidBridgeOrigin + ), + ); + }) + } + + #[test] + fn open_bridge_fails_if_destination_is_not_remote() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::parent_relay_chain_origin(), + Box::new( + [GlobalConsensus(RelayNetwork::get()), Parachain(BRIDGED_ASSET_HUB_ID)] + .into() + ), + None, + ), + Error::::BridgeLocations(BridgeLocationsError::DestinationIsLocal), + ); + }); + } + + #[test] + fn open_bridge_fails_if_outside_of_bridged_consensus() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::parent_relay_chain_origin(), + Box::new( + [ + GlobalConsensus(NonBridgedRelayNetwork::get()), + Parachain(BRIDGED_ASSET_HUB_ID) + ] + .into() + ), + None, + ), + Error::::BridgeLocations( + BridgeLocationsError::UnreachableDestination + ), + ); + }); + } + + #[test] + fn open_bridge_fails_if_origin_has_no_sovereign_account() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::origin_without_sovereign_account(), + Box::new(bridged_asset_hub_universal_location().into()), + None, + ), + Error::::InvalidBridgeOriginAccount, + ); + }); + } + + #[test] + fn open_bridge_fails_if_origin_sovereign_account_has_no_enough_funds() { + run_test(|| { + assert_noop!( + XcmOverBridge::open_bridge( + OpenBridgeOrigin::sibling_parachain_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + None, + ), + Error::::FailedToReserveBridgeDeposit, + ); + }); + } + + #[test] + fn open_bridge_fails_if_it_already_exists() { + run_test(|| { + let origin = OpenBridgeOrigin::parent_relay_chain_origin(); + let locations = XcmOverBridge::bridge_locations_from_origin( + origin.clone(), + Box::new(bridged_asset_hub_universal_location().into()), + ) + .unwrap(); + let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + + Bridges::::insert( + locations.bridge_id(), + Bridge { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into(), + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + deposit: None, + lane_id, + maybe_notify: None, + }, + ); + + assert_noop!( + XcmOverBridge::open_bridge( + origin, + Box::new(bridged_asset_hub_universal_location().into()), + None, + ), + Error::::BridgeAlreadyExists, + ); + }) + } + + #[test] + fn open_bridge_fails_if_its_lanes_already_exists() { + run_test(|| { + let origin = OpenBridgeOrigin::parent_relay_chain_origin(); + let locations = XcmOverBridge::bridge_locations_from_origin( + origin.clone(), + Box::new(bridged_asset_hub_universal_location().into()), + ) + .unwrap(); + let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap(); + fund_origin_sovereign_account( + &locations, + BridgeDeposit::get() + ExistentialDeposit::get(), + ); + + let lanes_manager = LanesManagerOf::::new(); + + lanes_manager.create_inbound_lane(lane_id).unwrap(); + assert_noop!( + XcmOverBridge::open_bridge( + origin.clone(), + Box::new(bridged_asset_hub_universal_location().into()), + None, + ), + Error::::LanesManager(LanesManagerError::InboundLaneAlreadyExists), + ); + + lanes_manager.active_inbound_lane(lane_id).unwrap().purge(); + lanes_manager.create_outbound_lane(lane_id).unwrap(); + assert_noop!( + XcmOverBridge::open_bridge( + origin, + Box::new(bridged_asset_hub_universal_location().into()), + None, + ), + Error::::LanesManager( + LanesManagerError::OutboundLaneAlreadyExists + ), + ); + }) + } + + #[test] + fn open_bridge_works() { + run_test(|| { + // in our test runtime, we expect that bridge may be opened by parent relay chain, + // any sibling parachain or local root + let origins = [ + (OpenBridgeOrigin::parent_relay_chain_origin(), None), + (OpenBridgeOrigin::sibling_parachain_origin(), Some(BridgeDeposit::get())), + (RuntimeOrigin::root(), None), + ]; + + // check that every origin may open the bridge + let lanes_manager = LanesManagerOf::::new(); + let existential_deposit = ExistentialDeposit::get(); + for (origin, expected_deposit_amount) in origins { + // reset events + System::set_block_number(1); + System::reset_events(); + + // compute all other locations + let xcm_version = xcm::latest::VERSION; + let locations = XcmOverBridge::bridge_locations_from_origin( + origin.clone(), + Box::new( + VersionedInteriorLocation::from(bridged_asset_hub_universal_location()) + .into_version(xcm_version) + .expect("valid conversion"), + ), + ) + .unwrap(); + let lane_id = locations.calculate_lane_id(xcm_version).unwrap(); + + // ensure that there's no bridge and lanes in the storage + assert_eq!(Bridges::::get(locations.bridge_id()), None); + assert_eq!( + lanes_manager.active_inbound_lane(lane_id).map(drop), + Err(LanesManagerError::UnknownInboundLane) + ); + assert_eq!( + lanes_manager.active_outbound_lane(lane_id).map(drop), + Err(LanesManagerError::UnknownOutboundLane) + ); + assert_eq!(LaneToBridge::::get(lane_id), None); + + // give enough funds to the sovereign account of the bridge origin + let expected_deposit = expected_deposit_amount.map(|deposit_amount| { + let bridge_owner_account = fund_origin_sovereign_account( + &locations, + deposit_amount + existential_deposit, + ); + assert_eq!( + Balances::free_balance(&bridge_owner_account), + deposit_amount + existential_deposit + ); + assert_eq!(Balances::reserved_balance(&bridge_owner_account), 0); + Deposit::new(bridge_owner_account, deposit_amount) + }); + + let maybe_notify = Some(Receiver::new(13, 15)); + + // now open the bridge + assert_ok!(XcmOverBridge::open_bridge( + origin, + Box::new(locations.bridge_destination_universal_location().clone().into()), + maybe_notify.clone(), + )); + + // ensure that everything has been set up in the runtime storage + assert_eq!( + Bridges::::get(locations.bridge_id()), + Some(Bridge { + bridge_origin_relative_location: Box::new( + locations.bridge_origin_relative_location().clone().into() + ), + bridge_origin_universal_location: Box::new( + locations.bridge_origin_universal_location().clone().into(), + ), + bridge_destination_universal_location: Box::new( + locations.bridge_destination_universal_location().clone().into(), + ), + state: BridgeState::Opened, + deposit: expected_deposit.clone(), + lane_id, + maybe_notify, + }), + ); + assert_eq!( + lanes_manager.active_inbound_lane(lane_id).map(|l| l.state()), + Ok(LaneState::Opened) + ); + assert_eq!( + lanes_manager.active_outbound_lane(lane_id).map(|l| l.state()), + Ok(LaneState::Opened) + ); + assert_eq!( + LaneToBridge::::get(lane_id), + Some(*locations.bridge_id()) + ); + if let Some(expected_deposit) = expected_deposit.as_ref() { + assert_eq!( + Balances::free_balance(&expected_deposit.account), + existential_deposit + ); + assert_eq!( + Balances::reserved_balance(&expected_deposit.account), + expected_deposit.amount + ); + } + + // ensure that the proper event is deposited + assert_eq!( + System::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmOverBridge(Event::BridgeOpened { + bridge_id: *locations.bridge_id(), + bridge_deposit: expected_deposit, + local_endpoint: Box::new( + locations.bridge_origin_universal_location().clone() + ), + remote_endpoint: Box::new( + locations.bridge_destination_universal_location().clone() + ), + lane_id: lane_id.into() + }), + topics: vec![], + }), + ); + + // check state + assert_ok!(XcmOverBridge::do_try_state()); + } + }); + } + + #[test] + fn close_bridge_fails_if_origin_is_not_allowed() { + run_test(|| { + assert_noop!( + XcmOverBridge::close_bridge( + OpenBridgeOrigin::disallowed_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + 0, + ), + sp_runtime::DispatchError::BadOrigin, + ); + }) + } + + #[test] + fn close_bridge_fails_if_origin_is_not_relative() { + run_test(|| { + assert_noop!( + XcmOverBridge::close_bridge( + OpenBridgeOrigin::parent_relay_chain_universal_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + 0, + ), + Error::::BridgeLocations( + BridgeLocationsError::InvalidBridgeOrigin + ), + ); + + assert_noop!( + XcmOverBridge::close_bridge( + OpenBridgeOrigin::sibling_parachain_universal_origin(), + Box::new(bridged_asset_hub_universal_location().into()), + 0, + ), + Error::::BridgeLocations( + BridgeLocationsError::InvalidBridgeOrigin + ), + ); + }) + } + + #[test] + fn close_bridge_fails_if_its_lanes_are_unknown() { + run_test(|| { + let origin = OpenBridgeOrigin::parent_relay_chain_origin(); + let (bridge, locations) = mock_open_bridge_from(origin.clone(), None); + + let lanes_manager = LanesManagerOf::::new(); + lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().purge(); + assert_noop!( + XcmOverBridge::close_bridge( + origin.clone(), + Box::new(locations.bridge_destination_universal_location().clone().into()), + 0, + ), + Error::::LanesManager(LanesManagerError::UnknownInboundLane), + ); + lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().purge(); + + let (_, locations) = mock_open_bridge_from(origin.clone(), None); + lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().purge(); + assert_noop!( + XcmOverBridge::close_bridge( + origin, + Box::new(locations.bridge_destination_universal_location().clone().into()), + 0, + ), + Error::::LanesManager(LanesManagerError::UnknownOutboundLane), + ); + }); + } + + #[test] + fn close_bridge_works() { + run_test(|| { + let origin = OpenBridgeOrigin::parent_relay_chain_origin(); + let expected_deposit = BridgeDeposit::get(); + let (bridge, locations) = mock_open_bridge_from(origin.clone(), Some(expected_deposit)); + System::set_block_number(1); + let bridge_owner_account = bridge.deposit.unwrap().account; + + // remember owner balances + let free_balance = Balances::free_balance(&bridge_owner_account); + let reserved_balance = Balances::reserved_balance(&bridge_owner_account); + + // enqueue some messages + for _ in 0..32 { + enqueue_message(bridge.lane_id); + } + + // now call the `close_bridge`, which will only partially prune messages + assert_ok!(XcmOverBridge::close_bridge( + origin.clone(), + Box::new(locations.bridge_destination_universal_location().clone().into()), + 16, + ),); + + // as a result, the bridge and lanes are switched to the `Closed` state, some messages + // are pruned, but funds are not unreserved + let lanes_manager = LanesManagerOf::::new(); + assert_eq!( + Bridges::::get(locations.bridge_id()).map(|b| b.state), + Some(BridgeState::Closed) + ); + assert_eq!( + lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().state(), + LaneState::Closed + ); + assert_eq!( + lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().state(), + LaneState::Closed + ); + assert_eq!( + lanes_manager + .any_state_outbound_lane(bridge.lane_id) + .unwrap() + .queued_messages() + .checked_len(), + Some(16) + ); + assert_eq!( + LaneToBridge::::get(bridge.lane_id), + Some(*locations.bridge_id()) + ); + assert_eq!(Balances::free_balance(&bridge_owner_account), free_balance); + assert_eq!(Balances::reserved_balance(&bridge_owner_account), reserved_balance); + assert_eq!( + System::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge { + bridge_id: *locations.bridge_id(), + lane_id: bridge.lane_id.into(), + pruned_messages: 16, + enqueued_messages: 16, + }), + topics: vec![], + }), + ); + + // now call the `close_bridge` again, which will only partially prune messages + assert_ok!(XcmOverBridge::close_bridge( + origin.clone(), + Box::new(locations.bridge_destination_universal_location().clone().into()), + 8, + ),); + + // nothing is changed (apart from the pruned messages) + assert_eq!( + Bridges::::get(locations.bridge_id()).map(|b| b.state), + Some(BridgeState::Closed) + ); + assert_eq!( + lanes_manager.any_state_inbound_lane(bridge.lane_id).unwrap().state(), + LaneState::Closed + ); + assert_eq!( + lanes_manager.any_state_outbound_lane(bridge.lane_id).unwrap().state(), + LaneState::Closed + ); + assert_eq!( + lanes_manager + .any_state_outbound_lane(bridge.lane_id) + .unwrap() + .queued_messages() + .checked_len(), + Some(8) + ); + assert_eq!( + LaneToBridge::::get(bridge.lane_id), + Some(*locations.bridge_id()) + ); + assert_eq!(Balances::free_balance(&bridge_owner_account), free_balance); + assert_eq!(Balances::reserved_balance(&bridge_owner_account), reserved_balance); + assert_eq!( + System::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge { + bridge_id: *locations.bridge_id(), + lane_id: bridge.lane_id.into(), + pruned_messages: 8, + enqueued_messages: 8, + }), + topics: vec![], + }), + ); + + // now call the `close_bridge` again that will prune all remaining messages and the + // bridge + assert_ok!(XcmOverBridge::close_bridge( + origin, + Box::new(locations.bridge_destination_universal_location().clone().into()), + 9, + ),); + + // there's no traces of bridge in the runtime storage and funds are unreserved + assert_eq!( + Bridges::::get(locations.bridge_id()).map(|b| b.state), + None + ); + assert_eq!( + lanes_manager.any_state_inbound_lane(bridge.lane_id).map(drop), + Err(LanesManagerError::UnknownInboundLane) + ); + assert_eq!( + lanes_manager.any_state_outbound_lane(bridge.lane_id).map(drop), + Err(LanesManagerError::UnknownOutboundLane) + ); + assert_eq!(LaneToBridge::::get(bridge.lane_id), None); + assert_eq!( + Balances::free_balance(&bridge_owner_account), + free_balance + reserved_balance + ); + assert_eq!(Balances::reserved_balance(&bridge_owner_account), 0); + assert_eq!( + System::events().last(), + Some(&EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::XcmOverBridge(Event::BridgePruned { + bridge_id: *locations.bridge_id(), + lane_id: bridge.lane_id.into(), + bridge_deposit: Some(Deposit::new(bridge_owner_account, expected_deposit)), + pruned_messages: 8, + }), + topics: vec![], + }), + ); + }); + } + + #[test] + fn update_notification_receiver_works() { + run_test(|| { + let origin = OpenBridgeOrigin::parent_relay_chain_origin(); + let locations = XcmOverBridge::bridge_locations_from_origin( + origin.clone(), + Box::new(VersionedInteriorLocation::from(bridged_asset_hub_universal_location())), + ) + .unwrap(); + + // open the bridge + assert_ok!(XcmOverBridge::open_bridge( + origin.clone(), + Box::new(locations.bridge_destination_universal_location().clone().into()), + Some(Receiver::new(13, 15)), + )); + assert_eq!( + Bridges::::get(locations.bridge_id()) + .map(|b| b.maybe_notify) + .unwrap(), + Some(Receiver::new(13, 15)) + ); + + // update the notification receiver to `None` + assert_ok!(XcmOverBridge::update_notification_receiver( + origin.clone(), + Box::new(locations.bridge_destination_universal_location().clone().into()), + None, + )); + assert_eq!( + Bridges::::get(locations.bridge_id()) + .map(|b| b.maybe_notify) + .unwrap(), + None, + ); + + // update the notification receiver to `Some(..)` + assert_ok!(XcmOverBridge::update_notification_receiver( + origin.clone(), + Box::new(locations.bridge_destination_universal_location().clone().into()), + Some(Receiver::new(29, 43)), + )); + assert_eq!( + Bridges::::get(locations.bridge_id()) + .map(|b| b.maybe_notify) + .unwrap(), + Some(Receiver::new(29, 43)) + ); + // update the notification receiver to `Some(..)` + assert_ok!(XcmOverBridge::update_notification_receiver( + origin.clone(), + Box::new(locations.bridge_destination_universal_location().clone().into()), + Some(Receiver::new(29, 79)), + )); + assert_eq!( + Bridges::::get(locations.bridge_id()) + .map(|b| b.maybe_notify) + .unwrap(), + Some(Receiver::new(29, 79)) + ); + }); + } + + #[test] + fn do_try_state_works() { + let bridge_origin_relative_location = SiblingLocation::get(); + let bridge_origin_universal_location = SiblingUniversalLocation::get(); + let bridge_destination_universal_location = BridgedUniversalDestination::get(); + let bridge_owner_account = + LocationToAccountId::convert_location(&bridge_origin_relative_location) + .expect("valid accountId"); + let bridge_owner_account_mismatch = + LocationToAccountId::convert_location(&Location::parent()).expect("valid accountId"); + let bridge_id = BridgeId::new( + &bridge_origin_universal_location, + &bridge_destination_universal_location, + ); + let bridge_id_mismatch = BridgeId::new(&InteriorLocation::Here, &InteriorLocation::Here); + let lane_id = TestLaneIdType::try_new(1, 2).unwrap(); + let lane_id_mismatch = TestLaneIdType::try_new(3, 4).unwrap(); + + let test_bridge_state = + |id, + bridge, + (lane_id, bridge_id), + (inbound_lane_id, outbound_lane_id), + expected_error: Option| { + Bridges::::insert(id, bridge); + LaneToBridge::::insert(lane_id, bridge_id); + + let lanes_manager = LanesManagerOf::::new(); + lanes_manager.create_inbound_lane(inbound_lane_id).unwrap(); + lanes_manager.create_outbound_lane(outbound_lane_id).unwrap(); + + let result = XcmOverBridge::do_try_state(); + if let Some(e) = expected_error { + assert_err!(result, e); + } else { + assert_ok!(result); + } + }; + let cleanup = |bridge_id, lane_ids| { + Bridges::::remove(bridge_id); + for lane_id in lane_ids { + LaneToBridge::::remove(lane_id); + let lanes_manager = LanesManagerOf::::new(); + if let Ok(lane) = lanes_manager.any_state_inbound_lane(lane_id) { + lane.purge(); + } + if let Ok(lane) = lanes_manager.any_state_outbound_lane(lane_id) { + lane.purge(); + } + } + assert_ok!(XcmOverBridge::do_try_state()); + }; + + run_test(|| { + // ok state + test_bridge_state( + bridge_id, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new( + VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + ), + ), + state: BridgeState::Opened, + deposit: Some(Deposit::new(bridge_owner_account.clone(), Zero::zero())), + lane_id, + maybe_notify: None, + }, + (lane_id, bridge_id), + (lane_id, lane_id), + None, + ); + cleanup(bridge_id, vec![lane_id]); + + // error - missing `LaneToBridge` mapping + test_bridge_state( + bridge_id, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new( + VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + ), + ), + state: BridgeState::Opened, + deposit: Some(Deposit::new(bridge_owner_account.clone(), Zero::zero())), + lane_id, + maybe_notify: None, + }, + (lane_id, bridge_id_mismatch), + (lane_id, lane_id), + Some(TryRuntimeError::Other( + "Found `LaneToBridge` inconsistency for bridge_id - missing mapping!", + )), + ); + cleanup(bridge_id, vec![lane_id]); + + // error bridge owner account cannot be calculated + test_bridge_state( + bridge_id, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + )), + state: BridgeState::Opened, + deposit: Some(Deposit::new(bridge_owner_account_mismatch.clone(), Zero::zero())), + lane_id, + maybe_notify: None, + }, + (lane_id, bridge_id), + (lane_id, lane_id), + Some(TryRuntimeError::Other("`bridge.deposit.account` is different than calculated from `bridge.bridge_origin_relative_location`, needs migration!")), + ); + cleanup(bridge_id, vec![lane_id]); + + // error when (bridge_origin_universal_location + bridge_destination_universal_location) + // produces different `BridgeId` + test_bridge_state( + bridge_id_mismatch, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + )), + state: BridgeState::Opened, + deposit: Some(Deposit::new(bridge_owner_account_mismatch.clone(), Zero::zero())), + lane_id, + maybe_notify: None, + }, + (lane_id, bridge_id_mismatch), + (lane_id, lane_id), + Some(TryRuntimeError::Other("`bridge_id` is different than calculated from `bridge_origin_universal_location_as_latest` and `bridge_destination_universal_location_as_latest`, needs migration!")), + ); + cleanup(bridge_id_mismatch, vec![lane_id]); + + // missing inbound lane for a bridge + test_bridge_state( + bridge_id, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new( + VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + ), + ), + state: BridgeState::Opened, + deposit: Some(Deposit::new(bridge_owner_account.clone(), Zero::zero())), + lane_id, + maybe_notify: None, + }, + (lane_id, bridge_id), + (lane_id_mismatch, lane_id), + Some(TryRuntimeError::Other("Inbound lane not found!")), + ); + cleanup(bridge_id, vec![lane_id, lane_id_mismatch]); + + // missing outbound lane for a bridge + test_bridge_state( + bridge_id, + Bridge { + bridge_origin_relative_location: Box::new(VersionedLocation::from( + bridge_origin_relative_location.clone(), + )), + bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from( + bridge_origin_universal_location.clone(), + )), + bridge_destination_universal_location: Box::new( + VersionedInteriorLocation::from( + bridge_destination_universal_location.clone(), + ), + ), + state: BridgeState::Opened, + deposit: Some(Deposit::new(bridge_owner_account, Zero::zero())), + lane_id, + maybe_notify: None, + }, + (lane_id, bridge_id), + (lane_id, lane_id_mismatch), + Some(TryRuntimeError::Other("Outbound lane not found!")), + ); + cleanup(bridge_id, vec![lane_id, lane_id_mismatch]); + + // missing bridge for inbound lane + let lanes_manager = LanesManagerOf::::new(); + assert!(lanes_manager.create_inbound_lane(lane_id).is_ok()); + assert_err!(XcmOverBridge::do_try_state(), TryRuntimeError::Other("Found `LaneToBridge` inconsistency for `InboundLanes`'s lane_id - missing mapping!")); + cleanup(bridge_id, vec![lane_id]); + + // missing bridge for outbound lane + let lanes_manager = LanesManagerOf::::new(); + assert!(lanes_manager.create_outbound_lane(lane_id).is_ok()); + assert_err!(XcmOverBridge::do_try_state(), TryRuntimeError::Other("Found `LaneToBridge` inconsistency for `OutboundLanes`'s lane_id - missing mapping!")); + cleanup(bridge_id, vec![lane_id]); + }); + } + + #[test] + fn ensure_encoding_compatibility() { + use codec::Encode; + + let bridge_destination_universal_location = BridgedUniversalDestination::get(); + let may_prune_messages = 13; + let receiver = Receiver::new(13, 15); + + assert_eq!( + bp_xcm_bridge::XcmBridgeCall::open_bridge { + bridge_destination_universal_location: Box::new( + bridge_destination_universal_location.clone().into() + ), + maybe_notify: Some(receiver.clone()), + } + .encode(), + Call::::open_bridge { + bridge_destination_universal_location: Box::new( + bridge_destination_universal_location.clone().into() + ), + maybe_notify: Some(receiver), + } + .encode() + ); + assert_eq!( + bp_xcm_bridge::XcmBridgeCall::close_bridge { + bridge_destination_universal_location: Box::new( + bridge_destination_universal_location.clone().into() + ), + may_prune_messages, + } + .encode(), + Call::::close_bridge { + bridge_destination_universal_location: Box::new( + bridge_destination_universal_location.clone().into() + ), + may_prune_messages, + } + .encode() + ); + } +} diff --git a/bridges/modules/xcm-bridge/src/migration.rs b/bridges/modules/xcm-bridge/src/migration.rs new file mode 100644 index 000000000000..6658c688c912 --- /dev/null +++ b/bridges/modules/xcm-bridge/src/migration.rs @@ -0,0 +1,260 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! A module that is responsible for migration of storage. + +use crate::{Config, Pallet, Receiver, LOG_TARGET}; +use frame_support::{ + traits::{Get, OnRuntimeUpgrade, StorageVersion}, + weights::Weight, +}; +use xcm::prelude::{InteriorLocation, Location}; + +// TODO: remove STORAGE_VERSION with v0 migration when renaming module - FAIL-CI +/// The in-code storage version. +pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + +/// This migration does not modify storage but can be used to open a bridge and link it to the +/// specified LaneId. This is useful when we want to open a bridge and use a custom LaneId instead +/// of the pre-calculated one provided by the `fn open_bridge extrinsic`. +/// Or perhaps if you want to ensure that your runtime (e.g., for testing) always has an open +/// bridge. +pub struct OpenBridgeForLane< + T, + I, + Lane, + CreateLane, + SourceRelativeLocation, + BridgedUniversalLocation, + MaybeNotifyRelativeLocation, +>( + core::marker::PhantomData<( + T, + I, + Lane, + CreateLane, + SourceRelativeLocation, + BridgedUniversalLocation, + MaybeNotifyRelativeLocation, + )>, +); +impl< + T: Config, + I: 'static, + Lane: Get, + CreateLane: Get, + SourceRelativeLocation: Get, + BridgedUniversalLocation: Get, + MaybeNotifyRelativeLocation: Get>, + > OnRuntimeUpgrade + for OpenBridgeForLane< + T, + I, + Lane, + CreateLane, + SourceRelativeLocation, + BridgedUniversalLocation, + MaybeNotifyRelativeLocation, + > +{ + fn on_runtime_upgrade() -> Weight { + let bridge_origin_relative_location = SourceRelativeLocation::get(); + let bridge_destination_universal_location = BridgedUniversalLocation::get(); + let lane_id = Lane::get(); + let create_lane = CreateLane::get(); + let maybe_notify = MaybeNotifyRelativeLocation::get(); + log::info!( + target: LOG_TARGET, + "OpenBridgeForLane - going to open bridge with lane_id: {lane_id:?} (create_lane: {create_lane:?}) \ + between bridge_origin_relative_location: {bridge_origin_relative_location:?} and \ + bridge_destination_universal_location: {bridge_destination_universal_location:?} \ + maybe_notify: {maybe_notify:?}", + ); + + let locations = match Pallet::::bridge_locations( + bridge_origin_relative_location.clone(), + bridge_destination_universal_location.clone(), + ) { + Ok(locations) => locations, + Err(e) => { + log::error!( + target: LOG_TARGET, + "OpenBridgeForLane - on_runtime_upgrade failed to construct bridge_locations with error: {e:?}" + ); + return T::DbWeight::get().reads(0) + }, + }; + + // check if already exists + if let Some((bridge_id, bridge)) = Pallet::::bridge_by_lane_id(&lane_id) { + log::info!( + target: LOG_TARGET, + "OpenBridgeForLane - bridge: {bridge:?} with bridge_id: {bridge_id:?} already exist for lane_id: {lane_id:?}!" + ); + if &bridge_id != locations.bridge_id() { + log::warn!( + target: LOG_TARGET, + "OpenBridgeForLane - check you parameters, because a different bridge: {bridge:?} \ + with bridge_id: {bridge_id:?} exist for lane_id: {lane_id:?} for requested \ + bridge_origin_relative_location: {bridge_origin_relative_location:?} and \ + bridge_destination_universal_location: {bridge_destination_universal_location:?} !", + ); + } + + return T::DbWeight::get().reads(2) + } + + if let Err(e) = + Pallet::::do_open_bridge(locations, lane_id, create_lane, maybe_notify) + { + log::error!(target: LOG_TARGET, "OpenBridgeForLane - do_open_bridge failed with error: {e:?}"); + T::DbWeight::get().reads(6) + } else { + log::info!(target: LOG_TARGET, "OpenBridgeForLane - do_open_bridge passed!"); + T::DbWeight::get().reads_writes(6, 4) + } + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: sp_std::vec::Vec) -> Result<(), sp_runtime::DispatchError> { + let bridge_origin_relative_location = SourceRelativeLocation::get(); + let bridge_destination_universal_location = BridgedUniversalLocation::get(); + let lane_id = Lane::get(); + + // check that requested bridge is stored + let Ok(locations) = Pallet::::bridge_locations( + bridge_origin_relative_location.clone(), + bridge_destination_universal_location.clone(), + ) else { + return Err(sp_runtime::DispatchError::Other("Invalid locations!")) + }; + let Some((bridge_id, _)) = Pallet::::bridge_by_lane_id(&lane_id) else { + return Err(sp_runtime::DispatchError::Other("Missing bridge!")) + }; + frame_support::ensure!( + locations.bridge_id() == &bridge_id, + "Bridge is not stored correctly!" + ); + + log::info!( + target: LOG_TARGET, + "OpenBridgeForLane - post_upgrade found opened bridge with lane_id: {lane_id:?} \ + between bridge_origin_relative_location: {bridge_origin_relative_location:?} and \ + bridge_destination_universal_location: {bridge_destination_universal_location:?}", + ); + + Ok(()) + } +} + +/// This module contains data structures that are valid for the initial state of `0`. +/// (used with v1 migration). +pub mod v0 { + use crate::{LaneIdOf, ThisChainOf}; + use bp_messages::LaneIdType; + use bp_runtime::{AccountIdOf, BalanceOf, Chain}; + use bp_xcm_bridge::BridgeState; + use codec::{Decode, Encode, MaxEncodedLen}; + use frame_support::{CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound}; + use scale_info::TypeInfo; + use sp_std::boxed::Box; + use xcm::{VersionedInteriorLocation, VersionedLocation}; + + #[derive( + CloneNoBound, + Decode, + Encode, + Eq, + PartialEqNoBound, + TypeInfo, + MaxEncodedLen, + RuntimeDebugNoBound, + )] + #[scale_info(skip_type_params(ThisChain, LaneId))] + pub(crate) struct Bridge { + pub bridge_origin_relative_location: Box, + pub bridge_origin_universal_location: Box, + pub bridge_destination_universal_location: Box, + pub state: BridgeState, + pub bridge_owner_account: AccountIdOf, + pub deposit: BalanceOf, + pub lane_id: LaneId, + } + + pub(crate) type BridgeOf = Bridge, LaneIdOf>; +} + +/// This migration to `1` updates the metadata of `Bridge`. +pub mod v1 { + use super::*; + use crate::{BalanceOf, Bridge, BridgeOf, Bridges, Deposit, ThisChainOf}; + use frame_support::{pallet_prelude::Zero, traits::UncheckedOnRuntimeUpgrade}; + use sp_std::marker::PhantomData; + + /// Migrates the pallet storage to v1. + pub struct UncheckedMigrationV0ToV1(PhantomData<(T, I)>); + + impl, I: 'static> UncheckedOnRuntimeUpgrade for UncheckedMigrationV0ToV1 { + fn on_runtime_upgrade() -> Weight { + let mut weight = T::DbWeight::get().reads(1); + + // Migrate account/deposit to the `Deposit` struct. + let translate = |pre: v0::BridgeOf| -> Option> { + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + let v0::Bridge { + bridge_origin_relative_location, + bridge_origin_universal_location, + bridge_destination_universal_location, + state, + bridge_owner_account, + deposit, + lane_id, + } = pre; + + // map deposit to the `Deposit` + let deposit = if deposit > BalanceOf::>::zero() { + Some(Deposit::new(bridge_owner_account, deposit)) + } else { + None + }; + + Some(v1::Bridge { + bridge_origin_relative_location, + bridge_origin_universal_location, + bridge_destination_universal_location, + state, + deposit, + lane_id, + maybe_notify: None, + }) + }; + Bridges::::translate_values(translate); + + weight + } + } + + /// [`UncheckedMigrationV0ToV1`] wrapped in a + /// [`VersionedMigration`](frame_support::migrations::VersionedMigration), ensuring the + /// migration is only performed when on-chain version is 0. + pub type MigrationToV1 = frame_support::migrations::VersionedMigration< + 0, + 1, + UncheckedMigrationV0ToV1, + Pallet, + ::DbWeight, + >; +} diff --git a/bridges/modules/xcm-bridge/src/mock.rs b/bridges/modules/xcm-bridge/src/mock.rs new file mode 100644 index 000000000000..d3d37c4c3890 --- /dev/null +++ b/bridges/modules/xcm-bridge/src/mock.rs @@ -0,0 +1,734 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +#![cfg(test)] + +use crate as pallet_xcm_bridge; +use bp_messages::{ + target_chain::{DispatchMessage, MessageDispatch}, + ChainWithMessages, HashedLaneId, MessageNonce, +}; +use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, HashOf}; +use bp_xcm_bridge::{BridgeId, BridgeLocations, LocalXcmChannelManager}; +use codec::Encode; +use frame_support::{ + assert_ok, derive_impl, parameter_types, + traits::{fungible::Mutate, EitherOf, EnsureOrigin, Equals, Everything, Get, OriginTrait}, + weights::RuntimeDbWeight, +}; +use frame_system::{EnsureNever, EnsureRoot, EnsureRootWithSuccess}; +use pallet_xcm_bridge::congestion::{ + BlobDispatcherWithChannelStatus, CongestionLimits, HereOrLocalConsensusXcmChannelManager, + UpdateBridgeStatusXcmChannelManager, +}; +use polkadot_parachain_primitives::primitives::Sibling; +use sp_core::H256; +use sp_runtime::{ + testing::Header as SubstrateHeader, + traits::{BlakeTwo256, ConstU32, Convert, IdentityLookup}, + AccountId32, BuildStorage, StateVersion, +}; +use sp_std::{cell::RefCell, marker::PhantomData}; +use xcm::{latest::ROCOCO_GENESIS_HASH, prelude::*}; +use xcm_builder::{ + AllowUnpaidExecutionFrom, DispatchBlob, DispatchBlobError, FixedWeightBounds, + InspectMessageQueues, NetworkExportTable, NetworkExportTableItem, ParentIsPreset, + SiblingParachainConvertsVia, SovereignPaidRemoteExporter, LocalExporter, +}; +use xcm_executor::{ + traits::{ConvertLocation, ConvertOrigin}, + XcmExecutor, +}; + +pub type AccountId = AccountId32; +pub type Balance = u64; +type Block = frame_system::mocking::MockBlock; + +/// Lane identifier type used for tests. +pub type TestLaneIdType = HashedLaneId; + +pub const SIBLING_ASSET_HUB_ID: u32 = 2001; +pub const THIS_BRIDGE_HUB_ID: u32 = 2002; +pub const BRIDGED_ASSET_HUB_ID: u32 = 1001; + +frame_support::construct_runtime! { + pub enum TestRuntime { + System: frame_system, + Balances: pallet_balances, + Messages: pallet_bridge_messages, + XcmOverBridge: pallet_xcm_bridge, + XcmOverBridgeWrappedWithExportMessageRouter: pallet_xcm_bridge_router = 57, + XcmOverBridgeByExportXcmRouter: pallet_xcm_bridge_router:: = 69, + } +} + +parameter_types! { + pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; + pub const ExistentialDeposit: Balance = 1; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for TestRuntime { + type AccountId = AccountId; + type AccountData = pallet_balances::AccountData; + type Block = Block; + type Lookup = IdentityLookup; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for TestRuntime { + type AccountStore = System; +} + +impl pallet_bridge_messages::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = TestMessagesWeights; + + type ThisChain = ThisUnderlyingChain; + type BridgedChain = BridgedUnderlyingChain; + type BridgedHeaderChain = BridgedHeaderChain; + + type OutboundPayload = Vec; + type InboundPayload = Vec; + type LaneId = TestLaneIdType; + + type DeliveryPayments = (); + type DeliveryConfirmationPayments = (); + type OnMessagesDelivered = (); + + type MessageDispatch = TestMessageDispatch; +} + +pub struct TestMessagesWeights; + +impl pallet_bridge_messages::WeightInfo for TestMessagesWeights { + fn receive_single_message_proof() -> Weight { + Weight::zero() + } + fn receive_n_messages_proof(_: u32) -> Weight { + Weight::zero() + } + fn receive_single_message_proof_with_outbound_lane_state() -> Weight { + Weight::zero() + } + fn receive_single_n_bytes_message_proof(_: u32) -> Weight { + Weight::zero() + } + fn receive_delivery_proof_for_single_message() -> Weight { + Weight::zero() + } + fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { + Weight::zero() + } + fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { + Weight::zero() + } + fn receive_single_n_bytes_message_proof_with_dispatch(_n: u32) -> Weight { + Weight::from_parts(1, 0) + } +} + +impl pallet_bridge_messages::WeightInfoExt for TestMessagesWeights { + fn expected_extra_storage_proof_size() -> u32 { + 0 + } + + fn receive_messages_proof_overhead_from_runtime() -> Weight { + Weight::zero() + } + + fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight { + Weight::zero() + } +} + +parameter_types! { + pub const HereLocation: Location = Location::here(); + pub const RelayNetwork: NetworkId = NetworkId::Kusama; + pub UniversalLocation: InteriorLocation = [ + GlobalConsensus(RelayNetwork::get()), + Parachain(THIS_BRIDGE_HUB_ID), + ].into(); + pub SiblingLocation: Location = Location::new(1, [Parachain(SIBLING_ASSET_HUB_ID)]); + pub SiblingUniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(SIBLING_ASSET_HUB_ID)].into(); + + pub const BridgedRelayNetwork: NetworkId = NetworkId::ByGenesis([1; 32]); + pub BridgedRelayNetworkLocation: Location = (Parent, GlobalConsensus(BridgedRelayNetwork::get())).into(); + pub BridgedRelativeDestination: InteriorLocation = [Parachain(BRIDGED_ASSET_HUB_ID)].into(); + pub BridgedUniversalDestination: InteriorLocation = [GlobalConsensus(BridgedRelayNetwork::get()), Parachain(BRIDGED_ASSET_HUB_ID)].into(); + pub const NonBridgedRelayNetwork: NetworkId = NetworkId::ByGenesis(ROCOCO_GENESIS_HASH); + + pub const BridgeDeposit: Balance = 100_000; + + // configuration for pallet_xcm_bridge_hub_router + pub BridgeHubLocation: Location = Here.into(); + pub BridgeTable: Vec + = vec![ + NetworkExportTableItem::new( + BridgedRelayNetwork::get(), + None, + BridgeHubLocation::get(), + None + ) + ]; + pub UnitWeightCost: Weight = Weight::from_parts(10, 10); + pub storage TestCongestionLimits: CongestionLimits = CongestionLimits::default(); +} + +/// **Universal** `InteriorLocation` of bridged asset hub. +pub fn bridged_asset_hub_universal_location() -> InteriorLocation { + BridgedUniversalDestination::get() +} + +pub(crate) type TestLocalXcmChannelManager = TestingLocalXcmChannelManager< + BridgeId, + HereOrLocalConsensusXcmChannelManager< + BridgeId, + // handles congestion for `XcmOverBridgeByExportXcmRouter` + XcmOverBridgeByExportXcmRouter, + // handles congestion for `XcmOverBridgeWrappedWithExportMessageRouter` + UpdateBridgeStatusXcmChannelManager< + TestRuntime, + (), + UpdateBridgeStatusXcmProvider, + FromBridgeHubLocationXcmSender, + >, + >, +>; + +impl pallet_xcm_bridge::Config for TestRuntime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + + type UniversalLocation = UniversalLocation; + type BridgedNetwork = BridgedRelayNetworkLocation; + type BridgeMessagesPalletInstance = (); + + type MessageExportPrice = (); + type DestinationVersion = AlwaysLatest; + + type ForceOrigin = EnsureNever<()>; + type OpenBridgeOrigin = EitherOf< + // We want to translate `RuntimeOrigin::root()` to the `Location::here()` + EnsureRootWithSuccess, + OpenBridgeOrigin, + >; + type BridgeOriginAccountIdConverter = LocationToAccountId; + + type BridgeDeposit = BridgeDeposit; + type Currency = Balances; + type RuntimeHoldReason = RuntimeHoldReason; + type AllowWithoutBridgeDeposit = (Equals, Equals); + + type LocalXcmChannelManager = TestLocalXcmChannelManager; + type BlobDispatcher = BlobDispatcherWithChannelStatus; + type CongestionLimits = TestCongestionLimits; +} + +#[cfg(feature = "runtime-benchmarks")] +impl crate::benchmarking::Config<()> for TestRuntime { + fn open_bridge_origin() -> Option<(RuntimeOrigin, Balance)> { + Some((OpenBridgeOrigin::sibling_parachain_origin(), ExistentialDeposit::get())) + } +} + +/// 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_router::config_preludes::TestDefaultConfig)] +impl pallet_xcm_bridge_router::Config + for TestRuntime +{ + // 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`. + type MessageExporter = SovereignPaidRemoteExporter< + pallet_xcm_bridge_router::impls::ViaRemoteBridgeHubExporter< + TestRuntime, + // () - means `pallet_xcm_bridge_router::Config<()>` + (), + NetworkExportTable, + BridgedRelayNetwork, + BridgeHubLocation, + >, + // **Note**: The crucial part is that `ExportMessage` is processed by `XcmExecutor`, which + // calls the `ExportXcm` implementation of `pallet_xcm_bridge_hub` as the + // `MessageExporter`. + ExecuteXcmOverSendXcm, + ExportMessageOriginUniversalLocation, + >; + + type BridgeIdResolver = pallet_xcm_bridge_router::impls::EnsureIsRemoteBridgeIdResolver< + ExportMessageOriginUniversalLocation, + >; + // We convert to root here `BridgeHubLocationXcmOriginAsRoot` + type UpdateBridgeStatusOrigin = EnsureRoot; +} + +/// 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. +pub type XcmOverBridgeByExportXcmRouterInstance = pallet_xcm_bridge_router::Instance2; +#[derive_impl(pallet_xcm_bridge_router::config_preludes::TestDefaultConfig)] +impl pallet_xcm_bridge_router::Config for TestRuntime { + // We use `LocalExporter` with `ViaLocalBridgeHubExporter` here to test and ensure that + // `pallet_xcm_bridge_hub_router` can trigger directly `pallet_xcm_bridge_hub` as exporter. + type MessageExporter = pallet_xcm_bridge_router::impls::ViaLocalBridgeHubExporter< + TestRuntime, + XcmOverBridgeByExportXcmRouterInstance, + LocalExporter, + >; + + type BridgeIdResolver = + pallet_xcm_bridge_router::impls::EnsureIsRemoteBridgeIdResolver; + // We don't need to support here `update_bridge_status`. + type UpdateBridgeStatusOrigin = EnsureNever<()>; +} + +/// 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) { + EXPORT_MESSAGE_ORIGIN_UNIVERSAL_LOCATION.with(|o| *o.borrow_mut() = universal_location); + } +} +impl Get 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> = RefCell::new(None); +} + +pub struct BridgeHubLocationXcmOriginAsRoot(PhantomData); +impl ConvertOrigin + for BridgeHubLocationXcmOriginAsRoot +{ + fn convert_origin( + origin: impl Into, + kind: OriginKind, + ) -> Result { + let origin = origin.into(); + if kind == OriginKind::Xcm && origin.eq(&BridgeHubLocation::get()) { + Ok(RuntimeOrigin::root()) + } else { + Err(origin) + } + } +} + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = (); + type AssetTransactor = (); + type OriginConverter = BridgeHubLocationXcmOriginAsRoot; + type IsReserve = (); + type IsTeleporter = (); + type UniversalLocation = UniversalLocation; + type Barrier = AllowUnpaidExecutionFrom; + type Weigher = FixedWeightBounds>; + type Trader = (); + type ResponseHandler = (); + type AssetTrap = (); + type AssetClaims = (); + type SubscriptionService = (); + type PalletInstancesInfo = (); + type MaxAssetsIntoHolding = (); + type AssetLocker = (); + type AssetExchanger = (); + type FeeManager = (); + // We just set `MessageExporter` as our `pallet_xcm_bridge_hub` instance. + type MessageExporter = (XcmOverBridge,); + type UniversalAliases = (); + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type Aliasers = (); + type TransactionalProcessor = (); + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type XcmRecorder = (); +} + +thread_local! { + pub static EXECUTE_XCM_ORIGIN: RefCell> = RefCell::new(None); +} + +/// The `SendXcm` implementation directly executes XCM using `XcmExecutor`. +pub struct ExecuteXcmOverSendXcm; +impl SendXcm for ExecuteXcmOverSendXcm { + type Ticket = Xcm<()>; + + fn validate( + _: &mut Option, + message: &mut Option>, + ) -> SendResult { + Ok((message.take().unwrap(), Assets::new())) + } + + fn deliver(ticket: Self::Ticket) -> Result { + let xcm: Xcm = ticket.into(); + + let origin = EXECUTE_XCM_ORIGIN.with(|o| o.borrow().clone().unwrap()); + let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); + let outcome = XcmExecutor::::prepare_and_execute( + origin, + xcm, + &mut hash, + Weight::MAX, + Weight::zero(), + ); + assert_ok!(outcome.ensure_complete()); + + Ok(hash) + } +} +impl InspectMessageQueues for ExecuteXcmOverSendXcm { + fn clear_messages() { + todo!() + } + + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + todo!() + } +} +impl ExecuteXcmOverSendXcm { + pub fn set_origin_for_execute(origin: Location) { + EXECUTE_XCM_ORIGIN.with(|o| *o.borrow_mut() = Some(origin)); + } +} + +/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used +/// when determining ownership of accounts for asset transacting and when attempting to use XCM +/// `Transact` in order to determine the dispatch Origin. +pub type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the parent `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, +); + +parameter_types! { + pub ParentRelayChainLocation: Location = Location { parents: 1, interior: Here }; +} +pub struct OpenBridgeOrigin; + +impl OpenBridgeOrigin { + pub fn parent_relay_chain_origin() -> RuntimeOrigin { + RuntimeOrigin::signed([0u8; 32].into()) + } + + pub fn parent_relay_chain_universal_origin() -> RuntimeOrigin { + RuntimeOrigin::signed([1u8; 32].into()) + } + + pub fn sibling_parachain_origin() -> RuntimeOrigin { + let mut account = [0u8; 32]; + account[..4].copy_from_slice(&SIBLING_ASSET_HUB_ID.encode()[..4]); + RuntimeOrigin::signed(account.into()) + } + + pub fn sibling_parachain_universal_origin() -> RuntimeOrigin { + RuntimeOrigin::signed([2u8; 32].into()) + } + + pub fn origin_without_sovereign_account() -> RuntimeOrigin { + RuntimeOrigin::signed([3u8; 32].into()) + } + + pub fn disallowed_origin() -> RuntimeOrigin { + RuntimeOrigin::signed([42u8; 32].into()) + } +} + +impl EnsureOrigin for OpenBridgeOrigin { + type Success = Location; + + fn try_origin(o: RuntimeOrigin) -> Result { + let signer = o.clone().into_signer(); + if signer == Self::parent_relay_chain_origin().into_signer() { + return Ok(ParentRelayChainLocation::get()) + } else if signer == Self::parent_relay_chain_universal_origin().into_signer() { + return Ok(Location { + parents: 2, + interior: GlobalConsensus(RelayNetwork::get()).into(), + }) + } else if signer == Self::sibling_parachain_universal_origin().into_signer() { + return Ok(Location { + parents: 2, + interior: [GlobalConsensus(RelayNetwork::get()), Parachain(SIBLING_ASSET_HUB_ID)] + .into(), + }) + } else if signer == Self::origin_without_sovereign_account().into_signer() { + return Ok(Location { + parents: 1, + interior: [Parachain(SIBLING_ASSET_HUB_ID), OnlyChild].into(), + }); + } else if signer == Self::sibling_parachain_origin().into_signer() { + return Ok(SiblingLocation::get()); + } + + Err(o) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + Ok(Self::parent_relay_chain_origin()) + } +} + +pub(crate) type OpenBridgeOriginOf = + >::OpenBridgeOrigin; + +pub(crate) fn fund_origin_sovereign_account( + locations: &BridgeLocations, + balance: Balance, +) -> AccountId { + let bridge_owner_account = + LocationToAccountId::convert_location(locations.bridge_origin_relative_location()).unwrap(); + assert_ok!(Balances::mint_into(&bridge_owner_account, balance)); + bridge_owner_account +} + +/// Testing wrapper implementation of `LocalXcmChannelManager`, that supports storing flags in +/// storage to facilitate testing of `LocalXcmChannelManager` implementation. +pub struct TestingLocalXcmChannelManager(PhantomData<(Bridge, Tested)>); + +impl TestingLocalXcmChannelManager { + fn suspended_key(bridge: &Bridge) -> Vec { + [b"TestingLocalXcmChannelManager.Suspended", bridge.encode().as_slice()].concat() + } + fn resumed_key(bridge: &Bridge) -> Vec { + [b"TestingLocalXcmChannelManager.Resumed", bridge.encode().as_slice()].concat() + } + + pub fn is_bridge_suspened(bridge: &Bridge) -> bool { + frame_support::storage::unhashed::get_or_default(&Self::suspended_key(bridge)) + } + + pub fn is_bridge_resumed(bridge: &Bridge) -> bool { + frame_support::storage::unhashed::get_or_default(&Self::resumed_key(bridge)) + } +} + +impl> + LocalXcmChannelManager for TestingLocalXcmChannelManager +{ + type Error = Tested::Error; + + fn suspend_bridge(local_origin: &Location, bridge: Bridge) -> Result<(), Self::Error> { + let result = Tested::suspend_bridge(local_origin, bridge); + + if result.is_ok() { + frame_support::storage::unhashed::put(&Self::suspended_key(&bridge), &true); + } + + result + } + + fn resume_bridge(local_origin: &Location, bridge: Bridge) -> Result<(), Self::Error> { + let result = Tested::resume_bridge(local_origin, bridge); + + if result.is_ok() { + frame_support::storage::unhashed::put(&Self::resumed_key(&bridge), &true); + } + + result + } +} + +/// Converts encoded call to the unpaid XCM `Transact`. +pub struct UpdateBridgeStatusXcmProvider; +impl Convert, Xcm<()>> for UpdateBridgeStatusXcmProvider { + fn convert(encoded_call: Vec) -> Xcm<()> { + Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + Transact { + origin_kind: OriginKind::Xcm, + fallback_max_weight: Some(Weight::from_parts(200_000_000, 6144)), + call: encoded_call.into(), + }, + ExpectTransactStatus(MaybeErrorCode::Success), + ]) + } +} + +/// `SendXcm` implementation which sets `BridgeHubLocation` as origin for `ExecuteXcmOverSendXcm`. +pub struct FromBridgeHubLocationXcmSender(PhantomData); +impl SendXcm for FromBridgeHubLocationXcmSender { + type Ticket = Inner::Ticket; + + fn validate( + destination: &mut Option, + message: &mut Option>, + ) -> SendResult { + Inner::validate(destination, message) + } + + fn deliver(ticket: Self::Ticket) -> Result { + ExecuteXcmOverSendXcm::set_origin_for_execute(BridgeHubLocation::get()); + Inner::deliver(ticket) + } +} + +pub struct TestBlobDispatcher; + +impl TestBlobDispatcher { + pub fn is_dispatched() -> bool { + frame_support::storage::unhashed::get_or_default(b"TestBlobDispatcher.Dispatched") + } + + fn congestion_key(with: &Location) -> Vec { + [b"TestBlobDispatcher.Congested.", with.encode().as_slice()].concat() + } + + pub fn make_congested(with: &Location) { + frame_support::storage::unhashed::put(&Self::congestion_key(with), &true); + } +} + +impl pallet_xcm_bridge::DispatchChannelStatusProvider for TestBlobDispatcher { + fn is_congested(with: &Location) -> bool { + frame_support::storage::unhashed::get_or_default(&Self::congestion_key(with)) + } +} + +impl DispatchBlob for TestBlobDispatcher { + fn dispatch_blob(_blob: Vec) -> Result<(), DispatchBlobError> { + frame_support::storage::unhashed::put(b"TestBlobDispatcher.Dispatched", &true); + Ok(()) + } +} + +pub struct ThisUnderlyingChain; + +impl Chain for ThisUnderlyingChain { + const ID: ChainId = *b"tuch"; + + type BlockNumber = u64; + type Hash = H256; + type Hasher = BlakeTwo256; + type Header = SubstrateHeader; + type AccountId = AccountId; + type Balance = Balance; + type Nonce = u64; + type Signature = sp_runtime::MultiSignature; + + const STATE_VERSION: StateVersion = StateVersion::V1; + + fn max_extrinsic_size() -> u32 { + u32::MAX + } + + fn max_extrinsic_weight() -> Weight { + Weight::MAX + } +} + +impl ChainWithMessages for ThisUnderlyingChain { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = "WithThisChainBridgeMessages"; + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 128; +} + +pub type BridgedHeaderHash = H256; +pub type BridgedChainHeader = SubstrateHeader; + +pub struct BridgedUnderlyingChain; +impl Chain for BridgedUnderlyingChain { + const ID: ChainId = *b"bgdc"; + type BlockNumber = u64; + type Hash = BridgedHeaderHash; + type Hasher = BlakeTwo256; + type Header = BridgedChainHeader; + type AccountId = AccountId; + type Balance = Balance; + type Nonce = u64; + type Signature = sp_runtime::MultiSignature; + + const STATE_VERSION: StateVersion = StateVersion::V1; + + fn max_extrinsic_size() -> u32 { + 4096 + } + + fn max_extrinsic_weight() -> Weight { + Weight::MAX + } +} + +impl ChainWithMessages for BridgedUnderlyingChain { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = "WithBridgedChainBridgeMessages"; + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 16; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 128; +} + +pub struct BridgedHeaderChain; +impl bp_header_chain::HeaderChain for BridgedHeaderChain { + fn finalized_header_state_root( + _hash: HashOf, + ) -> Option> { + unreachable!() + } +} + +/// Test message dispatcher. +pub struct TestMessageDispatch; + +impl TestMessageDispatch { + pub fn deactivate(lane: TestLaneIdType) { + frame_support::storage::unhashed::put(&(b"inactive", lane).encode()[..], &false); + } +} + +impl MessageDispatch for TestMessageDispatch { + type DispatchPayload = Vec; + type DispatchLevelResult = (); + type LaneId = TestLaneIdType; + + fn is_active(lane: Self::LaneId) -> bool { + frame_support::storage::unhashed::take::(&(b"inactive", lane).encode()[..]) != + Some(false) + } + + fn dispatch_weight( + _message: &mut DispatchMessage, + ) -> Weight { + Weight::zero() + } + + fn dispatch( + _: DispatchMessage, + ) -> MessageDispatchResult { + MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () } + } +} + +/// Return test externalities to use in tests. +pub fn new_test_ext() -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + sp_io::TestExternalities::new(t) +} + +/// Run pallet test. +pub fn run_test(test: impl FnOnce() -> T) -> T { + new_test_ext().execute_with(test) +} diff --git a/bridges/modules/xcm-bridge/src/weights.rs b/bridges/modules/xcm-bridge/src/weights.rs new file mode 100644 index 000000000000..c0bab87ef727 --- /dev/null +++ b/bridges/modules/xcm-bridge/src/weights.rs @@ -0,0 +1,90 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Autogenerated weights for pallet_xcm_bridge_hub +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-08-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/rip-bridge-node +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_xcm_bridge_hub_router +// --extrinsic=* +// --execution=wasm +// --wasm-execution=Compiled +// --heap-pages=4096 +// --output=./modules/xcm-bridge-hub-router/src/weights.rs +// --template=./.maintain/bridge-weight-template.hbs + +#![allow(clippy::all)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_xcm_bridge_hub_router. +pub trait WeightInfo { + fn open_bridge() -> Weight; + fn close_bridge() -> Weight; + fn update_notification_receiver() -> Weight; +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn open_bridge() -> Weight { + // Proof Size summary in bytes: + // Measured: `204` + // Estimated: `6070` + // Minimum execution time: 19_370_000 picoseconds. + Weight::from_parts(19_928_000, 0) + .saturating_add(Weight::from_parts(0, 6070)) + .saturating_add(RocksDbWeight::get().reads(2)) + .saturating_add(RocksDbWeight::get().writes(1)) + } + fn close_bridge() -> Weight { + // Proof Size summary in bytes: + // Measured: `204` + // Estimated: `6070` + // Minimum execution time: 20_045_000 picoseconds. + Weight::from_parts(20_861_000, 0) + .saturating_add(Weight::from_parts(0, 6070)) + .saturating_add(RocksDbWeight::get().reads(2)) + .saturating_add(RocksDbWeight::get().writes(1)) + } + fn update_notification_receiver() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `3530` + // Minimum execution time: 12_179_000 picoseconds. + Weight::from_parts(12_679_000, 0) + .saturating_add(Weight::from_parts(0, 3530)) + .saturating_add(RocksDbWeight::get().reads(1)) + .saturating_add(RocksDbWeight::get().writes(1)) + } +} diff --git a/bridges/primitives/xcm-bridge-router/Cargo.toml b/bridges/primitives/xcm-bridge-router/Cargo.toml new file mode 100644 index 000000000000..655f9fb073bd --- /dev/null +++ b/bridges/primitives/xcm-bridge-router/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "bp-xcm-bridge-router" +description = "Primitives of the xcm-bridge-hub fee pallet." +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +repository.workspace = true + +[lints] +workspace = true + +[dependencies] +codec = { features = ["bit-vec", "derive"], workspace = true } +scale-info = { features = ["bit-vec", "derive"], workspace = true } + +# Substrate Dependencies +sp-core = { workspace = true } +sp-runtime = { workspace = true } + +# Polkadot Dependencies +xcm = { workspace = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "scale-info/std", + "sp-core/std", + "sp-runtime/std", + "xcm/std", +] diff --git a/bridges/primitives/xcm-bridge-router/src/lib.rs b/bridges/primitives/xcm-bridge-router/src/lib.rs new file mode 100644 index 000000000000..fb9ece0b3f6a --- /dev/null +++ b/bridges/primitives/xcm-bridge-router/src/lib.rs @@ -0,0 +1,75 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Primitives of the `xcm-bridge-hub-router` pallet. + +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Decode, Encode, FullCodec, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_core::sp_std::fmt::Debug; +use sp_runtime::{FixedU128, RuntimeDebug}; +use xcm::latest::prelude::{InteriorLocation, Location, NetworkId}; + +/// Current status of the bridge. +#[derive(Clone, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen, RuntimeDebug)] +pub struct BridgeState { + /// Current delivery fee factor. + pub delivery_fee_factor: FixedU128, + /// Bridge congestion flag. + pub is_congested: bool, +} + +/// Trait that resolves a specific `BridgeId` for `dest`. +pub trait ResolveBridgeId { + /// Bridge identifier. + type BridgeId: FullCodec + MaxEncodedLen + TypeInfo + Debug + Clone + PartialEq + Eq; + /// Resolves `Self::BridgeId` for `dest`. If `None`, it means there is no supported bridge ID. + fn resolve_for_dest(bridged_dest: &Location) -> Option; + + /// Resolves `Self::BridgeId` for `bridged_network` and `bridged_dest`. If `None`, it means + /// there is no supported bridge ID. + fn resolve_for( + bridged_network: &NetworkId, + bridged_dest: &InteriorLocation, + ) -> Option; +} + +/// The default implementation of `ResolveBridgeId` for `()` returns `None`. +impl ResolveBridgeId for () { + type BridgeId = (); + + fn resolve_for_dest(_dest: &Location) -> Option { + None + } + + fn resolve_for( + _bridged_network: &NetworkId, + _bridged_dest: &InteriorLocation, + ) -> Option { + None + } +} + +// TODO:revert-for-depracated: BridgeId->H256 - FAIL-CI and keep `build_congestion_message` only for deprecated router +/// A minimized version of `pallet-xcm-bridge-hub-router::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum XcmBridgeHubRouterCall { + /// `pallet-xcm-bridge-hub-router::Call::report_bridge_status` + #[codec(index = 0)] + report_bridge_status { bridge_id: BridgeId, is_congested: bool }, +} diff --git a/bridges/primitives/xcm-bridge/Cargo.toml b/bridges/primitives/xcm-bridge/Cargo.toml new file mode 100644 index 000000000000..a324729b9154 --- /dev/null +++ b/bridges/primitives/xcm-bridge/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "bp-xcm-bridge" +description = "Primitives of the xcm-bridge-hub pallet." +version = "0.2.0" +authors.workspace = true +edition.workspace = true +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +repository.workspace = true + +[lints] +workspace = true + +[dependencies] +codec = { features = ["derive"], workspace = true } +impl-trait-for-tuples = { workspace = true } +scale-info = { features = ["derive"], workspace = true } +serde = { features = ["alloc", "derive"], workspace = true } + +# Bridge Dependencies +bp-messages = { workspace = true } +bp-runtime = { workspace = true } + +# Substrate Dependencies +frame-support = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-std = { workspace = true } + +# Polkadot Dependencies +xcm = { workspace = true } + +[features] +default = ["std"] +std = [ + "bp-messages/std", + "bp-runtime/std", + "codec/std", + "frame-support/std", + "scale-info/std", + "serde/std", + "sp-core/std", + "sp-io/std", + "sp-std/std", + "xcm/std", +] diff --git a/bridges/primitives/xcm-bridge/src/call_info.rs b/bridges/primitives/xcm-bridge/src/call_info.rs new file mode 100644 index 000000000000..d040746fae9b --- /dev/null +++ b/bridges/primitives/xcm-bridge/src/call_info.rs @@ -0,0 +1,47 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Defines structures related to calls of the `pallet-xcm-bridge` pallet. + +use crate::Receiver; +use bp_messages::MessageNonce; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_std::boxed::Box; +use xcm::prelude::VersionedInteriorLocation; + +/// A minimized version of `pallet_xcm_bridge::Call` that can be used without a runtime. +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum XcmBridgeCall { + /// `pallet_xcm_bridge::Call::open_bridge` + #[codec(index = 0)] + open_bridge { + /// Universal `InteriorLocation` from the bridged consensus. + bridge_destination_universal_location: Box, + /// Optional `maybe_notify` holds data about the `bridge_origin_relative_location` where + /// notifications can be sent to handle congestion. + maybe_notify: Option, + }, + /// `pallet_xcm_bridge::Call::close_bridge` + #[codec(index = 1)] + close_bridge { + /// Universal `InteriorLocation` from the bridged consensus. + bridge_destination_universal_location: Box, + /// The number of messages that we may prune in a single call. + may_prune_messages: MessageNonce, + }, +} diff --git a/bridges/primitives/xcm-bridge/src/lib.rs b/bridges/primitives/xcm-bridge/src/lib.rs new file mode 100644 index 000000000000..b791b71a1dc3 --- /dev/null +++ b/bridges/primitives/xcm-bridge/src/lib.rs @@ -0,0 +1,775 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Bridges Common is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Bridges Common. If not, see . + +//! Primitives of the xcm-bridge-hub pallet. + +#![warn(missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] + +use bp_messages::LaneIdType; +use bp_runtime::{AccountIdOf, BalanceOf, Chain}; +pub use call_info::XcmBridgeCall; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{ + ensure, sp_runtime::RuntimeDebug, CloneNoBound, PalletError, PartialEqNoBound, + RuntimeDebugNoBound, +}; +use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; +use sp_core::H256; +use sp_io::hashing::blake2_256; +use sp_std::boxed::Box; +use xcm::{ + latest::prelude::*, prelude::XcmVersion, IntoVersion, VersionedInteriorLocation, + VersionedLocation, +}; + +mod call_info; + +/// Encoded XCM blob. We expect the bridge messages pallet to use this blob type for both inbound +/// and outbound payloads. +pub type XcmAsPlainPayload = sp_std::vec::Vec; + +/// Bridge identifier - used **only** for communicating with sibling/parent chains in the same +/// consensus. +/// +/// For example, `SendXcm` implementations (which use the `latest` XCM) can use it to identify a +/// bridge and the corresponding `LaneId` that is used for over-consensus communication between +/// bridge hubs. +/// +/// This identifier is constructed from the `latest` XCM, so it is expected to ensure migration to +/// the `latest` XCM version. This could change the `BridgeId`, but it will not affect the `LaneId`. +/// In other words, `LaneId` will never change, while `BridgeId` could change with (every) XCM +/// upgrade. +#[derive( + Clone, + Copy, + Decode, + Encode, + Eq, + Ord, + PartialOrd, + PartialEq, + TypeInfo, + MaxEncodedLen, + Serialize, + Deserialize, +)] +pub struct BridgeId(H256); + +impl BridgeId { + /// Create bridge identifier from two universal locations. + /// + /// Note: The `BridgeId` is constructed from `latest` XCM, so if stored, you need to ensure + /// compatibility with newer XCM versions. + pub fn new( + universal_source: &InteriorLocation, + universal_destination: &InteriorLocation, + ) -> Self { + const VALUES_SEPARATOR: [u8; 33] = *b"bridges-bridge-id-value-separator"; + + BridgeId( + (universal_source, VALUES_SEPARATOR, universal_destination) + .using_encoded(blake2_256) + .into(), + ) + } + + /// Access the inner representation. + pub fn inner(&self) -> H256 { + self.0 + } +} + +impl core::fmt::Debug for BridgeId { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::fmt::Debug::fmt(&self.0, f) + } +} + +/// Local XCM channel manager. +pub trait LocalXcmChannelManager { + /// Error that may be returned when suspending/resuming the bridge. + type Error: sp_std::fmt::Debug; + + /// Suspend the bridge, opened by given origin. + /// + /// The `local_origin` is guaranteed to be in the same consensus. However, it may point to + /// something below the chain level - like the contract or pallet instance, for example. + fn suspend_bridge(local_origin: &Location, bridge: Bridge) -> Result<(), Self::Error>; + + /// Resume the previously suspended bridge, opened by given origin. + /// + /// The `local_origin` is guaranteed to be in the same consensus. However, it may point to + /// something below the chain level - like the contract or pallet instance, for example. + fn resume_bridge(local_origin: &Location, bridge: Bridge) -> Result<(), Self::Error>; +} + +impl LocalXcmChannelManager for () { + type Error = (); + + fn suspend_bridge(_local_origin: &Location, _bridge: Bridge) -> Result<(), Self::Error> { + Ok(()) + } + + fn resume_bridge(_local_origin: &Location, _bridge: Bridge) -> Result<(), Self::Error> { + Ok(()) + } +} + +/// Channel status provider that may report whether it is congested or not. +pub trait ChannelStatusProvider { + /// Returns true if the channel is currently active and can be used. + fn is_congested(with: &Location) -> bool; +} + +/// Tuple implementation of `ChannelStatusProvider`, by default indicating no congestion. +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl ChannelStatusProvider for Tuple { + fn is_congested(with: &Location) -> bool { + for_tuples!( #( + if Tuple::is_congested(with) { + return true; + } + )* ); + + false + } +} + +/// Bridge state. +#[derive(Clone, Copy, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen, RuntimeDebug)] +pub enum BridgeState { + /// Bridge is opened. Associated lanes are also opened. + Opened, + /// Bridge is suspended. Associated lanes are opened. + /// *suspended* means that we have sent the "Suspended" message/signal to the local bridge + /// origin. + /// + /// We keep accepting messages to the bridge to allow any inflight messages to be processed. + SoftSuspended, + /// Bridge is suspended and new messages are now being actively dropped. + HardSuspended, + /// Bridge is closed. Associated lanes are also closed. + /// After all outbound messages will be pruned, the bridge will vanish without any traces. + Closed, +} + +/// Bridge metadata. +#[derive( + CloneNoBound, Decode, Encode, Eq, PartialEqNoBound, TypeInfo, MaxEncodedLen, RuntimeDebugNoBound, +)] +#[scale_info(skip_type_params(ThisChain, LaneId))] +pub struct Bridge { + /// Relative location of the bridge origin chain. This is expected to be **convertible** to the + /// `latest` XCM, so the check and migration needs to be ensured. + pub bridge_origin_relative_location: Box, + + /// See [`BridgeLocations::bridge_origin_universal_location`]. + /// Stored for `BridgeId` sanity check. + pub bridge_origin_universal_location: Box, + /// See [`BridgeLocations::bridge_destination_universal_location`]. + /// Stored for `BridgeId` sanity check. + pub bridge_destination_universal_location: Box, + + /// Current bridge state. + pub state: BridgeState, + + /// Reserved amount on the sovereign account of the sibling bridge origin. + /// The account is derived from `self.bridge_origin_relative_location`. + pub deposit: Option>, + + /// Mapping to the unique `LaneId`. + pub lane_id: LaneId, + + /// Holds data about the `bridge_origin_relative_location` where notifications can be sent for + /// handling congestion. + pub maybe_notify: Option, +} + +/// Receiver metadata. +#[derive( + CloneNoBound, + Decode, + Encode, + Eq, + PartialEqNoBound, + TypeInfo, + MaxEncodedLen, + RuntimeDebugNoBound, + Serialize, + Deserialize, +)] +pub struct Receiver { + /// Pallet index. + pub pallet_index: u8, + /// Call/extrinsic index. + pub call_index: u8, +} + +impl Receiver { + /// Create a new receiver. + pub fn new(pallet_index: u8, call_index: u8) -> Self { + Self { pallet_index, call_index } + } +} + +/// An alias for the bridge deposit of `ThisChain`. +pub type DepositOf = Deposit, BalanceOf>; + +/// A structure containing information about from whom the deposit is reserved. +#[derive(Clone, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen, RuntimeDebug)] +pub struct Deposit { + /// Account with the reserved funds. + pub account: AccountId, + /// Reserved amount. + pub amount: Balance, +} + +impl Deposit { + /// Create new deposit. + pub fn new(account: AccountId, amount: Balance) -> Self { + Self { account, amount } + } +} + +/// Locations of bridge endpoints at both sides of the bridge. +#[derive(Clone, RuntimeDebug, PartialEq, Eq)] +pub struct BridgeLocations { + /// Relative (to this bridge hub) location of this side of the bridge. + bridge_origin_relative_location: Location, + /// Universal (unique) location of this side of the bridge. + bridge_origin_universal_location: InteriorLocation, + /// Universal (unique) location of other side of the bridge. + bridge_destination_universal_location: InteriorLocation, + /// An identifier of the dedicated bridge message lane. + bridge_id: BridgeId, +} + +/// Errors that may happen when we check bridge locations. +#[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo)] +pub enum BridgeLocationsError { + /// Origin or destination locations are not universal. + NonUniversalLocation, + /// Bridge origin location is not supported. + InvalidBridgeOrigin, + /// Bridge destination is not supported (in general). + InvalidBridgeDestination, + /// Destination location is within the same global consensus. + DestinationIsLocal, + /// Destination network is not the network we are bridged with. + UnreachableDestination, + /// Destination location is unsupported. We only support bridges with relay + /// chain or its parachains. + UnsupportedDestinationLocation, + /// The version of XCM location argument is unsupported. + UnsupportedXcmVersion, + /// The `LaneIdType` generator is not supported. + UnsupportedLaneIdType, +} + +impl BridgeLocations { + /// Given XCM locations, generate lane id and universal locations of bridge endpoints. + /// + /// The `here_universal_location` is the universal location of the bridge hub runtime. + /// + /// The `bridge_origin_relative_location` is the relative (to the `here_universal_location`) + /// location of the bridge endpoint at this side of the bridge. It may be the parent relay + /// chain or the sibling parachain. All junctions below parachain level are dropped. + /// + /// The `bridge_destination_universal_location` is the universal location of the bridge + /// destination. It may be the parent relay or the sibling parachain of the **bridged** + /// bridge hub. All junctions below parachain level are dropped. + /// + /// Why we drop all junctions between parachain level - that's because the lane is a bridge + /// between two chains. All routing under this level happens when the message is delivered + /// to the bridge destination. So at bridge level we don't care about low level junctions. + /// + /// Returns error if `bridge_origin_relative_location` is outside of `here_universal_location` + /// local consensus OR if `bridge_destination_universal_location` is not a universal location. + pub fn bridge_locations( + here_universal_location: InteriorLocation, + bridge_origin_relative_location: Location, + bridge_destination_universal_location: InteriorLocation, + expected_remote_network: NetworkId, + ) -> Result, BridgeLocationsError> { + fn strip_low_level_junctions( + location: InteriorLocation, + ) -> Result { + let mut junctions = location.into_iter(); + + let global_consensus = junctions + .next() + .filter(|junction| matches!(junction, GlobalConsensus(_))) + .ok_or(BridgeLocationsError::NonUniversalLocation)?; + + // we only expect `Parachain` junction here. There are other junctions that + // may need to be supported (like `GeneralKey` and `OnlyChild`), but now we + // only support bridges with relay and parachans + // + // if there's something other than parachain, let's strip it + let maybe_parachain = + junctions.next().filter(|junction| matches!(junction, Parachain(_))); + Ok(match maybe_parachain { + Some(parachain) => [global_consensus, parachain].into(), + None => [global_consensus].into(), + }) + } + + // ensure that the `here_universal_location` and `bridge_destination_universal_location` + // are universal locations within different consensus systems + let local_network = here_universal_location + .global_consensus() + .map_err(|_| BridgeLocationsError::NonUniversalLocation)?; + let remote_network = bridge_destination_universal_location + .global_consensus() + .map_err(|_| BridgeLocationsError::NonUniversalLocation)?; + ensure!(local_network != remote_network, BridgeLocationsError::DestinationIsLocal); + ensure!( + remote_network == expected_remote_network, + BridgeLocationsError::UnreachableDestination + ); + + // get universal location of endpoint, located at this side of the bridge + let bridge_origin_universal_location = here_universal_location + .within_global(bridge_origin_relative_location.clone()) + .map_err(|_| BridgeLocationsError::InvalidBridgeOrigin)?; + // strip low-level junctions within universal locations + let bridge_origin_universal_location = + strip_low_level_junctions(bridge_origin_universal_location)?; + let bridge_destination_universal_location = + strip_low_level_junctions(bridge_destination_universal_location)?; + + // we know that the `bridge_destination_universal_location` starts from the + // `GlobalConsensus` and we know that the `bridge_origin_universal_location` + // is also within the `GlobalConsensus`. So we know that the lane id will be + // the same on both ends of the bridge + let bridge_id = BridgeId::new( + &bridge_origin_universal_location, + &bridge_destination_universal_location, + ); + + Ok(Box::new(BridgeLocations { + bridge_origin_relative_location, + bridge_origin_universal_location, + bridge_destination_universal_location, + bridge_id, + })) + } + + /// Getter for `bridge_origin_relative_location` + pub fn bridge_origin_relative_location(&self) -> &Location { + &self.bridge_origin_relative_location + } + + /// Getter for `bridge_origin_universal_location` + pub fn bridge_origin_universal_location(&self) -> &InteriorLocation { + &self.bridge_origin_universal_location + } + + /// Getter for `bridge_destination_universal_location` + pub fn bridge_destination_universal_location(&self) -> &InteriorLocation { + &self.bridge_destination_universal_location + } + + /// Getter for `bridge_id` + pub fn bridge_id(&self) -> &BridgeId { + &self.bridge_id + } + + /// Generates the exact same `LaneId` on the both bridge hubs. + /// + /// Note: Use this **only** when opening a new bridge. + pub fn calculate_lane_id( + &self, + xcm_version: XcmVersion, + ) -> Result { + // a tricky helper struct that adds required `Ord` support for + // `VersionedInteriorLocation` + #[derive(Eq, PartialEq, Ord, PartialOrd)] + struct EncodedVersionedInteriorLocation(sp_std::vec::Vec); + impl Encode for EncodedVersionedInteriorLocation { + fn encode(&self) -> sp_std::vec::Vec { + self.0.clone() + } + } + + let universal_location1 = + VersionedInteriorLocation::from(self.bridge_origin_universal_location.clone()) + .into_version(xcm_version) + .map_err(|_| BridgeLocationsError::UnsupportedXcmVersion); + let universal_location2 = + VersionedInteriorLocation::from(self.bridge_destination_universal_location.clone()) + .into_version(xcm_version) + .map_err(|_| BridgeLocationsError::UnsupportedXcmVersion); + + LaneId::try_new( + EncodedVersionedInteriorLocation(universal_location1.encode()), + EncodedVersionedInteriorLocation(universal_location2.encode()), + ) + .map_err(|_| BridgeLocationsError::UnsupportedLaneIdType) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use xcm::latest::ROCOCO_GENESIS_HASH; + + const LOCAL_NETWORK: NetworkId = Kusama; + const REMOTE_NETWORK: NetworkId = Polkadot; + const UNREACHABLE_NETWORK: NetworkId = NetworkId::ByGenesis(ROCOCO_GENESIS_HASH); + const SIBLING_PARACHAIN: u32 = 1000; + const LOCAL_BRIDGE_HUB: u32 = 1001; + const REMOTE_PARACHAIN: u32 = 2000; + + struct SuccessfulTest { + here_universal_location: InteriorLocation, + bridge_origin_relative_location: Location, + + bridge_origin_universal_location: InteriorLocation, + bridge_destination_universal_location: InteriorLocation, + + expected_remote_network: NetworkId, + } + + fn run_successful_test(test: SuccessfulTest) -> BridgeLocations { + let locations = BridgeLocations::bridge_locations( + test.here_universal_location, + test.bridge_origin_relative_location.clone(), + test.bridge_destination_universal_location.clone(), + test.expected_remote_network, + ); + assert_eq!( + locations, + Ok(Box::new(BridgeLocations { + bridge_origin_relative_location: test.bridge_origin_relative_location, + bridge_origin_universal_location: test.bridge_origin_universal_location.clone(), + bridge_destination_universal_location: test + .bridge_destination_universal_location + .clone(), + bridge_id: BridgeId::new( + &test.bridge_origin_universal_location, + &test.bridge_destination_universal_location, + ), + })), + ); + + *locations.unwrap() + } + + // successful tests that with various origins and destinations + + #[test] + fn at_relay_from_local_relay_to_remote_relay_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: Here.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_relay_from_sibling_parachain_to_remote_relay_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: [Parachain(SIBLING_PARACHAIN)].into(), + + bridge_origin_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_relay_from_local_relay_to_remote_parachain_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: Here.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_relay_from_sibling_parachain_to_remote_parachain_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: [Parachain(SIBLING_PARACHAIN)].into(), + + bridge_origin_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_bridge_hub_from_local_relay_to_remote_relay_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: Parent.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_bridge_hub_from_sibling_parachain_to_remote_relay_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: ParentThen([Parachain(SIBLING_PARACHAIN)].into()) + .into(), + + bridge_origin_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_bridge_hub_from_local_relay_to_remote_parachain_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: Parent.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + #[test] + fn at_bridge_hub_from_sibling_parachain_to_remote_parachain_works() { + run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: ParentThen([Parachain(SIBLING_PARACHAIN)].into()) + .into(), + + bridge_origin_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + + expected_remote_network: REMOTE_NETWORK, + }); + } + + // successful tests that show that we are ignoring low-level junctions of bridge origins + + #[test] + fn low_level_junctions_at_bridge_origin_are_stripped() { + let locations1 = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: Here.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + let locations2 = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: [PalletInstance(0)].into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + + assert_eq!(locations1.bridge_id, locations2.bridge_id); + } + + #[test] + fn low_level_junctions_at_bridge_destination_are_stripped() { + let locations1 = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: Here.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + let locations2 = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_origin_relative_location: Here.into(), + + bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(), + bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(), + + expected_remote_network: REMOTE_NETWORK, + }); + + assert_eq!(locations1.bridge_id, locations2.bridge_id); + } + + #[test] + fn calculate_lane_id_works() { + type TestLaneId = bp_messages::HashedLaneId; + + let from_local_to_remote = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: ParentThen([Parachain(SIBLING_PARACHAIN)].into()) + .into(), + + bridge_origin_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + + expected_remote_network: REMOTE_NETWORK, + }); + + let from_remote_to_local = run_successful_test(SuccessfulTest { + here_universal_location: [GlobalConsensus(REMOTE_NETWORK), Parachain(LOCAL_BRIDGE_HUB)] + .into(), + bridge_origin_relative_location: ParentThen([Parachain(REMOTE_PARACHAIN)].into()) + .into(), + + bridge_origin_universal_location: [ + GlobalConsensus(REMOTE_NETWORK), + Parachain(REMOTE_PARACHAIN), + ] + .into(), + bridge_destination_universal_location: [ + GlobalConsensus(LOCAL_NETWORK), + Parachain(SIBLING_PARACHAIN), + ] + .into(), + + expected_remote_network: LOCAL_NETWORK, + }); + + assert_ne!( + from_local_to_remote.calculate_lane_id::(xcm::latest::VERSION), + from_remote_to_local.calculate_lane_id::(xcm::latest::VERSION - 1), + ); + assert_eq!( + from_local_to_remote.calculate_lane_id::(xcm::latest::VERSION), + from_remote_to_local.calculate_lane_id::(xcm::latest::VERSION), + ); + } + + // negative tests + + #[test] + fn bridge_locations_fails_when_here_is_not_universal_location() { + assert_eq!( + BridgeLocations::bridge_locations( + [Parachain(1000)].into(), + Here.into(), + [GlobalConsensus(REMOTE_NETWORK)].into(), + REMOTE_NETWORK, + ), + Err(BridgeLocationsError::NonUniversalLocation), + ); + } + + #[test] + fn bridge_locations_fails_when_computed_destination_is_not_universal_location() { + assert_eq!( + BridgeLocations::bridge_locations( + [GlobalConsensus(LOCAL_NETWORK)].into(), + Here.into(), + [OnlyChild].into(), + REMOTE_NETWORK, + ), + Err(BridgeLocationsError::NonUniversalLocation), + ); + } + + #[test] + fn bridge_locations_fails_when_computed_destination_is_local() { + assert_eq!( + BridgeLocations::bridge_locations( + [GlobalConsensus(LOCAL_NETWORK)].into(), + Here.into(), + [GlobalConsensus(LOCAL_NETWORK), OnlyChild].into(), + REMOTE_NETWORK, + ), + Err(BridgeLocationsError::DestinationIsLocal), + ); + } + + #[test] + fn bridge_locations_fails_when_computed_destination_is_unreachable() { + assert_eq!( + BridgeLocations::bridge_locations( + [GlobalConsensus(LOCAL_NETWORK)].into(), + Here.into(), + [GlobalConsensus(UNREACHABLE_NETWORK)].into(), + REMOTE_NETWORK, + ), + Err(BridgeLocationsError::UnreachableDestination), + ); + } +} diff --git a/cumulus/pallets/xcmp-queue/Cargo.toml b/cumulus/pallets/xcmp-queue/Cargo.toml index 43dfae8927d2..b727e2d776b9 100644 --- a/cumulus/pallets/xcmp-queue/Cargo.toml +++ b/cumulus/pallets/xcmp-queue/Cargo.toml @@ -39,7 +39,7 @@ bounded-collections = { workspace = true } frame-benchmarking = { optional = true, workspace = true } # Bridges -bp-xcm-bridge-hub-router = { optional = true, workspace = true } +bp-xcm-bridge = { optional = true, workspace = true } [dev-dependencies] @@ -55,7 +55,7 @@ cumulus-pallet-parachain-system = { workspace = true, default-features = true } default = ["std"] std = [ "bounded-collections/std", - "bp-xcm-bridge-hub-router?/std", + "bp-xcm-bridge?/std", "codec/std", "cumulus-primitives-core/std", "frame-benchmarking?/std", @@ -99,4 +99,4 @@ try-runtime = [ "polkadot-runtime-parachains/try-runtime", "sp-runtime/try-runtime", ] -bridging = ["bp-xcm-bridge-hub-router"] +bridging = ["bp-xcm-bridge"] diff --git a/cumulus/pallets/xcmp-queue/src/bridging.rs b/cumulus/pallets/xcmp-queue/src/bridging.rs index 8ed11505a27a..6cd53acf39d6 100644 --- a/cumulus/pallets/xcmp-queue/src/bridging.rs +++ b/cumulus/pallets/xcmp-queue/src/bridging.rs @@ -17,38 +17,10 @@ use crate::{pallet, OutboundState}; use cumulus_primitives_core::ParaId; use xcm::latest::prelude::*; -/// Adapter implementation for `bp_xcm_bridge_hub_router::XcmChannelStatusProvider` which checks -/// both `OutboundXcmpStatus` and `InboundXcmpStatus` for defined `Location` if any of those is -/// suspended. -pub struct InAndOutXcmpChannelStatusProvider(core::marker::PhantomData); -impl bp_xcm_bridge_hub_router::XcmChannelStatusProvider - for InAndOutXcmpChannelStatusProvider -{ - fn is_congested(with: &Location) -> bool { - // handle congestion only for a sibling parachain locations. - let sibling_para_id: ParaId = match with.unpack() { - (_, [Parachain(para_id)]) => (*para_id).into(), - _ => return false, - }; - - // if the inbound channel with recipient is suspended, it means that we are unable to - // receive congestion reports from the `with` location. So we assume the pipeline is - // congested too. - if pallet::Pallet::::is_inbound_channel_suspended(sibling_para_id) { - return true - } - - // if the outbound channel with recipient is suspended, it means that one of further - // queues (e.g. bridge queue between two bridge hubs) is overloaded, so we shall - // take larger fee for our outbound messages - OutXcmpChannelStatusProvider::::is_congested(with) - } -} - -/// Adapter implementation for `bp_xcm_bridge_hub_router::XcmChannelStatusProvider` which checks +/// Adapter implementation for `bp_xcm_bridge::ChannelStatusProvider` which checks /// only `OutboundXcmpStatus` for defined `SiblingParaId` if is suspended. pub struct OutXcmpChannelStatusProvider(core::marker::PhantomData); -impl bp_xcm_bridge_hub_router::XcmChannelStatusProvider +impl bp_xcm_bridge::ChannelStatusProvider for OutXcmpChannelStatusProvider { fn is_congested(with: &Location) -> bool { diff --git a/cumulus/pallets/xcmp-queue/src/lib.rs b/cumulus/pallets/xcmp-queue/src/lib.rs index 91f71558b54a..80ba902539be 100644 --- a/cumulus/pallets/xcmp-queue/src/lib.rs +++ b/cumulus/pallets/xcmp-queue/src/lib.rs @@ -691,11 +691,6 @@ impl Pallet { .max(::WeightInfo::on_idle_large_msg()) } - #[cfg(feature = "bridging")] - fn is_inbound_channel_suspended(sender: ParaId) -> bool { - >::get().iter().any(|c| c == &sender) - } - #[cfg(feature = "bridging")] /// Returns tuple of `OutboundState` and number of queued pages. fn outbound_channel_state(target: ParaId) -> Option<(OutboundState, u16)> { diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/genesis.rs index 62b2e4eed9e7..59063c72ecaf 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo/src/genesis.rs @@ -76,6 +76,7 @@ pub fn genesis() -> Storage { Location::new(1, [Parachain(1000)]), Junctions::from([ByGenesis(WESTEND_GENESIS_HASH).into(), Parachain(1000)]), Some(bp_messages::LegacyLaneId([0, 0, 0, 2])), + None, ), ], ..Default::default() diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs index 5286110bcab9..37d989e5b2be 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend/src/genesis.rs @@ -79,6 +79,7 @@ pub fn genesis() -> Storage { Parachain(1000), ]), Some(bp_messages::LegacyLaneId([0, 0, 0, 2])), + None, ), ], ..Default::default() diff --git a/cumulus/parachains/integration-tests/emulated/common/Cargo.toml b/cumulus/parachains/integration-tests/emulated/common/Cargo.toml index 4bd45ef1a87c..92f2edba73b5 100644 --- a/cumulus/parachains/integration-tests/emulated/common/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/common/Cargo.toml @@ -45,6 +45,7 @@ xcm-emulator = { workspace = true, default-features = true } # Bridges bp-messages = { workspace = true, default-features = true } -bp-xcm-bridge-hub = { workspace = true, default-features = true } +bp-xcm-bridge = { workspace = true, default-features = true } pallet-bridge-messages = { workspace = true, default-features = true } +pallet-xcm-bridge = { workspace = true, default-features = true } pallet-xcm-bridge-hub = { workspace = true, default-features = true } diff --git a/cumulus/parachains/integration-tests/emulated/common/src/impls.rs b/cumulus/parachains/integration-tests/emulated/common/src/impls.rs index 9dad323aa19c..c8c0d2650f1e 100644 --- a/cumulus/parachains/integration-tests/emulated/common/src/impls.rs +++ b/cumulus/parachains/integration-tests/emulated/common/src/impls.rs @@ -63,17 +63,12 @@ use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, MessageKey, OutboundLaneData, }; -pub use bp_xcm_bridge_hub::XcmBridgeHubCall; +pub use bp_xcm_bridge::{Receiver, XcmBridgeCall}; use pallet_bridge_messages::{Config as BridgeMessagesConfig, LaneIdOf, OutboundLanes, Pallet}; pub use pallet_bridge_messages::{ Instance1 as BridgeMessagesInstance1, Instance2 as BridgeMessagesInstance2, Instance3 as BridgeMessagesInstance3, }; -use pallet_xcm_bridge_hub::XcmBlobMessageDispatchResult; - -pub struct BridgeHubMessageHandler { - _marker: std::marker::PhantomData<(S, SI, T, TI)>, -} struct LaneIdWrapper(LaneId); impl From> for BridgeLaneId { @@ -87,6 +82,11 @@ impl From for LaneIdWrapper { } } +/// Implementation of `BridgeMessageHandler` for `pallet_bridge_messages`, +/// configured with `pallet_xcm_bridge_hub`. +pub struct BridgeHubMessageHandler { + _marker: std::marker::PhantomData<(S, SI, T, TI)>, +} impl BridgeMessageHandler for BridgeHubMessageHandler where S: BridgeMessagesConfig, @@ -95,7 +95,7 @@ where TI: 'static, >::InboundPayload: From>, >::MessageDispatch: - MessageDispatch, + MessageDispatch, { fn get_source_outbound_messages() -> Vec { // get the source active outbound lanes @@ -141,6 +141,94 @@ where data: DispatchMessageData::> { payload }, }); + use pallet_xcm_bridge_hub::XcmBlobMessageDispatchResult; + + let result = match dispatch_result.dispatch_level_result { + XcmBlobMessageDispatchResult::Dispatched => Ok(()), + XcmBlobMessageDispatchResult::InvalidPayload => Err(BridgeMessageDispatchError( + Box::new(XcmBlobMessageDispatchResult::InvalidPayload), + )), + XcmBlobMessageDispatchResult::NotDispatched(e) => Err(BridgeMessageDispatchError( + Box::new(XcmBlobMessageDispatchResult::NotDispatched(e)), + )), + }; + result + } + + fn notify_source_message_delivery(lane_id: BridgeLaneId) { + let lane_id: LaneIdOf = LaneIdWrapper::from(lane_id).0; + let data = OutboundLanes::::get(lane_id).unwrap(); + let new_data = OutboundLaneData { + oldest_unpruned_nonce: data.oldest_unpruned_nonce + 1, + latest_received_nonce: data.latest_received_nonce + 1, + ..data + }; + + OutboundLanes::::insert(lane_id, new_data); + } +} + +/// Implementation of `BridgeMessageHandler` for `pallet_bridge_messages`, +/// configured with `pallet_xcm_bridge`. +pub struct BridgeMessagesHandler { + _marker: std::marker::PhantomData<(S, SI, T, TI)>, +} +impl BridgeMessageHandler for BridgeMessagesHandler +where + S: BridgeMessagesConfig, + SI: 'static, + T: BridgeMessagesConfig, + TI: 'static, + >::InboundPayload: From>, + >::MessageDispatch: + MessageDispatch, +{ + fn get_source_outbound_messages() -> Vec { + // get the source active outbound lanes + let active_outbound_lanes = OutboundLanes::::iter_keys(); + + let mut messages: Vec = Default::default(); + + // collect messages from `OutboundMessages` for each active outbound lane in the source + for lane in active_outbound_lanes { + let latest_generated_nonce = + OutboundLanes::::get(lane).unwrap().latest_generated_nonce; + let latest_received_nonce = + OutboundLanes::::get(lane).unwrap().latest_received_nonce; + + (latest_received_nonce + 1..=latest_generated_nonce).for_each(|nonce| { + let encoded_payload: Vec = Pallet::::outbound_message_data(lane, nonce) + .expect("Bridge message does not exist") + .into(); + let payload = Vec::::decode(&mut &encoded_payload[..]) + .expect("Decoding XCM message failed"); + let message = BridgeMessage { lane_id: LaneIdWrapper(lane).into(), nonce, payload }; + + messages.push(message); + }); + } + messages + } + + fn dispatch_target_inbound_message( + message: BridgeMessage, + ) -> Result<(), BridgeMessageDispatchError> { + type TargetMessageDispatch = >::MessageDispatch; + type InboundPayload = >::InboundPayload; + + let lane_id = LaneIdWrapper::from(message.lane_id).0; + let nonce = message.nonce; + let payload = Ok(From::from(message.payload)); + + // Directly dispatch outbound messages assuming everything is correct + // and bypassing the `Relayers` and `InboundLane` logic + let dispatch_result = TargetMessageDispatch::::dispatch(DispatchMessage { + key: MessageKey { lane_id, nonce }, + data: DispatchMessageData::> { payload }, + }); + + use pallet_xcm_bridge::XcmBlobMessageDispatchResult; + let result = match dispatch_result.dispatch_level_result { XcmBlobMessageDispatchResult::Dispatched => Ok(()), XcmBlobMessageDispatchResult::InvalidPayload => Err(BridgeMessageDispatchError( @@ -936,25 +1024,27 @@ macro_rules! impl_bridge_helpers_for_chain { ( $chain:ident, $pallet:ident, $pallet_xcm:ident, $runtime_call_wrapper:path ) => { $crate::impls::paste::paste! { impl $chain { - /// Open bridge with `dest`. + /// Open bridge with `dest` (works with `pallet-xcm-bridge`). pub fn open_bridge( bridge_location: $crate::impls::Location, bridge_destination_universal_location: $crate::impls::InteriorLocation, - maybe_paid: Option<($crate::impls::Asset, $crate::impls::AccountId)> + maybe_paid: Option<($crate::impls::Asset, $crate::impls::AccountId)>, + maybe_notify: Option<$crate::impls::Receiver>, ) { ::execute_with(|| { use $crate::impls::{bx, Chain}; - use $crate::impls::XcmBridgeHubCall; + use $crate::impls::XcmBridgeCall; use $crate::impls::Encode; // important to use `root` and `OriginKind::Xcm` let root_origin = ::RuntimeOrigin::root(); // construct call - let call: $crate::impls::DoubleEncoded<()> = $runtime_call_wrapper(XcmBridgeHubCall::open_bridge { + let call: $crate::impls::DoubleEncoded<()> = $runtime_call_wrapper(XcmBridgeCall::open_bridge { bridge_destination_universal_location: bx!( bridge_destination_universal_location.clone().into() - ) + ), + maybe_notify, }).encode().into(); let xcm = if let Some((fee_asset, beneficiary)) = maybe_paid { diff --git a/cumulus/parachains/integration-tests/emulated/networks/rococo-westend-system/src/lib.rs b/cumulus/parachains/integration-tests/emulated/networks/rococo-westend-system/src/lib.rs index d87bc5aa9633..df62a64eb260 100644 --- a/cumulus/parachains/integration-tests/emulated/networks/rococo-westend-system/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/networks/rococo-westend-system/src/lib.rs @@ -32,7 +32,7 @@ use westend_emulated_chain::Westend; // Cumulus use emulated_integration_tests_common::{ accounts::{ALICE, BOB}, - impls::{BridgeHubMessageHandler, BridgeMessagesInstance1, BridgeMessagesInstance3}, + impls::{BridgeMessagesHandler, BridgeMessagesInstance1, BridgeMessagesInstance3}, xcm_emulator::{ decl_test_bridges, decl_test_networks, decl_test_sender_receiver_accounts_parameter_types, Chain, @@ -76,13 +76,13 @@ decl_test_bridges! { type BridgeHubRococoRuntime = ::Runtime; type BridgeHubWestendRuntime = ::Runtime; -pub type RococoWestendMessageHandler = BridgeHubMessageHandler< +pub type RococoWestendMessageHandler = BridgeMessagesHandler< BridgeHubRococoRuntime, BridgeMessagesInstance3, BridgeHubWestendRuntime, BridgeMessagesInstance1, >; -pub type WestendRococoMessageHandler = BridgeHubMessageHandler< +pub type WestendRococoMessageHandler = BridgeMessagesHandler< BridgeHubWestendRuntime, BridgeMessagesInstance1, BridgeHubRococoRuntime, diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml index 7bb7277df45c..d75169d920fc 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml @@ -32,7 +32,7 @@ xcm-runtime-apis = { workspace = true } # Bridges pallet-bridge-messages = { workspace = true } -pallet-xcm-bridge-hub = { workspace = true } +pallet-xcm-bridge = { workspace = true } # Cumulus cumulus-pallet-xcmp-queue = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml index dc3bbb269d70..3b4bde63fdcc 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/Cargo.toml @@ -33,7 +33,7 @@ xcm-runtime-apis = { workspace = true } # Bridges pallet-bridge-messages = { workspace = true } -pallet-xcm-bridge-hub = { workspace = true } +pallet-xcm-bridge = { workspace = true } # Cumulus asset-hub-westend-runtime = { workspace = true } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index 3da8aa9b6cfe..9b41bba349b2 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -97,11 +97,12 @@ bp-asset-hub-rococo = { workspace = true } bp-asset-hub-westend = { workspace = true } bp-bridge-hub-rococo = { workspace = true } bp-bridge-hub-westend = { workspace = true } -pallet-xcm-bridge-hub-router = { workspace = true } +pallet-xcm-bridge-router = { workspace = true } snowbridge-router-primitives = { workspace = true } [dev-dependencies] asset-test-utils = { workspace = true, default-features = true } +bp-xcm-bridge = { workspace = true } parachains-runtimes-test-utils = { workspace = true, default-features = true } [build-dependencies] @@ -139,7 +140,7 @@ runtime-benchmarks = [ "pallet-uniques/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", - "pallet-xcm-bridge-hub-router/runtime-benchmarks", + "pallet-xcm-bridge-router/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", @@ -181,7 +182,7 @@ try-runtime = [ "pallet-transaction-payment/try-runtime", "pallet-uniques/try-runtime", "pallet-utility/try-runtime", - "pallet-xcm-bridge-hub-router/try-runtime", + "pallet-xcm-bridge-router/try-runtime", "pallet-xcm/try-runtime", "parachain-info/try-runtime", "polkadot-runtime-common/try-runtime", @@ -193,6 +194,7 @@ std = [ "bp-asset-hub-westend/std", "bp-bridge-hub-rococo/std", "bp-bridge-hub-westend/std", + "bp-xcm-bridge/std", "codec/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-parachain-system/std", @@ -234,7 +236,7 @@ std = [ "pallet-uniques/std", "pallet-utility/std", "pallet-xcm-benchmarks?/std", - "pallet-xcm-bridge-hub-router/std", + "pallet-xcm-bridge-router/std", "pallet-xcm/std", "parachain-info/std", "parachains-common/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index 43b7bf0ba118..d60f00f946a6 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -100,7 +100,7 @@ use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; #[cfg(feature = "runtime-benchmarks")] use xcm::latest::prelude::{ Asset, Assets as XcmAssets, Fungible, Here, InteriorLocation, Junction, Junction::*, Location, - NetworkId, NonFungible, Parent, ParentThen, Response, XCM_VERSION, + NetworkId, NonFungible, Parent, ParentThen, Response, }; use xcm::{ latest::prelude::{AssetId, BodyId}, @@ -115,6 +115,7 @@ use xcm_runtime_apis::{ use frame_support::traits::PalletInfoAccess; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; +use xcm_builder::{NetworkExportTable, SovereignPaidRemoteExporter}; impl_opaque_keys! { pub struct SessionKeys { @@ -985,26 +986,40 @@ impl pallet_nfts::Config for Runtime { /// XCM router instance to BridgeHub with bridging capabilities for `Westend` global /// consensus with dynamic fees and back-pressure. -pub type ToWestendXcmRouterInstance = pallet_xcm_bridge_hub_router::Instance3; -impl pallet_xcm_bridge_hub_router::Config for Runtime { +pub type ToWestendXcmRouterInstance = pallet_xcm_bridge_router::Instance3; +impl pallet_xcm_bridge_router::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type WeightInfo = weights::pallet_xcm_bridge_hub_router::WeightInfo; + type WeightInfo = weights::pallet_xcm_bridge_router::WeightInfo; - type UniversalLocation = xcm_config::UniversalLocation; - type SiblingBridgeHubLocation = xcm_config::bridging::SiblingBridgeHub; - type BridgedNetworkId = xcm_config::bridging::to_westend::WestendNetwork; - type Bridges = xcm_config::bridging::NetworkExportTable; type DestinationVersion = PolkadotXcm; - type BridgeHubOrigin = frame_support::traits::EitherOfDiverse< - EnsureRoot, - EnsureXcm>, + // Let's use `SovereignPaidRemoteExporter`, which sends `ExportMessage` over HRMP to the sibling + // BridgeHub. + type MessageExporter = SovereignPaidRemoteExporter< + // `ExporterFor` wrapper handling dynamic fees for congestion. + pallet_xcm_bridge_router::impls::ViaRemoteBridgeHubExporter< + Runtime, + ToWestendXcmRouterInstance, + NetworkExportTable, + xcm_config::bridging::to_westend::WestendNetwork, + xcm_config::bridging::SiblingBridgeHub, + >, + XcmpQueue, + xcm_config::UniversalLocation, >; - type ToBridgeHubSender = XcmpQueue; - type LocalXcmChannelManager = - cumulus_pallet_xcmp_queue::bridging::InAndOutXcmpChannelStatusProvider; + // For congestion - resolves `BridgeId` using the same algorithm as `pallet_xcm_bridge_hub` on + // the BH. + type BridgeIdResolver = pallet_xcm_bridge_router::impls::EnsureIsRemoteBridgeIdResolver< + xcm_config::UniversalLocation, + >; + // For congestion - allow only calls from BH. + type UpdateBridgeStatusOrigin = + AsEnsureOriginWithArg>>; + + // For adding message size fees type ByteFee = xcm_config::bridging::XcmBridgeHubRouterByteFee; + // For adding message size fees type FeeAsset = xcm_config::bridging::XcmBridgeHubRouterFeeAssetId; } @@ -1092,7 +1107,7 @@ construct_runtime!( Proxy: pallet_proxy = 42, // Bridge utilities. - ToWestendXcmRouter: pallet_xcm_bridge_hub_router:: = 45, + ToWestendXcmRouter: pallet_xcm_bridge_router:: = 45, // The main stage. Assets: pallet_assets:: = 50, @@ -1316,7 +1331,7 @@ mod benches { [pallet_collator_selection, CollatorSelection] [cumulus_pallet_parachain_system, ParachainSystem] [cumulus_pallet_xcmp_queue, XcmpQueue] - [pallet_xcm_bridge_hub_router, ToWestend] + [pallet_xcm_bridge_router, ToWestend] [pallet_asset_conversion_ops, AssetConversionMigration] // XCM [pallet_xcm, PalletXcmExtrinsicsBenchmark::] @@ -1654,7 +1669,7 @@ impl_runtime_apis! { use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; - use pallet_xcm_bridge_hub_router::benchmarking::Pallet as XcmBridgeHubRouterBench; + use pallet_xcm_bridge_router::benchmarking::Pallet as XcmBridgeHubRouterBench; // This is defined once again in dispatch_benchmark, because list_benchmarks! // and add_benchmarks! are macros exported by define_benchmarks! macros and those types @@ -1701,7 +1716,7 @@ impl_runtime_apis! { use cumulus_pallet_session_benchmarking::Pallet as SessionBench; impl cumulus_pallet_session_benchmarking::Config for Runtime {} - use pallet_xcm_bridge_hub_router::benchmarking::{ + use pallet_xcm_bridge_router::benchmarking::{ Pallet as XcmBridgeHubRouterBench, Config as XcmBridgeHubRouterConfig, }; @@ -1815,31 +1830,11 @@ impl_runtime_apis! { } impl XcmBridgeHubRouterConfig for Runtime { - fn make_congested() { - cumulus_pallet_xcmp_queue::bridging::suspend_channel_for_benchmarks::( - xcm_config::bridging::SiblingBridgeHubParaId::get().into() - ); - } fn ensure_bridged_target_destination() -> Result { - ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests( - xcm_config::bridging::SiblingBridgeHubParaId::get().into() - ); - let bridged_asset_hub = xcm_config::bridging::to_westend::AssetHubWestend::get(); - let _ = PolkadotXcm::force_xcm_version( - RuntimeOrigin::root(), - alloc::boxed::Box::new(bridged_asset_hub.clone()), - XCM_VERSION, - ).map_err(|e| { - log::error!( - "Failed to dispatch `force_xcm_version({:?}, {:?}, {:?})`, error: {:?}", - RuntimeOrigin::root(), - bridged_asset_hub, - XCM_VERSION, - e - ); - BenchmarkError::Stop("XcmVersion was not stored!") - })?; - Ok(bridged_asset_hub) + Ok(xcm_config::bridging::to_westend::AssetHubWestend::get()) + } + fn update_bridge_status_origin() -> Option { + Some(pallet_xcm::Origin::Xcm(xcm_config::bridging::SiblingBridgeHub::get()).into()) } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/mod.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/mod.rs index 6893766ac72d..c3d4e1974bc2 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/mod.rs @@ -41,7 +41,7 @@ pub mod pallet_transaction_payment; pub mod pallet_uniques; pub mod pallet_utility; pub mod pallet_xcm; -pub mod pallet_xcm_bridge_hub_router; +pub mod pallet_xcm_bridge_router; pub mod paritydb_weights; pub mod rocksdb_weights; pub mod xcm; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm_bridge_hub_router.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm_bridge_hub_router.rs deleted file mode 100644 index 9a75428ada8b..000000000000 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm_bridge_hub_router.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Cumulus. - -// Cumulus is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Cumulus is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Cumulus. If not, see . - -//! Autogenerated weights for `pallet_xcm_bridge_hub_router` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-12-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-acd6uxux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: 1024 - -// Executed Command: -// target/production/polkadot-parachain -// benchmark -// pallet -// --steps=50 -// --repeat=20 -// --extrinsic=* -// --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_xcm_bridge_hub_router -// --chain=asset-hub-rococo-dev -// --header=./cumulus/file_header.txt -// --output=./cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/ - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{traits::Get, weights::Weight}; -use core::marker::PhantomData; - -/// Weight functions for `pallet_xcm_bridge_hub_router`. -pub struct WeightInfo(PhantomData); -impl pallet_xcm_bridge_hub_router::WeightInfo for WeightInfo { - /// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0) - /// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: Some(4002), added: 4497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) - /// Storage: `ToWestendXcmRouter::Bridge` (r:1 w:1) - /// Proof: `ToWestendXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) - fn on_initialize_when_non_congested() -> Weight { - // Proof Size summary in bytes: - // Measured: `154` - // Estimated: `5487` - // Minimum execution time: 13_884_000 picoseconds. - Weight::from_parts(14_312_000, 0) - .saturating_add(Weight::from_parts(0, 5487)) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0) - /// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: Some(4002), added: 4497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) - fn on_initialize_when_congested() -> Weight { - // Proof Size summary in bytes: - // Measured: `144` - // Estimated: `5487` - // Minimum execution time: 6_909_000 picoseconds. - Weight::from_parts(7_115_000, 0) - .saturating_add(Weight::from_parts(0, 5487)) - .saturating_add(T::DbWeight::get().reads(2)) - } - /// Storage: `ToWestendXcmRouter::Bridge` (r:1 w:1) - /// Proof: `ToWestendXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) - fn report_bridge_status() -> Weight { - // Proof Size summary in bytes: - // Measured: `150` - // Estimated: `1502` - // Minimum execution time: 12_394_000 picoseconds. - Weight::from_parts(12_883_000, 0) - .saturating_add(Weight::from_parts(0, 1502)) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } -} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm_bridge_router.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm_bridge_router.rs new file mode 100644 index 000000000000..e053a2ceba01 --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/pallet_xcm_bridge_router.rs @@ -0,0 +1,63 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_xcm_bridge_hub_router` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-12-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-acd6uxux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// target/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm_bridge_hub_router +// --chain=asset-hub-rococo-dev +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/assets/asset-hub-rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_xcm_bridge_hub_router`. +pub struct WeightInfo(PhantomData); +impl pallet_xcm_bridge_router::WeightInfo for WeightInfo { + /// Storage: `ToWestendXcmRouter::Bridges` (r:1 w:1) + /// Proof: `ToWestendXcmRouter::Bridges` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + fn update_bridge_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `3530` + // Minimum execution time: 12_193_000 picoseconds. + Weight::from_parts(12_658_000, 0) + .saturating_add(Weight::from_parts(0, 3530)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index 0c6ff5e4bfdd..00a9043348b7 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -562,19 +562,12 @@ pub mod bridging { /// (`AssetId` has to be aligned with `BridgeTable`) pub XcmBridgeHubRouterFeeAssetId: AssetId = TokenLocation::get().into(); - pub BridgeTable: alloc::vec::Vec = - alloc::vec::Vec::new().into_iter() - .chain(to_westend::BridgeTable::get()) - .collect(); - pub EthereumBridgeTable: alloc::vec::Vec = alloc::vec::Vec::new().into_iter() .chain(to_ethereum::BridgeTable::get()) .collect(); } - pub type NetworkExportTable = xcm_builder::NetworkExportTable; - pub type EthereumNetworkExportTable = xcm_builder::NetworkExportTable; pub mod to_westend { diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs index 144934ecd4ab..db5c97bcc4be 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs @@ -1244,35 +1244,37 @@ mod asset_hub_rococo_tests { } #[test] - fn report_bridge_status_from_xcm_bridge_router_for_westend_works() { - asset_test_utils::test_cases_over_bridge::report_bridge_status_from_xcm_bridge_router_works::< + fn update_bridge_status_from_xcm_bridge_router_for_westend_works() { + asset_test_utils::test_cases_over_bridge::update_bridge_status_from_xcm_bridge_router_works::< Runtime, AllPalletsWithoutSystem, XcmConfig, LocationToAccountId, ToWestendXcmRouterInstance, - >( - collator_session_keys(), - bridging_to_asset_hub_westend, - || bp_asset_hub_rococo::build_congestion_message(Default::default(), true).into(), - || bp_asset_hub_rococo::build_congestion_message(Default::default(), false).into(), - ) + >(collator_session_keys(), bridging_to_asset_hub_westend, |bridge_id, is_congested| { + bp_asset_hub_rococo::build_congestion_message(bridge_id.inner(), is_congested).into() + }) } #[test] fn test_report_bridge_status_call_compatibility() { + let bridge_id = bp_xcm_bridge::BridgeId::new( + &InteriorLocation::from([GlobalConsensus(ByGenesis([0; 32]))]), + &InteriorLocation::from([GlobalConsensus(ByGenesis([1; 32]))]), + ); + // if this test fails, make sure `bp_asset_hub_rococo` has valid encoding assert_eq!( RuntimeCall::ToWestendXcmRouter( - pallet_xcm_bridge_hub_router::Call::report_bridge_status { - bridge_id: Default::default(), + pallet_xcm_bridge_router::Call::update_bridge_status { + bridge_id: bridge_id.clone(), is_congested: true, } ) .encode(), bp_asset_hub_rococo::Call::ToWestendXcmRouter( bp_asset_hub_rococo::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), + bridge_id: bridge_id.inner(), is_congested: true, } ) @@ -1282,10 +1284,10 @@ mod asset_hub_rococo_tests { #[test] fn check_sane_weight_report_bridge_status_for_westend() { - use pallet_xcm_bridge_hub_router::WeightInfo; - let actual = >::WeightInfo::report_bridge_status(); + >>::WeightInfo::update_bridge_status(); let max_weight = bp_asset_hub_rococo::XcmBridgeHubRouterTransactCallMaxWeight::get(); assert!( actual.all_lte(max_weight), diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index 65ef63a7fb35..befaaf0147c5 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -100,11 +100,12 @@ bp-asset-hub-rococo = { workspace = true } bp-asset-hub-westend = { workspace = true } bp-bridge-hub-rococo = { workspace = true } bp-bridge-hub-westend = { workspace = true } -pallet-xcm-bridge-hub-router = { workspace = true } +pallet-xcm-bridge-router = { workspace = true } snowbridge-router-primitives = { workspace = true } [dev-dependencies] asset-test-utils = { workspace = true, default-features = true } +bp-xcm-bridge = { workspace = true } parachains-runtimes-test-utils = { workspace = true, default-features = true } [build-dependencies] @@ -144,7 +145,7 @@ runtime-benchmarks = [ "pallet-uniques/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", - "pallet-xcm-bridge-hub-router/runtime-benchmarks", + "pallet-xcm-bridge-router/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", @@ -188,7 +189,7 @@ try-runtime = [ "pallet-transaction-payment/try-runtime", "pallet-uniques/try-runtime", "pallet-utility/try-runtime", - "pallet-xcm-bridge-hub-router/try-runtime", + "pallet-xcm-bridge-router/try-runtime", "pallet-xcm/try-runtime", "parachain-info/try-runtime", "polkadot-runtime-common/try-runtime", @@ -200,6 +201,7 @@ std = [ "bp-asset-hub-westend/std", "bp-bridge-hub-rococo/std", "bp-bridge-hub-westend/std", + "bp-xcm-bridge/std", "codec/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-parachain-system/std", @@ -244,7 +246,7 @@ std = [ "pallet-uniques/std", "pallet-utility/std", "pallet-xcm-benchmarks?/std", - "pallet-xcm-bridge-hub-router/std", + "pallet-xcm-bridge-router/std", "pallet-xcm/std", "parachain-info/std", "parachains-common/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index ecbe1fb0e62a..4f2a24a88ebf 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -61,7 +61,6 @@ use frame_system::{ use pallet_asset_conversion_tx_payment::SwapAssetAdapter; use pallet_nfts::{DestroyWitness, PalletFeatures}; use pallet_revive::{evm::runtime::EthExtra, AddressMapper}; -use pallet_xcm::EnsureXcm; use parachains_common::{ impls::DealWithFees, message_queue::*, AccountId, AssetIdForTrustBackedAssets, AuraId, Balance, BlockNumber, CollectionId, Hash, Header, ItemId, Nonce, Signature, AVERAGE_ON_INITIALIZE_RATIO, @@ -94,12 +93,14 @@ use assets_common::{ foreign_creators::ForeignCreators, matching::{FromNetwork, FromSiblingParachain}, }; +use pallet_xcm::EnsureXcm; use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; use xcm::{ latest::prelude::AssetId, prelude::{VersionedAsset, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm}, }; +use xcm_builder::{NetworkExportTable, SovereignPaidRemoteExporter}; #[cfg(feature = "runtime-benchmarks")] use frame_support::traits::PalletInfoAccess; @@ -107,7 +108,7 @@ use frame_support::traits::PalletInfoAccess; #[cfg(feature = "runtime-benchmarks")] use xcm::latest::prelude::{ Asset, Assets as XcmAssets, Fungible, Here, InteriorLocation, Junction, Junction::*, Location, - NetworkId, NonFungible, Parent, ParentThen, Response, XCM_VERSION, + NetworkId, NonFungible, Parent, ParentThen, Response, }; use xcm_runtime_apis::{ @@ -1029,26 +1030,40 @@ impl pallet_nfts::Config for Runtime { /// XCM router instance to BridgeHub with bridging capabilities for `Rococo` global /// consensus with dynamic fees and back-pressure. -pub type ToRococoXcmRouterInstance = pallet_xcm_bridge_hub_router::Instance1; -impl pallet_xcm_bridge_hub_router::Config for Runtime { +pub type ToRococoXcmRouterInstance = pallet_xcm_bridge_router::Instance1; +impl pallet_xcm_bridge_router::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type WeightInfo = weights::pallet_xcm_bridge_hub_router::WeightInfo; + type WeightInfo = weights::pallet_xcm_bridge_router::WeightInfo; - type UniversalLocation = xcm_config::UniversalLocation; - type SiblingBridgeHubLocation = xcm_config::bridging::SiblingBridgeHub; - type BridgedNetworkId = xcm_config::bridging::to_rococo::RococoNetwork; - type Bridges = xcm_config::bridging::NetworkExportTable; type DestinationVersion = PolkadotXcm; - type BridgeHubOrigin = frame_support::traits::EitherOfDiverse< - EnsureRoot, - EnsureXcm>, + // Let's use `SovereignPaidRemoteExporter`, which sends `ExportMessage` over HRMP to the sibling + // BridgeHub. + type MessageExporter = SovereignPaidRemoteExporter< + // `ExporterFor` wrapper handling dynamic fees for congestion. + pallet_xcm_bridge_router::impls::ViaRemoteBridgeHubExporter< + Runtime, + ToRococoXcmRouterInstance, + NetworkExportTable, + xcm_config::bridging::to_rococo::RococoNetwork, + xcm_config::bridging::SiblingBridgeHub, + >, + XcmpQueue, + xcm_config::UniversalLocation, >; - type ToBridgeHubSender = XcmpQueue; - type LocalXcmChannelManager = - cumulus_pallet_xcmp_queue::bridging::InAndOutXcmpChannelStatusProvider; + // For congestion - resolves `BridgeId` using the same algorithm as `pallet_xcm_bridge_hub` on + // the BH. + type BridgeIdResolver = pallet_xcm_bridge_router::impls::EnsureIsRemoteBridgeIdResolver< + xcm_config::UniversalLocation, + >; + // For congestion - allow only calls from BH. + type UpdateBridgeStatusOrigin = + AsEnsureOriginWithArg>>; + + // For adding message size fees type ByteFee = xcm_config::bridging::XcmBridgeHubRouterByteFee; + // For adding message size fees type FeeAsset = xcm_config::bridging::XcmBridgeHubRouterFeeAssetId; } @@ -1124,7 +1139,7 @@ construct_runtime!( PolkadotXcm: pallet_xcm = 31, CumulusXcm: cumulus_pallet_xcm = 32, // Bridge utilities. - ToRococoXcmRouter: pallet_xcm_bridge_hub_router:: = 34, + ToRococoXcmRouter: pallet_xcm_bridge_router:: = 34, MessageQueue: pallet_message_queue = 35, // Handy utilities. @@ -1441,7 +1456,7 @@ mod benches { [pallet_collator_selection, CollatorSelection] [cumulus_pallet_parachain_system, ParachainSystem] [cumulus_pallet_xcmp_queue, XcmpQueue] - [pallet_xcm_bridge_hub_router, ToRococo] + [pallet_xcm_bridge_router, ToRococo] [pallet_asset_conversion_ops, AssetConversionMigration] [pallet_revive, Revive] // XCM @@ -1826,7 +1841,7 @@ impl_runtime_apis! { use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use cumulus_pallet_session_benchmarking::Pallet as SessionBench; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; - use pallet_xcm_bridge_hub_router::benchmarking::Pallet as XcmBridgeHubRouterBench; + use pallet_xcm_bridge_router::benchmarking::Pallet as XcmBridgeHubRouterBench; // This is defined once again in dispatch_benchmark, because list_benchmarks! // and add_benchmarks! are macros exported by define_benchmarks! macros and those types @@ -1981,37 +1996,17 @@ impl_runtime_apis! { } } - use pallet_xcm_bridge_hub_router::benchmarking::{ + use pallet_xcm_bridge_router::benchmarking::{ Pallet as XcmBridgeHubRouterBench, Config as XcmBridgeHubRouterConfig, }; impl XcmBridgeHubRouterConfig for Runtime { - fn make_congested() { - cumulus_pallet_xcmp_queue::bridging::suspend_channel_for_benchmarks::( - xcm_config::bridging::SiblingBridgeHubParaId::get().into() - ); - } fn ensure_bridged_target_destination() -> Result { - ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests( - xcm_config::bridging::SiblingBridgeHubParaId::get().into() - ); - let bridged_asset_hub = xcm_config::bridging::to_rococo::AssetHubRococo::get(); - let _ = PolkadotXcm::force_xcm_version( - RuntimeOrigin::root(), - alloc::boxed::Box::new(bridged_asset_hub.clone()), - XCM_VERSION, - ).map_err(|e| { - log::error!( - "Failed to dispatch `force_xcm_version({:?}, {:?}, {:?})`, error: {:?}", - RuntimeOrigin::root(), - bridged_asset_hub, - XCM_VERSION, - e - ); - BenchmarkError::Stop("XcmVersion was not stored!") - })?; - Ok(bridged_asset_hub) + Ok(xcm_config::bridging::to_rococo::AssetHubRococo::get()) + } + fn update_bridge_status_origin() -> Option { + Some(pallet_xcm::Origin::Xcm(xcm_config::bridging::SiblingBridgeHub::get()).into()) } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs index d653838ad80e..8c7c139549ee 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/mod.rs @@ -40,7 +40,7 @@ pub mod pallet_transaction_payment; pub mod pallet_uniques; pub mod pallet_utility; pub mod pallet_xcm; -pub mod pallet_xcm_bridge_hub_router; +pub mod pallet_xcm_bridge_router; pub mod paritydb_weights; pub mod rocksdb_weights; pub mod xcm; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm_bridge_hub_router.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm_bridge_hub_router.rs deleted file mode 100644 index 78aa839deacd..000000000000 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm_bridge_hub_router.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Cumulus. - -// Cumulus is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Cumulus is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Cumulus. If not, see . - -//! Autogenerated weights for `pallet_xcm_bridge_hub_router` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-12-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-acd6uxux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` -//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: 1024 - -// Executed Command: -// target/production/polkadot-parachain -// benchmark -// pallet -// --steps=50 -// --repeat=20 -// --extrinsic=* -// --wasm-execution=compiled -// --heap-pages=4096 -// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json -// --pallet=pallet_xcm_bridge_hub_router -// --chain=asset-hub-westend-dev -// --header=./cumulus/file_header.txt -// --output=./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/ - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{traits::Get, weights::Weight}; -use core::marker::PhantomData; - -/// Weight functions for `pallet_xcm_bridge_hub_router`. -pub struct WeightInfo(PhantomData); -impl pallet_xcm_bridge_hub_router::WeightInfo for WeightInfo { - /// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0) - /// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: Some(4002), added: 4497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) - /// Storage: `ToRococoXcmRouter::Bridge` (r:1 w:1) - /// Proof: `ToRococoXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) - fn on_initialize_when_non_congested() -> Weight { - // Proof Size summary in bytes: - // Measured: `259` - // Estimated: `5487` - // Minimum execution time: 14_643_000 picoseconds. - Weight::from_parts(14_992_000, 0) - .saturating_add(Weight::from_parts(0, 5487)) - .saturating_add(T::DbWeight::get().reads(3)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: `XcmpQueue::InboundXcmpSuspended` (r:1 w:0) - /// Proof: `XcmpQueue::InboundXcmpSuspended` (`max_values`: Some(1), `max_size`: Some(4002), added: 4497, mode: `MaxEncodedLen`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) - fn on_initialize_when_congested() -> Weight { - // Proof Size summary in bytes: - // Measured: `144` - // Estimated: `5487` - // Minimum execution time: 5_367_000 picoseconds. - Weight::from_parts(5_604_000, 0) - .saturating_add(Weight::from_parts(0, 5487)) - .saturating_add(T::DbWeight::get().reads(2)) - } - /// Storage: `ToRococoXcmRouter::Bridge` (r:1 w:1) - /// Proof: `ToRococoXcmRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: 512, mode: `MaxEncodedLen`) - fn report_bridge_status() -> Weight { - // Proof Size summary in bytes: - // Measured: `150` - // Estimated: `1502` - // Minimum execution time: 12_562_000 picoseconds. - Weight::from_parts(12_991_000, 0) - .saturating_add(Weight::from_parts(0, 1502)) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } -} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm_bridge_router.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm_bridge_router.rs new file mode 100644 index 000000000000..ff7c8261ef81 --- /dev/null +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/pallet_xcm_bridge_router.rs @@ -0,0 +1,63 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_xcm_bridge_hub_router` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-12-07, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-acd6uxux-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("asset-hub-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// target/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm_bridge_hub_router +// --chain=asset-hub-westend-dev +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/assets/asset-hub-westend/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_xcm_bridge_hub_router`. +pub struct WeightInfo(PhantomData); +impl pallet_xcm_bridge_router::WeightInfo for WeightInfo { + /// Storage: `ToRococoXcmRouter::Bridges` (r:1 w:1) + /// Proof: `ToRococoXcmRouter::Bridges` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + fn update_bridge_status() -> Weight { + // Proof Size summary in bytes: + // Measured: `109` + // Estimated: `3530` + // Minimum execution time: 10_800_000 picoseconds. + Weight::from_parts(11_171_000, 0) + .saturating_add(Weight::from_parts(0, 3530)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index 1ea2ce5136ab..e4baa067c397 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -580,15 +580,8 @@ pub mod bridging { /// Router expects payment with this `AssetId`. /// (`AssetId` has to be aligned with `BridgeTable`) pub XcmBridgeHubRouterFeeAssetId: AssetId = WestendLocation::get().into(); - - pub BridgeTable: alloc::vec::Vec = - alloc::vec::Vec::new().into_iter() - .chain(to_rococo::BridgeTable::get()) - .collect(); } - pub type NetworkExportTable = xcm_builder::NetworkExportTable; - pub mod to_rococo { use super::*; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs index 24b6d83ffae4..8d59346d94bd 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs @@ -1251,33 +1251,35 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_rococo_fees_paid_by_suffic } #[test] -fn report_bridge_status_from_xcm_bridge_router_for_rococo_works() { - asset_test_utils::test_cases_over_bridge::report_bridge_status_from_xcm_bridge_router_works::< +fn update_bridge_status_from_xcm_bridge_router_for_rococo_works() { + asset_test_utils::test_cases_over_bridge::update_bridge_status_from_xcm_bridge_router_works::< Runtime, AllPalletsWithoutSystem, XcmConfig, LocationToAccountId, ToRococoXcmRouterInstance, - >( - collator_session_keys(), - bridging_to_asset_hub_rococo, - || bp_asset_hub_westend::build_congestion_message(Default::default(), true).into(), - || bp_asset_hub_westend::build_congestion_message(Default::default(), false).into(), - ) + >(collator_session_keys(), bridging_to_asset_hub_rococo, |bridge_id, is_congested| { + bp_asset_hub_westend::build_congestion_message(bridge_id.inner(), is_congested).into() + }) } #[test] fn test_report_bridge_status_call_compatibility() { + let bridge_id = bp_xcm_bridge::BridgeId::new( + &InteriorLocation::from([GlobalConsensus(ByGenesis([0; 32]))]), + &InteriorLocation::from([GlobalConsensus(ByGenesis([1; 32]))]), + ); + // if this test fails, make sure `bp_asset_hub_rococo` has valid encoding assert_eq!( - RuntimeCall::ToRococoXcmRouter(pallet_xcm_bridge_hub_router::Call::report_bridge_status { - bridge_id: Default::default(), + RuntimeCall::ToRococoXcmRouter(pallet_xcm_bridge_router::Call::update_bridge_status { + bridge_id: bridge_id.clone(), is_congested: true, }) .encode(), bp_asset_hub_westend::Call::ToRococoXcmRouter( bp_asset_hub_westend::XcmBridgeHubRouterCall::report_bridge_status { - bridge_id: Default::default(), + bridge_id: bridge_id.inner(), is_congested: true, } ) @@ -1287,10 +1289,10 @@ fn test_report_bridge_status_call_compatibility() { #[test] fn check_sane_weight_report_bridge_status() { - use pallet_xcm_bridge_hub_router::WeightInfo; - let actual = >::WeightInfo::report_bridge_status(); + >>::WeightInfo::update_bridge_status(); let max_weight = bp_asset_hub_westend::XcmBridgeHubRouterTransactCallMaxWeight::get(); assert!( actual.all_lte(max_weight), diff --git a/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml b/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml index cad8d10a7da3..b98352b03164 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/test-utils/Cargo.toml @@ -42,7 +42,7 @@ xcm-executor = { workspace = true } xcm-runtime-apis = { workspace = true } # Bridges -pallet-xcm-bridge-hub-router = { workspace = true } +pallet-xcm-bridge-router = { workspace = true } [dev-dependencies] hex-literal = { workspace = true, default-features = true } @@ -65,7 +65,7 @@ std = [ "pallet-collator-selection/std", "pallet-session/std", "pallet-timestamp/std", - "pallet-xcm-bridge-hub-router/std", + "pallet-xcm-bridge-router/std", "pallet-xcm/std", "parachain-info/std", "parachains-common/std", diff --git a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs index 9b05f2d46dfb..3d914ba9c4ef 100644 --- a/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs +++ b/cumulus/parachains/runtimes/assets/test-utils/src/test_cases_over_bridge.rs @@ -489,7 +489,7 @@ pub fn receive_reserve_asset_deposited_from_different_consensus_works< }) } -pub fn report_bridge_status_from_xcm_bridge_router_works< +pub fn update_bridge_status_from_xcm_bridge_router_works< Runtime, AllPalletsWithoutSystem, XcmConfig, @@ -498,8 +498,10 @@ pub fn report_bridge_status_from_xcm_bridge_router_works< >( collator_session_keys: CollatorSessionKeys, prepare_configuration: fn() -> TestBridgingConfig, - congested_message: fn() -> Xcm, - uncongested_message: fn() -> Xcm, + congestion_message: fn( + pallet_xcm_bridge_router::BridgeIdOf, + bool, + ) -> Xcm, ) where Runtime: frame_system::Config + pallet_balances::Config @@ -509,7 +511,7 @@ pub fn report_bridge_status_from_xcm_bridge_router_works< + pallet_collator_selection::Config + cumulus_pallet_parachain_system::Config + cumulus_pallet_xcmp_queue::Config - + pallet_xcm_bridge_hub_router::Config + + pallet_xcm_bridge_router::Config + pallet_timestamp::Config, AllPalletsWithoutSystem: OnInitialize> + OnFinalize>, @@ -532,12 +534,29 @@ pub fn report_bridge_status_from_xcm_bridge_router_works< .with_tracing() .build() .execute_with(|| { - let report_bridge_status = |is_congested: bool| { + let update_bridge_status = |is_congested: bool| { // prepare bridge config - let TestBridgingConfig { local_bridge_hub_location, .. } = prepare_configuration(); + let TestBridgingConfig { + local_bridge_hub_location, bridged_target_location, .. + } = prepare_configuration(); + + use pallet_xcm_bridge_router::ResolveBridgeId; + let bridge_id = <>::BridgeIdResolver>::resolve_for_dest( + &bridged_target_location + ) + .expect("resolved BridgeId"); + + // check before + let bridge_state = pallet_xcm_bridge_router::Bridges::< + Runtime, + XcmBridgeHubRouterInstance, + >::get(&bridge_id); + let is_congested_before = bridge_state.map(|bs| bs.is_congested).unwrap_or(false); // Call received XCM execution - let xcm = if is_congested { congested_message() } else { uncongested_message() }; + let xcm = congestion_message(bridge_id.clone(), is_congested); let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); // execute xcm as XcmpQueue would do @@ -551,10 +570,110 @@ pub fn report_bridge_status_from_xcm_bridge_router_works< Weight::zero(), ); assert_ok!(outcome.ensure_complete()); - assert_eq!(is_congested, pallet_xcm_bridge_hub_router::Pallet::::bridge().is_congested); + + // check after + let bridge_state = pallet_xcm_bridge_router::Bridges::< + Runtime, + XcmBridgeHubRouterInstance, + >::get(&bridge_id); + let is_congested_after = bridge_state.map(|bs| bs.is_congested).unwrap_or(false); + assert_eq!(is_congested_after, is_congested); + assert_ne!(is_congested_after, is_congested_before,); }; - report_bridge_status(true); - report_bridge_status(false); + update_bridge_status(true); + update_bridge_status(false); }) } + +// TODO: revert-for-depracated-old and deprecate for old router - FAIL-CI +// pub fn report_bridge_status_from_xcm_bridge_router_works< +// Runtime, +// AllPalletsWithoutSystem, +// XcmConfig, +// LocationToAccountId, +// XcmBridgeHubRouterInstance, +// >( +// collator_session_keys: CollatorSessionKeys, +// prepare_configuration: fn() -> TestBridgingConfig, +// congestion_message: fn( +// pallet_xcm_bridge_hub_router::BridgeIdOf, +// bool, +// ) -> Xcm, +// ) where +// Runtime: frame_system::Config +// + pallet_balances::Config +// + pallet_session::Config +// + pallet_xcm::Config +// + parachain_info::Config +// + pallet_collator_selection::Config +// + cumulus_pallet_parachain_system::Config +// + cumulus_pallet_xcmp_queue::Config +// + pallet_xcm_bridge_hub_router::Config +// + pallet_timestamp::Config, +// AllPalletsWithoutSystem: +// OnInitialize> + OnFinalize>, +// AccountIdOf: Into<[u8; 32]>, +// ValidatorIdOf: From>, +// BalanceOf: From, +// ::Balance: From + Into, +// XcmConfig: xcm_executor::Config, +// LocationToAccountId: ConvertLocation>, +// ::AccountId: +// Into<<::RuntimeOrigin as OriginTrait>::AccountId>, +// <::Lookup as StaticLookup>::Source: +// From<::AccountId>, +// ::AccountId: From, +// XcmBridgeHubRouterInstance: 'static, +// { +// ExtBuilder::::default() +// .with_collators(collator_session_keys.collators()) +// .with_session_keys(collator_session_keys.session_keys()) +// .with_tracing() +// .build() +// .execute_with(|| { +// let update_bridge_status = |is_congested: bool| { +// // prepare bridge config +// let TestBridgingConfig { +// local_bridge_hub_location, bridged_target_location, .. +// } = prepare_configuration(); +// +// use pallet_xcm_bridge_hub_router::ResolveBridgeId; +// let bridge_id = <>::BridgeIdResolver>::resolve_for_dest( +// &bridged_target_location +// ) +// .expect("resolved BridgeId"); +// +// // check before +// let bridge_state = pallet_xcm_bridge_hub_router::Bridges::< +// Runtime, +// XcmBridgeHubRouterInstance, +// >::get(&bridge_id); +// let is_congested_before = bridge_state.map(|bs| bs.is_congested).unwrap_or(false); +// +// // Call received XCM execution +// let xcm = congestion_message(bridge_id.clone(), is_congested); +// let mut hash = xcm.using_encoded(sp_io::hashing::blake2_256); +// +// // execute xcm as XcmpQueue would do +// let outcome = XcmExecutor::::prepare_and_execute( +// local_bridge_hub_location.clone(), +// xcm, +// &mut hash, +// RuntimeHelper::::xcm_max_weight( +// XcmReceivedFrom::Sibling, +// ), +// Weight::zero(), +// ); +// assert_ok!(outcome.ensure_complete()); +// +// // check after +// assert_eq!(is_congested, pallet_xcm_bridge_hub_router::Pallet::::bridge().is_congested); +// }; +// +// update_bridge_status(true); +// update_bridge_status(false); +// }) +// } \ No newline at end of file diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index b3d48adfedc5..90f5fa804253 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -97,13 +97,13 @@ bp-relayers = { workspace = true } bp-rococo = { workspace = true } bp-runtime = { workspace = true } bp-westend = { workspace = true } -bp-xcm-bridge-hub-router = { workspace = true } +bp-xcm-bridge-router = { workspace = true } bridge-runtime-common = { workspace = true } pallet-bridge-grandpa = { workspace = true } pallet-bridge-messages = { workspace = true } pallet-bridge-parachains = { workspace = true } pallet-bridge-relayers = { workspace = true } -pallet-xcm-bridge-hub = { workspace = true } +pallet-xcm-bridge = { workspace = true } # Ethereum Bridge (Snowbridge) snowbridge-beacon-primitives = { workspace = true } @@ -142,7 +142,7 @@ std = [ "bp-rococo/std", "bp-runtime/std", "bp-westend/std", - "bp-xcm-bridge-hub-router/std", + "bp-xcm-bridge-router/std", "bridge-hub-common/std", "bridge-runtime-common/std", "codec/std", @@ -180,7 +180,7 @@ std = [ "pallet-transaction-payment/std", "pallet-utility/std", "pallet-xcm-benchmarks?/std", - "pallet-xcm-bridge-hub/std", + "pallet-xcm-bridge/std", "pallet-xcm/std", "parachain-info/std", "parachains-common/std", @@ -248,7 +248,7 @@ runtime-benchmarks = [ "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", - "pallet-xcm-bridge-hub/runtime-benchmarks", + "pallet-xcm-bridge/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", @@ -292,7 +292,7 @@ try-runtime = [ "pallet-timestamp/try-runtime", "pallet-transaction-payment/try-runtime", "pallet-utility/try-runtime", - "pallet-xcm-bridge-hub/try-runtime", + "pallet-xcm-bridge/try-runtime", "pallet-xcm/try-runtime", "parachain-info/try-runtime", "polkadot-runtime-common/try-runtime", diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs index 1f58e9c2f2ba..d775e35fdd51 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs @@ -39,7 +39,7 @@ use pallet_bridge_messages::LaneIdOf; use pallet_bridge_relayers::extension::{ BridgeRelayersTransactionExtension, WithMessagesExtensionConfig, }; -use pallet_xcm_bridge_hub::XcmAsPlainPayload; +use pallet_xcm_bridge::{congestion::BlobDispatcherWithChannelStatus, XcmAsPlainPayload}; use polkadot_parachain_primitives::primitives::Sibling; use testnet_parachains_constants::rococo::currency::UNITS as ROC; use xcm::{ @@ -85,13 +85,6 @@ pub type FromRococoBulletinMessagesProof = pub type ToRococoBulletinMessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof>; -/// Dispatches received XCM messages from other bridge. -type FromRococoBulletinMessageBlobDispatcher = BridgeBlobDispatcher< - XcmRouter, - UniversalLocation, - BridgeRococoToRococoBulletinMessagesPalletInstance, ->; - /// Transaction extension that refunds relayers that are delivering messages from the Rococo /// Bulletin chain. pub type OnBridgeHubRococoRefundRococoBulletinMessages = BridgeRelayersTransactionExtension< @@ -130,9 +123,10 @@ impl pallet_bridge_messages::Config for Runt } /// Add support for the export and dispatch of XCM programs. -pub type XcmOverPolkadotBulletinInstance = pallet_xcm_bridge_hub::Instance2; -impl pallet_xcm_bridge_hub::Config for Runtime { +pub type XcmOverPolkadotBulletinInstance = pallet_xcm_bridge::Instance2; +impl pallet_xcm_bridge::Config for Runtime { type RuntimeEvent = RuntimeEvent; + type WeightInfo = weights::pallet_xcm_bridge_over_bulletin::WeightInfo; type UniversalLocation = UniversalLocation; type BridgedNetwork = RococoBulletinGlobalConsensusNetworkLocation; @@ -155,7 +149,18 @@ impl pallet_xcm_bridge_hub::Config for Runtime type AllowWithoutBridgeDeposit = Equals; type LocalXcmChannelManager = (); - type BlobDispatcher = FromRococoBulletinMessageBlobDispatcher; + // Dispatching inbound messages from the bridge. + type BlobDispatcher = BlobDispatcherWithChannelStatus< + // Dispatches received XCM messages from other bridge + BridgeBlobDispatcher< + XcmRouter, + UniversalLocation, + BridgeRococoToRococoBulletinMessagesPalletInstance, + >, + // no congestion checking + (), + >; + type CongestionLimits = (); } #[cfg(test)] @@ -231,51 +236,29 @@ mod tests { } } -#[cfg(feature = "runtime-benchmarks")] -pub(crate) fn open_bridge_for_benchmarks( - with: pallet_xcm_bridge_hub::LaneIdOf, - sibling_para_id: u32, -) -> InteriorLocation -where - R: pallet_xcm_bridge_hub::Config, - XBHI: 'static, - C: xcm_executor::traits::ConvertLocation< - bp_runtime::AccountIdOf>, - >, -{ - use pallet_xcm_bridge_hub::{Bridge, BridgeId, BridgeState}; - use sp_runtime::traits::Zero; - use xcm::{latest::ROCOCO_GENESIS_HASH, VersionedInteriorLocation}; - - // insert bridge metadata - let lane_id = with; - let sibling_parachain = Location::new(1, [Parachain(sibling_para_id)]); - let universal_source = - [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), Parachain(sibling_para_id)].into(); - let universal_destination = - [GlobalConsensus(RococoBulletinGlobalConsensusNetwork::get())].into(); - let bridge_id = BridgeId::new(&universal_source, &universal_destination); - - // insert only bridge metadata, because the benchmarks create lanes - pallet_xcm_bridge_hub::Bridges::::insert( - bridge_id, - Bridge { - bridge_origin_relative_location: alloc::boxed::Box::new( - sibling_parachain.clone().into(), - ), - bridge_origin_universal_location: alloc::boxed::Box::new( - VersionedInteriorLocation::from(universal_source.clone()), - ), - bridge_destination_universal_location: alloc::boxed::Box::new( - VersionedInteriorLocation::from(universal_destination), - ), - state: BridgeState::Opened, - bridge_owner_account: C::convert_location(&sibling_parachain).expect("valid AccountId"), - deposit: Zero::zero(), - lane_id, - }, - ); - pallet_xcm_bridge_hub::LaneToBridge::::insert(lane_id, bridge_id); +/// Contains the migration for the PeopleRococo<>RococoBulletin bridge. +pub mod migration { + use super::*; + use frame_support::traits::ConstBool; + + parameter_types! { + pub BulletinRococoLocation: InteriorLocation = [GlobalConsensus(RococoBulletinGlobalConsensusNetwork::get())].into(); + pub RococoPeopleToRococoBulletinMessagesLane: bp_messages::HashedLaneId = pallet_xcm_bridge::Pallet::< Runtime, XcmOverPolkadotBulletinInstance >::bridge_locations( + PeopleRococoLocation::get(), + BulletinRococoLocation::get() + ) + .unwrap() + .calculate_lane_id(xcm::latest::VERSION).expect("Valid locations"); + } - universal_source + /// Ensure that the existing lanes for the People<>Bulletin bridge are correctly configured. + pub type StaticToDynamicLanes = pallet_xcm_bridge::migration::OpenBridgeForLane< + Runtime, + XcmOverPolkadotBulletinInstance, + RococoPeopleToRococoBulletinMessagesLane, + ConstBool, + PeopleRococoLocation, + BulletinRococoLocation, + (), + >; } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs index d394af73e747..2bda4d4f7a11 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs @@ -24,14 +24,15 @@ use crate::{ weights, xcm_config::UniversalLocation, AccountId, Balance, Balances, BridgeWestendMessages, PolkadotXcm, Runtime, RuntimeEvent, - RuntimeHoldReason, XcmOverBridgeHubWestend, XcmRouter, XcmpQueue, + RuntimeHoldReason, XcmOverBridgeHubWestend, XcmRouter, }; +use alloc::{vec, vec::Vec}; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, target_chain::FromBridgedChainMessagesProof, LegacyLaneId, }; use bridge_hub_common::xcm_version::XcmVersionOfDestAndRemoteBridge; -use pallet_xcm_bridge_hub::{BridgeId, XcmAsPlainPayload}; +use pallet_xcm_bridge::XcmAsPlainPayload; use frame_support::{parameter_types, traits::PalletInfoAccess}; use frame_system::{EnsureNever, EnsureRoot}; @@ -39,8 +40,12 @@ use pallet_bridge_messages::LaneIdOf; use pallet_bridge_relayers::extension::{ BridgeRelayersTransactionExtension, WithMessagesExtensionConfig, }; +use pallet_xcm_bridge::congestion::{ + BlobDispatcherWithChannelStatus, UpdateBridgeStatusXcmChannelManager, +}; use parachains_common::xcm_config::{AllSiblingSystemParachains, RelayOrOtherSystemParachains}; use polkadot_parachain_primitives::primitives::Sibling; +use sp_runtime::traits::Convert; use testnet_parachains_constants::rococo::currency::UNITS as ROC; use xcm::{ latest::{prelude::*, WESTEND_GENESIS_HASH}, @@ -80,10 +85,6 @@ pub type FromWestendBridgeHubMessagesProof = pub type ToWestendBridgeHubMessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof>; -/// Dispatches received XCM messages from other bridge -type FromWestendMessageBlobDispatcher = - BridgeBlobDispatcher; - /// Transaction extension that refunds relayers that are delivering messages from the Westend /// parachain. pub type OnBridgeHubRococoRefundBridgeHubWestendMessages = BridgeRelayersTransactionExtension< @@ -129,11 +130,30 @@ impl pallet_bridge_messages::Config for Ru type OnMessagesDelivered = XcmOverBridgeHubWestend; } +/// Converts encoded call to the unpaid XCM `Transact`. +pub struct UpdateBridgeStatusXcmProvider; +impl Convert, Xcm<()>> for UpdateBridgeStatusXcmProvider { + fn convert(encoded_call: Vec) -> Xcm<()> { + Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + Transact { + origin_kind: OriginKind::Xcm, + fallback_max_weight: Some( + bp_asset_hub_rococo::XcmBridgeHubRouterTransactCallMaxWeight::get(), + ), + call: encoded_call.into(), + }, + ExpectTransactStatus(MaybeErrorCode::Success), + ]) + } +} + /// Add support for the export and dispatch of XCM programs withing /// `WithBridgeHubWestendMessagesInstance`. -pub type XcmOverBridgeHubWestendInstance = pallet_xcm_bridge_hub::Instance1; -impl pallet_xcm_bridge_hub::Config for Runtime { +pub type XcmOverBridgeHubWestendInstance = pallet_xcm_bridge::Instance1; +impl pallet_xcm_bridge::Config for Runtime { type RuntimeEvent = RuntimeEvent; + type WeightInfo = weights::pallet_xcm_bridge_over_westend::WeightInfo; type UniversalLocation = UniversalLocation; type BridgedNetwork = WestendGlobalConsensusNetworkLocation; @@ -157,93 +177,29 @@ impl pallet_xcm_bridge_hub::Config for Runtime type AllowWithoutBridgeDeposit = RelayOrOtherSystemParachains; - type LocalXcmChannelManager = CongestionManager; - type BlobDispatcher = FromWestendMessageBlobDispatcher; -} - -/// Implementation of `bp_xcm_bridge_hub::LocalXcmChannelManager` for congestion management. -pub struct CongestionManager; -impl pallet_xcm_bridge_hub::LocalXcmChannelManager for CongestionManager { - type Error = SendError; - - fn is_congested(with: &Location) -> bool { - // This is used to check the inbound bridge queue/messages to determine if they can be - // dispatched and sent to the sibling parachain. Therefore, checking outbound `XcmpQueue` - // is sufficient here. - use bp_xcm_bridge_hub_router::XcmChannelStatusProvider; - cumulus_pallet_xcmp_queue::bridging::OutXcmpChannelStatusProvider::::is_congested( - with, - ) - } - - fn suspend_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error> { - // This bridge is intended for AH<>AH communication with a hard-coded/static lane, - // so `local_origin` is expected to represent only the local AH. - send_xcm::( - local_origin.clone(), - bp_asset_hub_rococo::build_congestion_message(bridge.inner(), true).into(), - ) - .map(|_| ()) - } - - fn resume_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error> { - // This bridge is intended for AH<>AH communication with a hard-coded/static lane, - // so `local_origin` is expected to represent only the local AH. - send_xcm::( - local_origin.clone(), - bp_asset_hub_rococo::build_congestion_message(bridge.inner(), false).into(), - ) - .map(|_| ()) - } -} - -#[cfg(feature = "runtime-benchmarks")] -pub(crate) fn open_bridge_for_benchmarks( - with: pallet_xcm_bridge_hub::LaneIdOf, - sibling_para_id: u32, -) -> InteriorLocation -where - R: pallet_xcm_bridge_hub::Config, - XBHI: 'static, - C: xcm_executor::traits::ConvertLocation< - bp_runtime::AccountIdOf>, - >, -{ - use pallet_xcm_bridge_hub::{Bridge, BridgeId, BridgeState}; - use sp_runtime::traits::Zero; - use xcm::{latest::ROCOCO_GENESIS_HASH, VersionedInteriorLocation}; - - // insert bridge metadata - let lane_id = with; - let sibling_parachain = Location::new(1, [Parachain(sibling_para_id)]); - let universal_source = - [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), Parachain(sibling_para_id)].into(); - let universal_destination = - [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(2075)].into(); - let bridge_id = BridgeId::new(&universal_source, &universal_destination); - - // insert only bridge metadata, because the benchmarks create lanes - pallet_xcm_bridge_hub::Bridges::::insert( - bridge_id, - Bridge { - bridge_origin_relative_location: alloc::boxed::Box::new( - sibling_parachain.clone().into(), - ), - bridge_origin_universal_location: alloc::boxed::Box::new( - VersionedInteriorLocation::from(universal_source.clone()), - ), - bridge_destination_universal_location: alloc::boxed::Box::new( - VersionedInteriorLocation::from(universal_destination), - ), - state: BridgeState::Opened, - bridge_owner_account: C::convert_location(&sibling_parachain).expect("valid AccountId"), - deposit: Zero::zero(), - lane_id, - }, - ); - pallet_xcm_bridge_hub::LaneToBridge::::insert(lane_id, bridge_id); - - universal_source + // This pallet is deployed on BH, so we expect a remote router with `ExportMessage`. We handle + // congestion with XCM using `update_bridge_status` sent to the sending chain. (congestion with + // local sending chain) + type LocalXcmChannelManager = UpdateBridgeStatusXcmChannelManager< + Runtime, + XcmOverBridgeHubWestendInstance, + UpdateBridgeStatusXcmProvider, + XcmRouter, + >; + // Dispatching inbound messages from the bridge and managing congestion with the local + // receiving/destination chain + type BlobDispatcher = BlobDispatcherWithChannelStatus< + // Dispatches received XCM messages from other bridge + BridgeBlobDispatcher< + XcmRouter, + UniversalLocation, + BridgeRococoToWestendMessagesPalletInstance, + >, + // Provides the status of the XCMP queue's outbound queue, indicating whether messages can + // be dispatched to the sibling. + cumulus_pallet_xcmp_queue::bridging::OutXcmpChannelStatusProvider, + >; + type CongestionLimits = (); } #[cfg(test)] @@ -349,7 +305,7 @@ pub mod migration { } /// Ensure that the existing lanes for the AHR<>AHW bridge are correctly configured. - pub type StaticToDynamicLanes = pallet_xcm_bridge_hub::migration::OpenBridgeForLane< + pub type StaticToDynamicLanes = pallet_xcm_bridge::migration::OpenBridgeForLane< Runtime, XcmOverBridgeHubWestendInstance, AssetHubRococoToAssetHubWestendMessagesLane, @@ -358,6 +314,7 @@ pub mod migration { ConstBool, AssetHubRococoLocation, AssetHubWestendUniversalLocation, + (), >; mod v1_wrong { diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/genesis_config_presets.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/genesis_config_presets.rs index 55fd499c2f54..918467fcf7ea 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/genesis_config_presets.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/genesis_config_presets.rs @@ -33,7 +33,12 @@ fn bridge_hub_rococo_genesis( id: ParaId, bridges_pallet_owner: Option, asset_hub_para_id: ParaId, - opened_bridges: Vec<(Location, InteriorLocation, Option)>, + opened_bridges: Vec<( + Location, + InteriorLocation, + Option, + Option, + )>, ) -> serde_json::Value { build_struct_json_patch!(RuntimeGenesisConfig { balances: BalancesConfig { @@ -73,6 +78,7 @@ fn bridge_hub_rococo_genesis( Location::new(1, [Parachain(1004)]), Junctions::from([GlobalConsensus(NetworkId::PolkadotBulletin).into()]), Some(bp_messages::LegacyLaneId([0, 0, 0, 0])), + None, )], }, xcm_over_bridge_hub_westend: XcmOverBridgeHubWestendConfig { opened_bridges }, @@ -97,6 +103,7 @@ pub fn get_preset(id: &sp_genesis_builder::PresetId) -> Option bridge_hub_rococo_genesis( diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 67bc06a9321e..4462ee40d228 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -182,6 +182,10 @@ pub type Migrations = ( RocksDbWeight, >, pallet_bridge_relayers::migration::v1::MigrationToV1, + pallet_xcm_bridge::migration::v1::MigrationToV1< + Runtime, + bridge_to_westend_config::XcmOverBridgeHubWestendInstance, + >, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, cumulus_pallet_aura_ext::migration::MigrateV0ToV1, @@ -594,7 +598,7 @@ construct_runtime!( // With-Westend messaging bridge module. BridgeWestendMessages: pallet_bridge_messages:: = 51, // With-Westend bridge hub pallet. - XcmOverBridgeHubWestend: pallet_xcm_bridge_hub:: = 52, + XcmOverBridgeHubWestend: pallet_xcm_bridge:: = 52, // With-Rococo Bulletin GRANDPA bridge module. // @@ -609,7 +613,7 @@ construct_runtime!( // storage keys, used by this runtime and the relayer process. BridgePolkadotBulletinMessages: pallet_bridge_messages:: = 61, // With-Rococo Bulletin bridge hub pallet. - XcmOverPolkadotBulletin: pallet_xcm_bridge_hub:: = 62, + XcmOverPolkadotBulletin: pallet_xcm_bridge:: = 62, // Bridge relayers pallet, used by several bridges here (another instance). BridgeRelayersForPermissionlessLanes: pallet_bridge_relayers:: = 63, @@ -688,6 +692,8 @@ mod benches { [pallet_bridge_messages, RococoToRococoBulletin] [pallet_bridge_relayers, Legacy] [pallet_bridge_relayers, PermissionlessLanes] + [pallet_xcm_bridge, OverWestend] + [pallet_xcm_bridge, OverBulletin] // Ethereum Bridge [snowbridge_pallet_inbound_queue, EthereumInboundQueue] [snowbridge_pallet_outbound_queue, EthereumOutboundQueue] @@ -1069,10 +1075,12 @@ impl_runtime_apis! { // Change weight file names. type WestendFinality = BridgeWestendGrandpa; type WithinWestend = pallet_bridge_parachains::benchmarking::Pallet::; - type RococoToWestend = pallet_bridge_messages::benchmarking::Pallet ::; - type RococoToRococoBulletin = pallet_bridge_messages::benchmarking::Pallet ::; + type RococoToWestend = pallet_bridge_messages::benchmarking::Pallet::; + type RococoToRococoBulletin = pallet_bridge_messages::benchmarking::Pallet::; type Legacy = BridgeRelayersBench::; type PermissionlessLanes = BridgeRelayersBench::; + type OverWestend = pallet_xcm_bridge::benchmarking::Pallet::; + type OverBulletin = pallet_xcm_bridge::benchmarking::Pallet::; let mut list = Vec::::new(); list_benchmarks!(list, extra); @@ -1268,30 +1276,16 @@ impl_runtime_apis! { BenchmarkError::Stop("XcmVersion was not stored!") })?; - let sibling_parachain_location = Location::new(1, [Parachain(5678)]); - - // fund SA - use frame_support::traits::fungible::Mutate; - use xcm_executor::traits::ConvertLocation; - frame_support::assert_ok!( - Balances::mint_into( - &xcm_config::LocationToAccountId::convert_location(&sibling_parachain_location).expect("valid AccountId"), - bridge_to_westend_config::BridgeDeposit::get() - .saturating_add(ExistentialDeposit::get()) - .saturating_add(UNITS * 5) - ) - ); - // open bridge + let sibling_parachain_location = Location::new(1, [Parachain(5678)]); let bridge_destination_universal_location: InteriorLocation = [GlobalConsensus(NetworkId::ByGenesis(WESTEND_GENESIS_HASH)), Parachain(8765)].into(); - let locations = XcmOverBridgeHubWestend::bridge_locations( + let _ = XcmOverBridgeHubWestend::open_bridge_for_benchmarks( + bp_messages::LegacyLaneId([1, 2, 3, 4]), sibling_parachain_location.clone(), bridge_destination_universal_location.clone(), - )?; - XcmOverBridgeHubWestend::do_open_bridge( - locations, - bp_messages::LegacyLaneId([1, 2, 3, 4]), true, + None, + || ExistentialDeposit::get(), ).map_err(|e| { log::error!( "Failed to `XcmOverBridgeHubWestend::open_bridge`({:?}, {:?})`, error: {:?}", @@ -1325,6 +1319,8 @@ impl_runtime_apis! { type RococoToRococoBulletin = pallet_bridge_messages::benchmarking::Pallet ::; type Legacy = BridgeRelayersBench::; type PermissionlessLanes = BridgeRelayersBench::; + type OverWestend = pallet_xcm_bridge::benchmarking::Pallet::; + type OverBulletin = pallet_xcm_bridge::benchmarking::Pallet::; use bridge_runtime_common::messages_benchmarking::{ prepare_message_delivery_proof_from_grandpa_chain, @@ -1339,6 +1335,18 @@ impl_runtime_apis! { MessageProofParams, }; + impl pallet_xcm_bridge::benchmarking::Config for Runtime { + fn open_bridge_origin() -> Option<(RuntimeOrigin, Balance)> { + None + } + } + + impl pallet_xcm_bridge::benchmarking::Config for Runtime { + fn open_bridge_origin() -> Option<(RuntimeOrigin, Balance)> { + None + } + } + impl BridgeMessagesConfig for Runtime { fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { let bench_lane_id = >::bench_lane_id(); @@ -1360,26 +1368,34 @@ impl_runtime_apis! { use cumulus_primitives_core::XcmpMessageSource; assert!(XcmpQueue::take_outbound_messages(usize::MAX).is_empty()); ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(42.into()); - let universal_source = bridge_to_westend_config::open_bridge_for_benchmarks::< - Runtime, - bridge_to_westend_config::XcmOverBridgeHubWestendInstance, - xcm_config::LocationToAccountId, - >(params.lane, 42); + let bridge_locations = XcmOverBridgeHubWestend::open_bridge_for_benchmarks( + params.lane, + Location::new(1, [Parachain(42)]), + [GlobalConsensus(bridge_to_westend_config::WestendGlobalConsensusNetwork::get()), Parachain(2075)].into(), + // do not create lanes, because they are already created `params.lane` + false, + None, + || ExistentialDeposit::get(), + ).expect("valid bridge opened"); prepare_message_proof_from_parachain::< Runtime, bridge_common_config::BridgeGrandpaWestendInstance, bridge_to_westend_config::WithBridgeHubWestendMessagesInstance, - >(params, generate_xcm_builder_bridge_message_sample(universal_source)) + >(params, generate_xcm_builder_bridge_message_sample(bridge_locations.bridge_origin_universal_location().clone())) } fn prepare_message_delivery_proof( params: MessageDeliveryProofParams>, ) -> bridge_to_westend_config::ToWestendBridgeHubMessagesDeliveryProof { - let _ = bridge_to_westend_config::open_bridge_for_benchmarks::< - Runtime, - bridge_to_westend_config::XcmOverBridgeHubWestendInstance, - xcm_config::LocationToAccountId, - >(params.lane, 42); + let _ = XcmOverBridgeHubWestend::open_bridge_for_benchmarks( + params.lane, + Location::new(1, [Parachain(42)]), + [GlobalConsensus(bridge_to_westend_config::WestendGlobalConsensusNetwork::get()), Parachain(2075)].into(), + // do not create lanes, because they are already created `params.lane` + false, + None, + || ExistentialDeposit::get(), + ); prepare_message_delivery_proof_from_parachain::< Runtime, bridge_common_config::BridgeGrandpaWestendInstance, @@ -1405,26 +1421,34 @@ impl_runtime_apis! { use cumulus_primitives_core::XcmpMessageSource; assert!(XcmpQueue::take_outbound_messages(usize::MAX).is_empty()); ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(42.into()); - let universal_source = bridge_to_bulletin_config::open_bridge_for_benchmarks::< - Runtime, - bridge_to_bulletin_config::XcmOverPolkadotBulletinInstance, - xcm_config::LocationToAccountId, - >(params.lane, 42); + let bridge_locations = XcmOverPolkadotBulletin::open_bridge_for_benchmarks( + params.lane, + Location::new(1, [Parachain(42)]), + [GlobalConsensus(bridge_to_bulletin_config::RococoBulletinGlobalConsensusNetwork::get())].into(), + // do not create lanes, because they are already created `params.lane` + false, + None, + || ExistentialDeposit::get(), + ).expect("valid bridge opened"); prepare_message_proof_from_grandpa_chain::< Runtime, bridge_common_config::BridgeGrandpaRococoBulletinInstance, bridge_to_bulletin_config::WithRococoBulletinMessagesInstance, - >(params, generate_xcm_builder_bridge_message_sample(universal_source)) + >(params, generate_xcm_builder_bridge_message_sample(bridge_locations.bridge_origin_universal_location().clone())) } fn prepare_message_delivery_proof( params: MessageDeliveryProofParams>, ) -> bridge_to_bulletin_config::ToRococoBulletinMessagesDeliveryProof { - let _ = bridge_to_bulletin_config::open_bridge_for_benchmarks::< - Runtime, - bridge_to_bulletin_config::XcmOverPolkadotBulletinInstance, - xcm_config::LocationToAccountId, - >(params.lane, 42); + let _ = XcmOverPolkadotBulletin::open_bridge_for_benchmarks( + params.lane, + Location::new(1, [Parachain(42)]), + [GlobalConsensus(bridge_to_bulletin_config::RococoBulletinGlobalConsensusNetwork::get())].into(), + // do not create lanes, because they are already created `params.lane` + false, + None, + || ExistentialDeposit::get(), + ).expect("valid bridge opened"); prepare_message_delivery_proof_from_grandpa_chain::< Runtime, bridge_common_config::BridgeGrandpaRococoBulletinInstance, diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs index 7a0accf2e7a4..4265d75ad2e1 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/mod.rs @@ -43,6 +43,8 @@ pub mod pallet_timestamp; pub mod pallet_transaction_payment; pub mod pallet_utility; pub mod pallet_xcm; +pub mod pallet_xcm_bridge_over_bulletin; +pub mod pallet_xcm_bridge_over_westend; pub mod paritydb_weights; pub mod rocksdb_weights; pub mod snowbridge_pallet_ethereum_client; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_rococo_bulletin.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_rococo_bulletin.rs index cde511fc749d..e99b5c529d6f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_rococo_bulletin.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_rococo_bulletin.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_bridge_messages` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-11-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-wiukf8gn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -53,20 +53,20 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49208), added: 51683, mode: `MaxEncodedLen`) /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) - /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1921), added: 4396, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn receive_single_message_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `933` - // Estimated: `52674` - // Minimum execution time: 61_893_000 picoseconds. - Weight::from_parts(63_358_000, 0) - .saturating_add(Weight::from_parts(0, 52674)) + // Measured: `963` + // Estimated: `52673` + // Minimum execution time: 63_162_000 picoseconds. + Weight::from_parts(64_800_000, 0) + .saturating_add(Weight::from_parts(0, 52673)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -75,24 +75,24 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49208), added: 51683, mode: `MaxEncodedLen`) /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) - /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1921), added: 4396, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 4076]`. /// The range of component `n` is `[1, 4076]`. fn receive_n_messages_proof(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `933` - // Estimated: `52674` - // Minimum execution time: 61_612_000 picoseconds. - Weight::from_parts(62_758_000, 0) - .saturating_add(Weight::from_parts(0, 52674)) - // Standard Error: 13_521 - .saturating_add(Weight::from_parts(14_530_846, 0).saturating_mul(n.into())) + // Measured: `963` + // Estimated: `52673` + // Minimum execution time: 62_746_000 picoseconds. + Weight::from_parts(64_378_000, 0) + .saturating_add(Weight::from_parts(0, 52673)) + // Standard Error: 18_595 + .saturating_add(Weight::from_parts(15_438_479, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -101,20 +101,20 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49208), added: 51683, mode: `MaxEncodedLen`) /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) - /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1921), added: 4396, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn receive_single_message_proof_with_outbound_lane_state() -> Weight { // Proof Size summary in bytes: - // Measured: `933` - // Estimated: `52674` - // Minimum execution time: 66_862_000 picoseconds. - Weight::from_parts(69_531_000, 0) - .saturating_add(Weight::from_parts(0, 52674)) + // Measured: `963` + // Estimated: `52673` + // Minimum execution time: 68_757_000 picoseconds. + Weight::from_parts(71_599_000, 0) + .saturating_add(Weight::from_parts(0, 52673)) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -123,24 +123,24 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49208), added: 51683, mode: `MaxEncodedLen`) /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) - /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1921), added: 4396, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 16384]`. /// The range of component `n` is `[1, 16384]`. fn receive_single_n_bytes_message_proof(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `933` - // Estimated: `52674` - // Minimum execution time: 58_971_000 picoseconds. - Weight::from_parts(62_999_984, 0) - .saturating_add(Weight::from_parts(0, 52674)) - // Standard Error: 7 - .saturating_add(Weight::from_parts(2_050, 0).saturating_mul(n.into())) + // Measured: `963` + // Estimated: `52673` + // Minimum execution time: 60_861_000 picoseconds. + Weight::from_parts(65_303_205, 0) + .saturating_add(Weight::from_parts(0, 52673)) + // Standard Error: 9 + .saturating_add(Weight::from_parts(2_156, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -149,20 +149,20 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) - /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1921), added: 4396, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundMessages` (r:0 w:1) - /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65596), added: 68071, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: - // Measured: `900` - // Estimated: `5383` - // Minimum execution time: 43_066_000 picoseconds. - Weight::from_parts(43_878_000, 0) - .saturating_add(Weight::from_parts(0, 5383)) + // Measured: `930` + // Estimated: `5386` + // Minimum execution time: 44_686_000 picoseconds. + Weight::from_parts(46_494_000, 0) + .saturating_add(Weight::from_parts(0, 5386)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -171,20 +171,20 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) - /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1921), added: 4396, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundMessages` (r:0 w:2) - /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65596), added: 68071, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: - // Measured: `900` - // Estimated: `5383` - // Minimum execution time: 44_120_000 picoseconds. - Weight::from_parts(45_914_000, 0) - .saturating_add(Weight::from_parts(0, 5383)) + // Measured: `930` + // Estimated: `5386` + // Minimum execution time: 46_167_000 picoseconds. + Weight::from_parts(47_728_000, 0) + .saturating_add(Weight::from_parts(0, 5386)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -193,20 +193,20 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) - /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1921), added: 4396, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::OutboundMessages` (r:0 w:2) - /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65596), added: 68071, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: - // Measured: `900` - // Estimated: `5383` - // Minimum execution time: 44_930_000 picoseconds. - Weight::from_parts(46_111_000, 0) - .saturating_add(Weight::from_parts(0, 5383)) + // Measured: `930` + // Estimated: `5386` + // Minimum execution time: 46_285_000 picoseconds. + Weight::from_parts(48_097_000, 0) + .saturating_add(Weight::from_parts(0, 5386)) .saturating_add(T::DbWeight::get().reads(5)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -215,11 +215,11 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (r:1 w:0) /// Proof: `BridgePolkadotBulletinGrandpa::ImportedHeaders` (`max_values`: Some(1024), `max_size`: Some(68), added: 1553, mode: `MaxEncodedLen`) /// Storage: `BridgePolkadotBulletinMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Proof: `BridgePolkadotBulletinMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49208), added: 51683, mode: `MaxEncodedLen`) /// Storage: `XcmOverPolkadotBulletin::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverPolkadotBulletin::LaneToBridge` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) /// Storage: `XcmOverPolkadotBulletin::Bridges` (r:1 w:0) - /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverPolkadotBulletin::Bridges` (`max_values`: None, `max_size`: Some(1921), added: 4396, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `XcmpQueue::DeliveryFeeFactor` (r:1 w:0) @@ -240,13 +240,13 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// The range of component `n` is `[1, 16384]`. fn receive_single_n_bytes_message_proof_with_dispatch(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1092` - // Estimated: `52674` - // Minimum execution time: 81_911_000 picoseconds. - Weight::from_parts(88_170_136, 0) - .saturating_add(Weight::from_parts(0, 52674)) - // Standard Error: 9 - .saturating_add(Weight::from_parts(7_233, 0).saturating_mul(n.into())) + // Measured: `1122` + // Estimated: `52673` + // Minimum execution time: 86_225_000 picoseconds. + Weight::from_parts(91_787_938, 0) + .saturating_add(Weight::from_parts(0, 52673)) + // Standard Error: 10 + .saturating_add(Weight::from_parts(7_223, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(4)) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_westend.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_westend.rs index b27bbf4ff6c6..2243d02adbd2 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_westend.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_bridge_messages_rococo_to_westend.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_bridge_messages` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-11-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-wiukf8gn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 // Executed Command: @@ -53,21 +53,23 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) - /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1893), added: 4368, mode: `MaxEncodedLen`) + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn receive_single_message_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `810` - // Estimated: `52674` - // Minimum execution time: 62_750_000 picoseconds. - Weight::from_parts(65_328_000, 0) - .saturating_add(Weight::from_parts(0, 52674)) - .saturating_add(T::DbWeight::get().reads(6)) + // Measured: `940` + // Estimated: `52645` + // Minimum execution time: 62_868_000 picoseconds. + Weight::from_parts(65_210_000, 0) + .saturating_add(Weight::from_parts(0, 52645)) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) @@ -75,25 +77,27 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) - /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1893), added: 4368, mode: `MaxEncodedLen`) + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 4076]`. /// The range of component `n` is `[1, 4076]`. fn receive_n_messages_proof(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `810` - // Estimated: `52674` - // Minimum execution time: 62_275_000 picoseconds. - Weight::from_parts(63_714_000, 0) - .saturating_add(Weight::from_parts(0, 52674)) - // Standard Error: 13_139 - .saturating_add(Weight::from_parts(14_630_892, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(6)) + // Measured: `940` + // Estimated: `52645` + // Minimum execution time: 62_893_000 picoseconds. + Weight::from_parts(63_992_000, 0) + .saturating_add(Weight::from_parts(0, 52645)) + // Standard Error: 13_856 + .saturating_add(Weight::from_parts(12_332_627, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) @@ -101,21 +105,23 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) - /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1893), added: 4368, mode: `MaxEncodedLen`) + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn receive_single_message_proof_with_outbound_lane_state() -> Weight { // Proof Size summary in bytes: - // Measured: `810` - // Estimated: `52674` - // Minimum execution time: 68_950_000 picoseconds. - Weight::from_parts(71_420_000, 0) - .saturating_add(Weight::from_parts(0, 52674)) - .saturating_add(T::DbWeight::get().reads(6)) + // Measured: `940` + // Estimated: `52645` + // Minimum execution time: 68_193_000 picoseconds. + Weight::from_parts(70_799_000, 0) + .saturating_add(Weight::from_parts(0, 52645)) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) @@ -123,25 +129,27 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) - /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1893), added: 4368, mode: `MaxEncodedLen`) + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 16384]`. /// The range of component `n` is `[1, 16384]`. fn receive_single_n_bytes_message_proof(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `810` - // Estimated: `52674` - // Minimum execution time: 60_477_000 picoseconds. - Weight::from_parts(64_935_758, 0) - .saturating_add(Weight::from_parts(0, 52674)) - // Standard Error: 8 - .saturating_add(Weight::from_parts(2_008, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(6)) + // Measured: `940` + // Estimated: `52645` + // Minimum execution time: 61_230_000 picoseconds. + Weight::from_parts(65_437_770, 0) + .saturating_add(Weight::from_parts(0, 52645)) + // Standard Error: 9 + .saturating_add(Weight::from_parts(2_168, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) @@ -149,24 +157,24 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) - /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1893), added: 4368, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::OutboundMessages` (r:0 w:1) - /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: - // Measured: `779` - // Estimated: `5383` - // Minimum execution time: 52_939_000 picoseconds. - Weight::from_parts(54_637_000, 0) - .saturating_add(Weight::from_parts(0, 5383)) + // Measured: `711` + // Estimated: `5358` + // Minimum execution time: 51_650_000 picoseconds. + Weight::from_parts(53_190_000, 0) + .saturating_add(Weight::from_parts(0, 5358)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -175,24 +183,24 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) - /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1893), added: 4368, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::OutboundMessages` (r:0 w:2) - /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: - // Measured: `779` - // Estimated: `5383` - // Minimum execution time: 54_645_000 picoseconds. - Weight::from_parts(57_391_000, 0) - .saturating_add(Weight::from_parts(0, 5383)) + // Measured: `711` + // Estimated: `5358` + // Minimum execution time: 51_836_000 picoseconds. + Weight::from_parts(53_960_000, 0) + .saturating_add(Weight::from_parts(0, 5358)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -201,24 +209,24 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:2 w:2) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) - /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1893), added: 4368, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::OutboundMessages` (r:0 w:2) - /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: - // Measured: `779` - // Estimated: `6144` - // Minimum execution time: 59_581_000 picoseconds. - Weight::from_parts(61_657_000, 0) - .saturating_add(Weight::from_parts(0, 6144)) + // Measured: `711` + // Estimated: `6086` + // Minimum execution time: 56_606_000 picoseconds. + Weight::from_parts(58_528_000, 0) + .saturating_add(Weight::from_parts(0, 6086)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -227,11 +235,13 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeWestendParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeWestendParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeWestendMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Proof: `BridgeWestendMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubWestend::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubWestend::LaneToBridge` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) - /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1893), added: 4368, mode: `MaxEncodedLen`) + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `XcmpQueue::DeliveryFeeFactor` (r:1 w:0) @@ -244,21 +254,19 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParachainSystem::RelevantMessagingState` (r:1 w:0) /// Proof: `ParachainSystem::RelevantMessagingState` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `XcmpQueue::OutboundXcmpMessages` (r:0 w:1) /// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: Some(105506), added: 107981, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 16384]`. /// The range of component `n` is `[1, 16384]`. fn receive_single_n_bytes_message_proof_with_dispatch(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1140` - // Estimated: `52674` - // Minimum execution time: 83_530_000 picoseconds. - Weight::from_parts(91_297_344, 0) - .saturating_add(Weight::from_parts(0, 52674)) + // Measured: `1071` + // Estimated: `52645` + // Minimum execution time: 85_179_000 picoseconds. + Weight::from_parts(91_600_892, 0) + .saturating_add(Weight::from_parts(0, 52645)) // Standard Error: 11 - .saturating_add(Weight::from_parts(7_197, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(7_210, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(4)) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm_bridge_over_bulletin.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm_bridge_over_bulletin.rs new file mode 100644 index 000000000000..92f12f977426 --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm_bridge_over_bulletin.rs @@ -0,0 +1,81 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_xcm_bridge_hub` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-11-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-wiukf8gn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// target/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm_bridge_hub +// --chain=bridge-hub-rococo-dev +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_xcm_bridge_hub`. +pub struct WeightInfo(PhantomData); +impl pallet_xcm_bridge::WeightInfo for WeightInfo { + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn open_bridge() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn close_bridge() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn update_notification_receiver() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm_bridge_over_westend.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm_bridge_over_westend.rs new file mode 100644 index 000000000000..92f12f977426 --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/pallet_xcm_bridge_over_westend.rs @@ -0,0 +1,81 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_xcm_bridge_hub` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-11-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-wiukf8gn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-rococo-dev")`, DB CACHE: 1024 + +// Executed Command: +// target/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm_bridge_hub +// --chain=bridge-hub-rococo-dev +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_xcm_bridge_hub`. +pub struct WeightInfo(PhantomData); +impl pallet_xcm_bridge::WeightInfo for WeightInfo { + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn open_bridge() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn close_bridge() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn update_notification_receiver() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index daf22190a42b..629d5d740a79 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-11-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-wiukf8gn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-rococo-dev"), DB CACHE: 1024 // Executed Command: @@ -68,8 +68,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `171` // Estimated: `6196` - // Minimum execution time: 69_010_000 picoseconds. - Weight::from_parts(70_067_000, 6196) + // Minimum execution time: 73_985_000 picoseconds. + Weight::from_parts(75_642_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -77,15 +77,26 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_069_000 picoseconds. - Weight::from_parts(1_116_000, 0) + // Minimum execution time: 946_000 picoseconds. + Weight::from_parts(1_025_000, 0) } + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) pub fn pay_fees() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 3_881_000 picoseconds. + Weight::from_parts(4_137_000, 3593) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + pub fn asset_claimer() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_011_000 picoseconds. - Weight::from_parts(2_095_000, 0) + // Minimum execution time: 960_000 picoseconds. + Weight::from_parts(1_023_000, 0) } // Storage: `PolkadotXcm::Queries` (r:1 w:0) // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -93,58 +104,58 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `3497` - // Minimum execution time: 7_630_000 picoseconds. - Weight::from_parts(7_992_000, 3497) + // Minimum execution time: 7_926_000 picoseconds. + Weight::from_parts(8_167_000, 3497) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_909_000 picoseconds. - Weight::from_parts(8_100_000, 0) + // Minimum execution time: 7_878_000 picoseconds. + Weight::from_parts(8_181_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_749_000 picoseconds. - Weight::from_parts(1_841_000, 0) + // Minimum execution time: 1_580_000 picoseconds. + Weight::from_parts(1_738_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_109_000 picoseconds. - Weight::from_parts(1_156_000, 0) + // Minimum execution time: 967_000 picoseconds. + Weight::from_parts(1_028_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_073_000 picoseconds. - Weight::from_parts(1_143_000, 0) + // Minimum execution time: 990_000 picoseconds. + Weight::from_parts(1_043_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_050_000 picoseconds. - Weight::from_parts(1_084_000, 0) + // Minimum execution time: 924_000 picoseconds. + Weight::from_parts(1_001_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_060_000 picoseconds. - Weight::from_parts(1_114_000, 0) + // Minimum execution time: 971_000 picoseconds. + Weight::from_parts(1_016_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_065_000 picoseconds. - Weight::from_parts(1_112_000, 0) + // Minimum execution time: 953_000 picoseconds. + Weight::from_parts(1_023_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -166,8 +177,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `171` // Estimated: `6196` - // Minimum execution time: 65_538_000 picoseconds. - Weight::from_parts(66_943_000, 6196) + // Minimum execution time: 69_178_000 picoseconds. + Weight::from_parts(71_090_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -177,8 +188,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 10_898_000 picoseconds. - Weight::from_parts(11_262_000, 3555) + // Minimum execution time: 11_211_000 picoseconds. + Weight::from_parts(11_590_000, 3555) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -186,8 +197,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_026_000 picoseconds. - Weight::from_parts(1_104_000, 0) + // Minimum execution time: 925_000 picoseconds. + Weight::from_parts(995_000, 0) } // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -207,8 +218,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 25_133_000 picoseconds. - Weight::from_parts(25_526_000, 3503) + // Minimum execution time: 25_942_000 picoseconds. + Weight::from_parts(26_756_000, 3503) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -218,44 +229,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_946_000 picoseconds. - Weight::from_parts(3_074_000, 0) + // Minimum execution time: 3_019_000 picoseconds. + Weight::from_parts(3_245_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_428_000 picoseconds. - Weight::from_parts(1_490_000, 0) + // Minimum execution time: 1_359_000 picoseconds. + Weight::from_parts(1_442_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_158_000 picoseconds. - Weight::from_parts(1_222_000, 0) + // Minimum execution time: 1_053_000 picoseconds. + Weight::from_parts(1_169_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_056_000 picoseconds. - Weight::from_parts(1_117_000, 0) + // Minimum execution time: 953_000 picoseconds. + Weight::from_parts(1_039_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_045_000 picoseconds. - Weight::from_parts(1_084_000, 0) + // Minimum execution time: 957_000 picoseconds. + Weight::from_parts(1_005_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_224_000 picoseconds. - Weight::from_parts(1_268_000, 0) + // Minimum execution time: 1_151_000 picoseconds. + Weight::from_parts(1_198_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -277,8 +288,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `171` // Estimated: `6196` - // Minimum execution time: 70_789_000 picoseconds. - Weight::from_parts(72_321_000, 6196) + // Minimum execution time: 75_598_000 picoseconds. + Weight::from_parts(78_209_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -286,8 +297,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_521_000 picoseconds. - Weight::from_parts(4_649_000, 0) + // Minimum execution time: 4_954_000 picoseconds. + Weight::from_parts(5_198_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -309,8 +320,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `171` // Estimated: `6196` - // Minimum execution time: 66_129_000 picoseconds. - Weight::from_parts(68_089_000, 6196) + // Minimum execution time: 69_330_000 picoseconds. + Weight::from_parts(71_245_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -318,44 +329,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_094_000 picoseconds. - Weight::from_parts(1_157_000, 0) + // Minimum execution time: 993_000 picoseconds. + Weight::from_parts(1_078_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_059_000 picoseconds. - Weight::from_parts(1_109_000, 0) + // Minimum execution time: 942_000 picoseconds. + Weight::from_parts(1_017_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_053_000 picoseconds. - Weight::from_parts(1_080_000, 0) + // Minimum execution time: 928_000 picoseconds. + Weight::from_parts(1_005_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) + // Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1893), added: 4368, mode: `MaxEncodedLen`) // Storage: `PolkadotXcm::SupportedVersion` (r:2 w:0) // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) - // Storage: `XcmOverBridgeHubWestend::Bridges` (r:1 w:0) - // Proof: `XcmOverBridgeHubWestend::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) // Storage: `BridgeWestendMessages::PalletOperatingMode` (r:1 w:0) // Proof: `BridgeWestendMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) // Storage: `BridgeWestendMessages::OutboundLanes` (r:1 w:1) - // Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) + // Proof: `BridgeWestendMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) // Storage: `BridgeWestendMessages::OutboundMessages` (r:0 w:1) - // Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) + // Proof: `BridgeWestendMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) /// The range of component `x` is `[1, 1000]`. pub fn export_message(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `190` - // Estimated: `6130` - // Minimum execution time: 42_081_000 picoseconds. - Weight::from_parts(42_977_658, 6130) - // Standard Error: 77 - .saturating_add(Weight::from_parts(44_912, 0).saturating_mul(x.into())) + // Measured: `632` + // Estimated: `6572` + // Minimum execution time: 58_042_000 picoseconds. + Weight::from_parts(59_689_657, 6572) + // Standard Error: 212 + .saturating_add(Weight::from_parts(49_274, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -363,8 +374,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_041_000 picoseconds. - Weight::from_parts(1_084_000, 0) + // Minimum execution time: 947_000 picoseconds. + Weight::from_parts(1_018_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: @@ -373,13 +384,6 @@ impl WeightInfo { // Minimum execution time: 1_085_000 picoseconds. Weight::from_parts(1_161_000, 0) } - pub fn asset_claimer() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 707_000 picoseconds. - Weight::from_parts(749_000, 0) - } pub fn execute_with_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs index b0f4366e29cf..ad5d2bd43148 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/tests.rs @@ -328,7 +328,7 @@ mod bridge_hub_westend_tests { bridge_hub_test_utils::open_bridge_with_storage::< Runtime, XcmOverBridgeHubWestendInstance - >(locations, LegacyLaneId([0, 0, 0, 1])) + >(locations, LegacyLaneId([0, 0, 0, 1]), None) } ).1 }, @@ -393,7 +393,7 @@ mod bridge_hub_westend_tests { bridge_hub_test_utils::open_bridge_with_storage::< Runtime, XcmOverBridgeHubWestendInstance, - >(locations, LegacyLaneId([0, 0, 0, 1])) + >(locations, LegacyLaneId([0, 0, 0, 1]), None) }, ) .1 @@ -428,7 +428,7 @@ mod bridge_hub_westend_tests { bridge_hub_test_utils::open_bridge_with_storage::< Runtime, XcmOverBridgeHubWestendInstance, - >(locations, LegacyLaneId([0, 0, 0, 1])) + >(locations, LegacyLaneId([0, 0, 0, 1]), None) }, ) .1 @@ -598,7 +598,7 @@ mod bridge_hub_bulletin_tests { bridge_hub_test_utils::open_bridge_with_storage::< Runtime, XcmOverPolkadotBulletinInstance - >(locations, LegacyLaneId([0, 0, 0, 0])) + >(locations, LegacyLaneId([0, 0, 0, 0]), None) } ).1 }, @@ -662,7 +662,7 @@ mod bridge_hub_bulletin_tests { bridge_hub_test_utils::open_bridge_with_storage::< Runtime, XcmOverPolkadotBulletinInstance, - >(locations, LegacyLaneId([0, 0, 0, 0])) + >(locations, LegacyLaneId([0, 0, 0, 0]), None) }, ) .1 @@ -696,7 +696,7 @@ mod bridge_hub_bulletin_tests { bridge_hub_test_utils::open_bridge_with_storage::< Runtime, XcmOverPolkadotBulletinInstance, - >(locations, LegacyLaneId([0, 0, 0, 0])) + >(locations, LegacyLaneId([0, 0, 0, 0]), None) }, ) .1 diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml index 444023eac722..85983f6a0857 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml @@ -97,14 +97,14 @@ bp-relayers = { workspace = true } bp-rococo = { workspace = true } bp-runtime = { workspace = true } bp-westend = { workspace = true } -bp-xcm-bridge-hub-router = { workspace = true } +bp-xcm-bridge-router = { workspace = true } bridge-hub-common = { workspace = true } bridge-runtime-common = { workspace = true } pallet-bridge-grandpa = { workspace = true } pallet-bridge-messages = { workspace = true } pallet-bridge-parachains = { workspace = true } pallet-bridge-relayers = { workspace = true } -pallet-xcm-bridge-hub = { workspace = true } +pallet-xcm-bridge = { workspace = true } # Ethereum Bridge (Snowbridge) snowbridge-beacon-primitives = { workspace = true } @@ -141,7 +141,7 @@ std = [ "bp-rococo/std", "bp-runtime/std", "bp-westend/std", - "bp-xcm-bridge-hub-router/std", + "bp-xcm-bridge-router/std", "bridge-hub-common/std", "bridge-runtime-common/std", "codec/std", @@ -179,7 +179,7 @@ std = [ "pallet-transaction-payment/std", "pallet-utility/std", "pallet-xcm-benchmarks?/std", - "pallet-xcm-bridge-hub/std", + "pallet-xcm-bridge/std", "pallet-xcm/std", "parachain-info/std", "parachains-common/std", @@ -247,7 +247,7 @@ runtime-benchmarks = [ "pallet-transaction-payment/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", - "pallet-xcm-bridge-hub/runtime-benchmarks", + "pallet-xcm-bridge/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", @@ -291,7 +291,7 @@ try-runtime = [ "pallet-timestamp/try-runtime", "pallet-transaction-payment/try-runtime", "pallet-utility/try-runtime", - "pallet-xcm-bridge-hub/try-runtime", + "pallet-xcm-bridge/try-runtime", "pallet-xcm/try-runtime", "parachain-info/try-runtime", "polkadot-runtime-common/try-runtime", diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs index a5fb33cf504d..6eb3875c78df 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs @@ -21,15 +21,16 @@ use crate::{ weights, xcm_config::UniversalLocation, AccountId, Balance, Balances, BridgeRococoMessages, PolkadotXcm, Runtime, RuntimeEvent, - RuntimeHoldReason, XcmOverBridgeHubRococo, XcmRouter, XcmpQueue, + RuntimeHoldReason, XcmOverBridgeHubRococo, XcmRouter, }; +use alloc::{vec, vec::Vec}; use bp_messages::{ source_chain::FromBridgedChainMessagesDeliveryProof, target_chain::FromBridgedChainMessagesProof, LegacyLaneId, }; use bp_parachains::SingleParaStoredHeaderDataBuilder; use bridge_hub_common::xcm_version::XcmVersionOfDestAndRemoteBridge; -use pallet_xcm_bridge_hub::{BridgeId, XcmAsPlainPayload}; +use pallet_xcm_bridge::XcmAsPlainPayload; use frame_support::{ parameter_types, @@ -40,8 +41,12 @@ use pallet_bridge_messages::LaneIdOf; use pallet_bridge_relayers::extension::{ BridgeRelayersTransactionExtension, WithMessagesExtensionConfig, }; +use pallet_xcm_bridge::congestion::{ + BlobDispatcherWithChannelStatus, UpdateBridgeStatusXcmChannelManager, +}; use parachains_common::xcm_config::{AllSiblingSystemParachains, RelayOrOtherSystemParachains}; use polkadot_parachain_primitives::primitives::Sibling; +use sp_runtime::traits::Convert; use testnet_parachains_constants::westend::currency::UNITS as WND; use xcm::{ latest::{prelude::*, ROCOCO_GENESIS_HASH}, @@ -87,10 +92,6 @@ pub type FromRococoBridgeHubMessagesProof = pub type ToRococoBridgeHubMessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof>; -/// Dispatches received XCM messages from other bridge -type FromRococoMessageBlobDispatcher = - BridgeBlobDispatcher; - /// Transaction extension that refunds relayers that are delivering messages from the Rococo /// parachain. pub type OnBridgeHubWestendRefundBridgeHubRococoMessages = BridgeRelayersTransactionExtension< @@ -160,10 +161,29 @@ impl pallet_bridge_messages::Config for Run type OnMessagesDelivered = XcmOverBridgeHubRococo; } +/// Converts encoded call to the unpaid XCM `Transact`. +pub struct UpdateBridgeStatusXcmProvider; +impl Convert, Xcm<()>> for UpdateBridgeStatusXcmProvider { + fn convert(encoded_call: Vec) -> Xcm<()> { + Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + Transact { + origin_kind: OriginKind::Xcm, + fallback_max_weight: Some( + bp_asset_hub_westend::XcmBridgeHubRouterTransactCallMaxWeight::get(), + ), + call: encoded_call.into(), + }, + ExpectTransactStatus(MaybeErrorCode::Success), + ]) + } +} + /// Add support for the export and dispatch of XCM programs. -pub type XcmOverBridgeHubRococoInstance = pallet_xcm_bridge_hub::Instance1; -impl pallet_xcm_bridge_hub::Config for Runtime { +pub type XcmOverBridgeHubRococoInstance = pallet_xcm_bridge::Instance1; +impl pallet_xcm_bridge::Config for Runtime { type RuntimeEvent = RuntimeEvent; + type WeightInfo = weights::pallet_xcm_bridge::WeightInfo; type UniversalLocation = UniversalLocation; type BridgedNetwork = RococoGlobalConsensusNetworkLocation; @@ -186,93 +206,29 @@ impl pallet_xcm_bridge_hub::Config for Runtime { type AllowWithoutBridgeDeposit = RelayOrOtherSystemParachains; - type LocalXcmChannelManager = CongestionManager; - type BlobDispatcher = FromRococoMessageBlobDispatcher; -} - -/// Implementation of `bp_xcm_bridge_hub::LocalXcmChannelManager` for congestion management. -pub struct CongestionManager; -impl pallet_xcm_bridge_hub::LocalXcmChannelManager for CongestionManager { - type Error = SendError; - - fn is_congested(with: &Location) -> bool { - // This is used to check the inbound bridge queue/messages to determine if they can be - // dispatched and sent to the sibling parachain. Therefore, checking outbound `XcmpQueue` - // is sufficient here. - use bp_xcm_bridge_hub_router::XcmChannelStatusProvider; - cumulus_pallet_xcmp_queue::bridging::OutXcmpChannelStatusProvider::::is_congested( - with, - ) - } - - fn suspend_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error> { - // This bridge is intended for AH<>AH communication with a hard-coded/static lane, - // so `local_origin` is expected to represent only the local AH. - send_xcm::( - local_origin.clone(), - bp_asset_hub_westend::build_congestion_message(bridge.inner(), true).into(), - ) - .map(|_| ()) - } - - fn resume_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error> { - // This bridge is intended for AH<>AH communication with a hard-coded/static lane, - // so `local_origin` is expected to represent only the local AH. - send_xcm::( - local_origin.clone(), - bp_asset_hub_westend::build_congestion_message(bridge.inner(), false).into(), - ) - .map(|_| ()) - } -} - -#[cfg(feature = "runtime-benchmarks")] -pub(crate) fn open_bridge_for_benchmarks( - with: pallet_xcm_bridge_hub::LaneIdOf, - sibling_para_id: u32, -) -> InteriorLocation -where - R: pallet_xcm_bridge_hub::Config, - XBHI: 'static, - C: xcm_executor::traits::ConvertLocation< - bp_runtime::AccountIdOf>, - >, -{ - use pallet_xcm_bridge_hub::{Bridge, BridgeId, BridgeState}; - use sp_runtime::traits::Zero; - use xcm::{latest::WESTEND_GENESIS_HASH, VersionedInteriorLocation}; - - // insert bridge metadata - let lane_id = with; - let sibling_parachain = Location::new(1, [Parachain(sibling_para_id)]); - let universal_source = - [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(sibling_para_id)].into(); - let universal_destination = - [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), Parachain(2075)].into(); - let bridge_id = BridgeId::new(&universal_source, &universal_destination); - - // insert only bridge metadata, because the benchmarks create lanes - pallet_xcm_bridge_hub::Bridges::::insert( - bridge_id, - Bridge { - bridge_origin_relative_location: alloc::boxed::Box::new( - sibling_parachain.clone().into(), - ), - bridge_origin_universal_location: alloc::boxed::Box::new( - VersionedInteriorLocation::from(universal_source.clone()), - ), - bridge_destination_universal_location: alloc::boxed::Box::new( - VersionedInteriorLocation::from(universal_destination), - ), - state: BridgeState::Opened, - bridge_owner_account: C::convert_location(&sibling_parachain).expect("valid AccountId"), - deposit: Zero::zero(), - lane_id, - }, - ); - pallet_xcm_bridge_hub::LaneToBridge::::insert(lane_id, bridge_id); - - universal_source + // This pallet is deployed on BH, so we expect a remote router with `ExportMessage`. We handle + // congestion with XCM using `update_bridge_status` sent to the sending chain. (congestion with + // local sending chain) + type LocalXcmChannelManager = UpdateBridgeStatusXcmChannelManager< + Runtime, + XcmOverBridgeHubRococoInstance, + UpdateBridgeStatusXcmProvider, + XcmRouter, + >; + // Dispatching inbound messages from the bridge and managing congestion with the local + // receiving/destination chain + type BlobDispatcher = BlobDispatcherWithChannelStatus< + // Dispatches received XCM messages from other bridge + BridgeBlobDispatcher< + XcmRouter, + UniversalLocation, + BridgeWestendToRococoMessagesPalletInstance, + >, + // Provides the status of the XCMP queue's outbound queue, indicating whether messages can + // be dispatched to the sibling. + cumulus_pallet_xcmp_queue::bridging::OutXcmpChannelStatusProvider, + >; + type CongestionLimits = (); } #[cfg(test)] diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/genesis_config_presets.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/genesis_config_presets.rs index 69ba9ca9ece7..4acb196ee952 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/genesis_config_presets.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/genesis_config_presets.rs @@ -33,7 +33,12 @@ fn bridge_hub_westend_genesis( id: ParaId, bridges_pallet_owner: Option, asset_hub_para_id: ParaId, - opened_bridges: Vec<(Location, InteriorLocation, Option)>, + opened_bridges: Vec<( + Location, + InteriorLocation, + Option, + Option, + )>, ) -> serde_json::Value { build_struct_json_patch!(RuntimeGenesisConfig { balances: BalancesConfig { @@ -88,6 +93,7 @@ pub fn get_preset(id: &sp_genesis_builder::PresetId) -> Option bridge_hub_westend_genesis( diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index 3824a4e9a7cb..2e710f19f311 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -163,6 +163,10 @@ pub type Migrations = ( RocksDbWeight, >, pallet_bridge_relayers::migration::v1::MigrationToV1, + pallet_xcm_bridge::migration::v1::MigrationToV1< + Runtime, + bridge_to_rococo_config::XcmOverBridgeHubRococoInstance, + >, snowbridge_pallet_system::migration::v0::InitializeOnUpgrade< Runtime, ConstU32, @@ -566,7 +570,7 @@ construct_runtime!( BridgeRococoGrandpa: pallet_bridge_grandpa:: = 42, BridgeRococoParachains: pallet_bridge_parachains:: = 43, BridgeRococoMessages: pallet_bridge_messages:: = 44, - XcmOverBridgeHubRococo: pallet_xcm_bridge_hub:: = 45, + XcmOverBridgeHubRococo: pallet_xcm_bridge:: = 45, EthereumInboundQueue: snowbridge_pallet_inbound_queue = 80, EthereumOutboundQueue: snowbridge_pallet_outbound_queue = 81, @@ -625,6 +629,7 @@ mod benches { [pallet_bridge_grandpa, RococoFinality] [pallet_bridge_parachains, WithinRococo] [pallet_bridge_messages, WestendToRococo] + [pallet_xcm_bridge, OverRococo] // Ethereum Bridge [snowbridge_pallet_inbound_queue, EthereumInboundQueue] [snowbridge_pallet_outbound_queue, EthereumOutboundQueue] @@ -959,6 +964,7 @@ impl_runtime_apis! { type RococoFinality = BridgeRococoGrandpa; type WithinRococo = pallet_bridge_parachains::benchmarking::Pallet::; type WestendToRococo = pallet_bridge_messages::benchmarking::Pallet ::; + type OverRococo = pallet_xcm_bridge::benchmarking::Pallet::; let mut list = Vec::::new(); list_benchmarks!(list, extra); @@ -1154,30 +1160,16 @@ impl_runtime_apis! { BenchmarkError::Stop("XcmVersion was not stored!") })?; - let sibling_parachain_location = Location::new(1, [Parachain(5678)]); - - // fund SA - use frame_support::traits::fungible::Mutate; - use xcm_executor::traits::ConvertLocation; - frame_support::assert_ok!( - Balances::mint_into( - &xcm_config::LocationToAccountId::convert_location(&sibling_parachain_location).expect("valid AccountId"), - bridge_to_rococo_config::BridgeDeposit::get() - .saturating_add(ExistentialDeposit::get()) - .saturating_add(UNITS * 5) - ) - ); - // open bridge + let sibling_parachain_location = Location::new(1, [Parachain(5678)]); let bridge_destination_universal_location: InteriorLocation = [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), Parachain(8765)].into(); - let locations = XcmOverBridgeHubRococo::bridge_locations( + let _ = XcmOverBridgeHubRococo::open_bridge_for_benchmarks( + bp_messages::LegacyLaneId([1, 2, 3, 4]), sibling_parachain_location.clone(), bridge_destination_universal_location.clone(), - )?; - XcmOverBridgeHubRococo::do_open_bridge( - locations, - bp_messages::LegacyLaneId([1, 2, 3, 4]), true, + None, + || ExistentialDeposit::get(), ).map_err(|e| { log::error!( "Failed to `XcmOverBridgeHubRococo::open_bridge`({:?}, {:?})`, error: {:?}", @@ -1208,6 +1200,7 @@ impl_runtime_apis! { type RococoFinality = BridgeRococoGrandpa; type WithinRococo = pallet_bridge_parachains::benchmarking::Pallet::; type WestendToRococo = pallet_bridge_messages::benchmarking::Pallet ::; + type OverRococo = pallet_xcm_bridge::benchmarking::Pallet::; use bridge_runtime_common::messages_benchmarking::{ prepare_message_delivery_proof_from_parachain, @@ -1220,6 +1213,12 @@ impl_runtime_apis! { MessageProofParams, }; + impl pallet_xcm_bridge::benchmarking::Config for Runtime { + fn open_bridge_origin() -> Option<(RuntimeOrigin, Balance)> { + None + } + } + impl BridgeMessagesConfig for Runtime { fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool { let bench_lane_id = >::bench_lane_id(); @@ -1241,26 +1240,34 @@ impl_runtime_apis! { use cumulus_primitives_core::XcmpMessageSource; assert!(XcmpQueue::take_outbound_messages(usize::MAX).is_empty()); ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(42.into()); - let universal_source = bridge_to_rococo_config::open_bridge_for_benchmarks::< - Runtime, - bridge_to_rococo_config::XcmOverBridgeHubRococoInstance, - xcm_config::LocationToAccountId, - >(params.lane, 42); + let bridge_locations = XcmOverBridgeHubRococo::open_bridge_for_benchmarks( + params.lane, + Location::new(1, [Parachain(42)]), + [GlobalConsensus(bridge_to_rococo_config::RococoGlobalConsensusNetwork::get()), Parachain(2075)].into(), + // do not create lanes, because they are already created `params.lane` + false, + None, + || ExistentialDeposit::get(), + ).expect("valid bridge opened"); prepare_message_proof_from_parachain::< Runtime, bridge_to_rococo_config::BridgeGrandpaRococoInstance, bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance, - >(params, generate_xcm_builder_bridge_message_sample(universal_source)) + >(params, generate_xcm_builder_bridge_message_sample(bridge_locations.bridge_origin_universal_location().clone())) } fn prepare_message_delivery_proof( params: MessageDeliveryProofParams>, ) -> bridge_to_rococo_config::ToRococoBridgeHubMessagesDeliveryProof { - let _ = bridge_to_rococo_config::open_bridge_for_benchmarks::< - Runtime, - bridge_to_rococo_config::XcmOverBridgeHubRococoInstance, - xcm_config::LocationToAccountId, - >(params.lane, 42); + let _ = XcmOverBridgeHubRococo::open_bridge_for_benchmarks( + params.lane, + Location::new(1, [Parachain(42)]), + [GlobalConsensus(bridge_to_rococo_config::RococoGlobalConsensusNetwork::get()), Parachain(2075)].into(), + // do not create lanes, because they are already created `params.lane` + false, + None, + || ExistentialDeposit::get(), + ); prepare_message_delivery_proof_from_parachain::< Runtime, bridge_to_rococo_config::BridgeGrandpaRococoInstance, diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/mod.rs index 313da55831c8..e25697fdf766 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/mod.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/mod.rs @@ -42,6 +42,7 @@ pub mod pallet_timestamp; pub mod pallet_transaction_payment; pub mod pallet_utility; pub mod pallet_xcm; +pub mod pallet_xcm_bridge; pub mod paritydb_weights; pub mod rocksdb_weights; pub mod xcm; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_messages.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_messages.rs index 492226d3ec2b..b16f00706657 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_messages.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_bridge_messages.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_bridge_messages` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-15, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-11-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-696hpswk-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-wiukf8gn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024 // Executed Command: @@ -53,21 +53,23 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) - /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1893), added: 4368, mode: `MaxEncodedLen`) + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn receive_single_message_proof() -> Weight { // Proof Size summary in bytes: - // Measured: `701` - // Estimated: `52674` - // Minimum execution time: 62_015_000 picoseconds. - Weight::from_parts(63_891_000, 0) - .saturating_add(Weight::from_parts(0, 52674)) - .saturating_add(T::DbWeight::get().reads(6)) + // Measured: `765` + // Estimated: `52645` + // Minimum execution time: 61_826_000 picoseconds. + Weight::from_parts(65_518_000, 0) + .saturating_add(Weight::from_parts(0, 52645)) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) @@ -75,24 +77,26 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) - /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1893), added: 4368, mode: `MaxEncodedLen`) + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 4076]`. fn receive_n_messages_proof(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `701` - // Estimated: `52674` - // Minimum execution time: 62_034_000 picoseconds. - Weight::from_parts(63_355_000, 0) - .saturating_add(Weight::from_parts(0, 52674)) - // Standard Error: 8_231 - .saturating_add(Weight::from_parts(14_096_117, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(6)) + // Measured: `765` + // Estimated: `52645` + // Minimum execution time: 62_597_000 picoseconds. + Weight::from_parts(64_453_000, 0) + .saturating_add(Weight::from_parts(0, 52645)) + // Standard Error: 12_681 + .saturating_add(Weight::from_parts(12_812_446, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) @@ -100,21 +104,23 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) - /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1893), added: 4368, mode: `MaxEncodedLen`) + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn receive_single_message_proof_with_outbound_lane_state() -> Weight { // Proof Size summary in bytes: - // Measured: `701` - // Estimated: `52674` - // Minimum execution time: 65_063_000 picoseconds. - Weight::from_parts(67_125_000, 0) - .saturating_add(Weight::from_parts(0, 52674)) - .saturating_add(T::DbWeight::get().reads(6)) + // Measured: `765` + // Estimated: `52645` + // Minimum execution time: 69_093_000 picoseconds. + Weight::from_parts(72_845_000, 0) + .saturating_add(Weight::from_parts(0, 52645)) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) @@ -122,24 +128,26 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) - /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1893), added: 4368, mode: `MaxEncodedLen`) + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:0) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 16384]`. fn receive_single_n_bytes_message_proof(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `701` - // Estimated: `52674` - // Minimum execution time: 58_688_000 picoseconds. - Weight::from_parts(61_404_716, 0) - .saturating_add(Weight::from_parts(0, 52674)) - // Standard Error: 7 - .saturating_add(Weight::from_parts(2_249, 0).saturating_mul(n.into())) - .saturating_add(T::DbWeight::get().reads(6)) + // Measured: `765` + // Estimated: `52645` + // Minimum execution time: 60_938_000 picoseconds. + Weight::from_parts(65_548_162, 0) + .saturating_add(Weight::from_parts(0, 52645)) + // Standard Error: 21 + .saturating_add(Weight::from_parts(2_344, 0).saturating_mul(n.into())) + .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) @@ -147,24 +155,24 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) - /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1893), added: 4368, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::OutboundMessages` (r:0 w:1) - /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_single_message() -> Weight { // Proof Size summary in bytes: - // Measured: `710` - // Estimated: `5383` - // Minimum execution time: 53_123_000 picoseconds. - Weight::from_parts(54_417_000, 0) - .saturating_add(Weight::from_parts(0, 5383)) + // Measured: `642` + // Estimated: `5358` + // Minimum execution time: 51_254_000 picoseconds. + Weight::from_parts(52_130_000, 0) + .saturating_add(Weight::from_parts(0, 5358)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -173,24 +181,24 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:1 w:1) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) - /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1893), added: 4368, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::OutboundMessages` (r:0 w:2) - /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { // Proof Size summary in bytes: - // Measured: `710` - // Estimated: `5383` - // Minimum execution time: 55_140_000 picoseconds. - Weight::from_parts(56_456_000, 0) - .saturating_add(Weight::from_parts(0, 5383)) + // Measured: `642` + // Estimated: `5358` + // Minimum execution time: 51_764_000 picoseconds. + Weight::from_parts(53_507_000, 0) + .saturating_add(Weight::from_parts(0, 5358)) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -199,24 +207,24 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::OutboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) /// Storage: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Proof: UNKNOWN KEY `0x6e0a18b62a1de81c5f519181cc611e18` (r:1 w:0) /// Storage: `BridgeRelayers::RelayerRewards` (r:2 w:2) - /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(102), added: 2577, mode: `MaxEncodedLen`) + /// Proof: `BridgeRelayers::RelayerRewards` (`max_values`: None, `max_size`: Some(73), added: 2548, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) - /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1893), added: 4368, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::OutboundMessages` (r:0 w:2) - /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { // Proof Size summary in bytes: - // Measured: `710` - // Estimated: `6144` - // Minimum execution time: 60_415_000 picoseconds. - Weight::from_parts(62_057_000, 0) - .saturating_add(Weight::from_parts(0, 6144)) + // Measured: `642` + // Estimated: `6086` + // Minimum execution time: 56_972_000 picoseconds. + Weight::from_parts(58_619_000, 0) + .saturating_add(Weight::from_parts(0, 6086)) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -225,11 +233,13 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Storage: `BridgeRococoParachains::ImportedParaHeads` (r:1 w:0) /// Proof: `BridgeRococoParachains::ImportedParaHeads` (`max_values`: Some(64), `max_size`: Some(196), added: 1186, mode: `MaxEncodedLen`) /// Storage: `BridgeRococoMessages::InboundLanes` (r:1 w:1) - /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49209), added: 51684, mode: `MaxEncodedLen`) + /// Proof: `BridgeRococoMessages::InboundLanes` (`max_values`: None, `max_size`: Some(49180), added: 51655, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubRococo::LaneToBridge` (r:1 w:0) - /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(65), added: 2540, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubRococo::LaneToBridge` (`max_values`: None, `max_size`: Some(36), added: 2511, mode: `MaxEncodedLen`) /// Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) - /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) + /// Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1893), added: 4368, mode: `MaxEncodedLen`) + /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) + /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `ParachainInfo::ParachainId` (r:1 w:0) /// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) /// Storage: `XcmpQueue::DeliveryFeeFactor` (r:1 w:0) @@ -242,20 +252,18 @@ impl pallet_bridge_messages::WeightInfo for WeightInfo< /// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `ParachainSystem::RelevantMessagingState` (r:1 w:0) /// Proof: `ParachainSystem::RelevantMessagingState` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) - /// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1) - /// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: Some(1282), added: 1777, mode: `MaxEncodedLen`) /// Storage: `XcmpQueue::OutboundXcmpMessages` (r:0 w:1) /// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: Some(105506), added: 107981, mode: `MaxEncodedLen`) /// The range of component `n` is `[1, 16384]`. fn receive_single_n_bytes_message_proof_with_dispatch(n: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `965` - // Estimated: `52674` - // Minimum execution time: 84_340_000 picoseconds. - Weight::from_parts(89_615_003, 0) - .saturating_add(Weight::from_parts(0, 52674)) - // Standard Error: 15 - .saturating_add(Weight::from_parts(7_574, 0).saturating_mul(n.into())) + // Measured: `896` + // Estimated: `52645` + // Minimum execution time: 85_043_000 picoseconds. + Weight::from_parts(89_141_726, 0) + .saturating_add(Weight::from_parts(0, 52645)) + // Standard Error: 14 + .saturating_add(Weight::from_parts(7_597, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(12)) .saturating_add(T::DbWeight::get().writes(4)) } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_xcm_bridge.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_xcm_bridge.rs new file mode 100644 index 000000000000..eccf6dc34ddd --- /dev/null +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/pallet_xcm_bridge.rs @@ -0,0 +1,81 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! Autogenerated weights for `pallet_xcm_bridge_hub` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-11-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-wiukf8gn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("bridge-hub-westend-dev")`, DB CACHE: 1024 + +// Executed Command: +// target/production/polkadot-parachain +// benchmark +// pallet +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_xcm_bridge_hub +// --chain=bridge-hub-westend-dev +// --header=./cumulus/file_header.txt +// --output=./cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/ + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_xcm_bridge_hub`. +pub struct WeightInfo(PhantomData); +impl pallet_xcm_bridge::WeightInfo for WeightInfo { + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn open_bridge() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn close_bridge() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `Benchmark::Override` (r:0 w:0) + /// Proof: `Benchmark::Override` (`max_values`: None, `max_size`: None, mode: `Measured`) + fn update_notification_receiver() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 18_446_744_073_709_551_000 picoseconds. + Weight::from_parts(18_446_744_073_709_551_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index 03cbaa866ad8..d728112147bb 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -17,9 +17,9 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-08-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-11-09, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-svzsllib-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-wiukf8gn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: Compiled, CHAIN: Some("bridge-hub-westend-dev"), DB CACHE: 1024 // Executed Command: @@ -68,8 +68,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `208` // Estimated: `6196` - // Minimum execution time: 70_353_000 picoseconds. - Weight::from_parts(72_257_000, 6196) + // Minimum execution time: 73_953_000 picoseconds. + Weight::from_parts(77_340_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -77,15 +77,26 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 996_000 picoseconds. - Weight::from_parts(1_027_000, 0) + // Minimum execution time: 1_087_000 picoseconds. + Weight::from_parts(1_144_000, 0) } + // Storage: `System::Account` (r:1 w:1) + // Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) pub fn pay_fees() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `3593` + // Minimum execution time: 3_908_000 picoseconds. + Weight::from_parts(4_071_000, 3593) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + pub fn asset_claimer() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_926_000 picoseconds. - Weight::from_parts(2_033_000, 0) + // Minimum execution time: 1_066_000 picoseconds. + Weight::from_parts(1_121_000, 0) } // Storage: `PolkadotXcm::Queries` (r:1 w:0) // Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -93,58 +104,58 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `32` // Estimated: `3497` - // Minimum execution time: 7_961_000 picoseconds. - Weight::from_parts(8_256_000, 3497) + // Minimum execution time: 8_100_000 picoseconds. + Weight::from_parts(8_237_000, 3497) .saturating_add(T::DbWeight::get().reads(1)) } pub fn transact() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 7_589_000 picoseconds. - Weight::from_parts(7_867_000, 0) + // Minimum execution time: 7_861_000 picoseconds. + Weight::from_parts(8_196_000, 0) } pub fn refund_surplus() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_602_000 picoseconds. - Weight::from_parts(1_660_000, 0) + // Minimum execution time: 1_757_000 picoseconds. + Weight::from_parts(1_841_000, 0) } pub fn set_error_handler() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_056_000 picoseconds. - Weight::from_parts(1_096_000, 0) + // Minimum execution time: 1_057_000 picoseconds. + Weight::from_parts(1_123_000, 0) } pub fn set_appendix() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_014_000 picoseconds. - Weight::from_parts(1_075_000, 0) + // Minimum execution time: 1_065_000 picoseconds. + Weight::from_parts(1_119_000, 0) } pub fn clear_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 986_000 picoseconds. - Weight::from_parts(1_031_000, 0) + // Minimum execution time: 1_030_000 picoseconds. + Weight::from_parts(1_102_000, 0) } pub fn descend_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_015_000 picoseconds. - Weight::from_parts(1_069_000, 0) + // Minimum execution time: 1_052_000 picoseconds. + Weight::from_parts(1_138_000, 0) } pub fn clear_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 993_000 picoseconds. - Weight::from_parts(1_063_000, 0) + // Minimum execution time: 1_056_000 picoseconds. + Weight::from_parts(1_169_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -166,8 +177,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `208` // Estimated: `6196` - // Minimum execution time: 66_350_000 picoseconds. - Weight::from_parts(68_248_000, 6196) + // Minimum execution time: 69_990_000 picoseconds. + Weight::from_parts(72_086_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -177,8 +188,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `90` // Estimated: `3555` - // Minimum execution time: 11_247_000 picoseconds. - Weight::from_parts(11_468_000, 3555) + // Minimum execution time: 11_270_000 picoseconds. + Weight::from_parts(11_624_000, 3555) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -186,8 +197,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_060_000 picoseconds. - Weight::from_parts(1_103_000, 0) + // Minimum execution time: 1_025_000 picoseconds. + Weight::from_parts(1_112_000, 0) } // Storage: `PolkadotXcm::VersionNotifyTargets` (r:1 w:1) // Proof: `PolkadotXcm::VersionNotifyTargets` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -207,8 +218,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `38` // Estimated: `3503` - // Minimum execution time: 25_599_000 picoseconds. - Weight::from_parts(26_336_000, 3503) + // Minimum execution time: 25_768_000 picoseconds. + Weight::from_parts(26_467_000, 3503) .saturating_add(T::DbWeight::get().reads(7)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -218,44 +229,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_863_000 picoseconds. - Weight::from_parts(3_090_000, 0) + // Minimum execution time: 3_020_000 picoseconds. + Weight::from_parts(3_199_000, 0) .saturating_add(T::DbWeight::get().writes(1)) } pub fn burn_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_385_000 picoseconds. - Weight::from_parts(1_468_000, 0) + // Minimum execution time: 1_533_000 picoseconds. + Weight::from_parts(1_598_000, 0) } pub fn expect_asset() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_087_000 picoseconds. - Weight::from_parts(1_164_000, 0) + // Minimum execution time: 1_109_000 picoseconds. + Weight::from_parts(1_181_000, 0) } pub fn expect_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_022_000 picoseconds. - Weight::from_parts(1_066_000, 0) + // Minimum execution time: 1_062_000 picoseconds. + Weight::from_parts(1_121_000, 0) } pub fn expect_error() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_015_000 picoseconds. - Weight::from_parts(1_070_000, 0) + // Minimum execution time: 1_094_000 picoseconds. + Weight::from_parts(1_120_000, 0) } pub fn expect_transact_status() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_203_000 picoseconds. - Weight::from_parts(1_241_000, 0) + // Minimum execution time: 1_254_000 picoseconds. + Weight::from_parts(1_327_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -277,8 +288,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `208` // Estimated: `6196` - // Minimum execution time: 70_773_000 picoseconds. - Weight::from_parts(72_730_000, 6196) + // Minimum execution time: 75_152_000 picoseconds. + Weight::from_parts(77_164_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -286,8 +297,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_173_000 picoseconds. - Weight::from_parts(4_445_000, 0) + // Minimum execution time: 4_454_000 picoseconds. + Weight::from_parts(4_613_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) @@ -309,8 +320,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `208` // Estimated: `6196` - // Minimum execution time: 66_471_000 picoseconds. - Weight::from_parts(68_362_000, 6196) + // Minimum execution time: 70_387_000 picoseconds. + Weight::from_parts(71_863_000, 6196) .saturating_add(T::DbWeight::get().reads(9)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -318,44 +329,44 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_067_000 picoseconds. - Weight::from_parts(1_108_000, 0) + // Minimum execution time: 1_108_000 picoseconds. + Weight::from_parts(1_150_000, 0) } pub fn set_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 997_000 picoseconds. - Weight::from_parts(1_043_000, 0) + // Minimum execution time: 1_051_000 picoseconds. + Weight::from_parts(1_108_000, 0) } pub fn clear_topic() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_000_000 picoseconds. - Weight::from_parts(1_056_000, 0) + // Minimum execution time: 1_041_000 picoseconds. + Weight::from_parts(1_110_000, 0) } // Storage: `ParachainInfo::ParachainId` (r:1 w:0) // Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + // Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) + // Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1893), added: 4368, mode: `MaxEncodedLen`) // Storage: `PolkadotXcm::SupportedVersion` (r:2 w:0) // Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`) - // Storage: `XcmOverBridgeHubRococo::Bridges` (r:1 w:0) - // Proof: `XcmOverBridgeHubRococo::Bridges` (`max_values`: None, `max_size`: Some(1918), added: 4393, mode: `MaxEncodedLen`) // Storage: `BridgeRococoMessages::PalletOperatingMode` (r:1 w:0) // Proof: `BridgeRococoMessages::PalletOperatingMode` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) // Storage: `BridgeRococoMessages::OutboundLanes` (r:1 w:1) - // Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(74), added: 2549, mode: `MaxEncodedLen`) + // Proof: `BridgeRococoMessages::OutboundLanes` (`max_values`: None, `max_size`: Some(45), added: 2520, mode: `MaxEncodedLen`) // Storage: `BridgeRococoMessages::OutboundMessages` (r:0 w:1) - // Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65597), added: 68072, mode: `MaxEncodedLen`) + // Proof: `BridgeRococoMessages::OutboundMessages` (`max_values`: None, `max_size`: Some(65568), added: 68043, mode: `MaxEncodedLen`) /// The range of component `x` is `[1, 1000]`. pub fn export_message(x: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `225` - // Estimated: `6165` - // Minimum execution time: 43_316_000 picoseconds. - Weight::from_parts(45_220_843, 6165) - // Standard Error: 169 - .saturating_add(Weight::from_parts(44_459, 0).saturating_mul(x.into())) + // Measured: `595` + // Estimated: `6535` + // Minimum execution time: 56_987_000 picoseconds. + Weight::from_parts(58_914_389, 6535) + // Standard Error: 154 + .saturating_add(Weight::from_parts(52_548, 0).saturating_mul(x.into())) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(2)) } @@ -363,8 +374,8 @@ impl WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 998_000 picoseconds. - Weight::from_parts(1_054_000, 0) + // Minimum execution time: 1_019_000 picoseconds. + Weight::from_parts(1_090_000, 0) } pub fn unpaid_execution() -> Weight { // Proof Size summary in bytes: @@ -373,13 +384,6 @@ impl WeightInfo { // Minimum execution time: 995_000 picoseconds. Weight::from_parts(1_060_000, 0) } - pub fn asset_claimer() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 707_000 picoseconds. - Weight::from_parts(749_000, 0) - } pub fn execute_with_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs index d7e7fbe0c72e..265223207a7f 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/tests/tests.rs @@ -249,7 +249,7 @@ fn handle_export_message_from_system_parachain_add_to_outbound_queue_works() { |locations, _fee| { bridge_hub_test_utils::open_bridge_with_storage::< Runtime, XcmOverBridgeHubRococoInstance - >(locations, LegacyLaneId([0, 0, 0, 1])) + >(locations, LegacyLaneId([0, 0, 0, 1]), None) } ).1 }, @@ -312,7 +312,7 @@ fn relayed_incoming_message_works() { bridge_hub_test_utils::open_bridge_with_storage::< Runtime, XcmOverBridgeHubRococoInstance, - >(locations, LegacyLaneId([0, 0, 0, 1])) + >(locations, LegacyLaneId([0, 0, 0, 1]), None) }, ) .1 @@ -347,7 +347,7 @@ fn free_relay_extrinsic_works() { bridge_hub_test_utils::open_bridge_with_storage::< Runtime, XcmOverBridgeHubRococoInstance, - >(locations, LegacyLaneId([0, 0, 0, 1])) + >(locations, LegacyLaneId([0, 0, 0, 1]), None) }, ) .1 diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml index 132e42deea4a..ec48d7dca669 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/Cargo.toml @@ -49,12 +49,14 @@ bp-polkadot-core = { workspace = true } bp-relayers = { workspace = true } bp-runtime = { workspace = true } bp-test-utils = { workspace = true } +bp-xcm-bridge = { workspace = true } +# TODO: FAIL-CI - do we need this here? if so, split tests or make them generic bp-xcm-bridge-hub = { workspace = true } pallet-bridge-grandpa = { workspace = true } pallet-bridge-messages = { features = ["test-helpers"], workspace = true } pallet-bridge-parachains = { workspace = true } pallet-bridge-relayers = { workspace = true } -pallet-xcm-bridge-hub = { workspace = true } +pallet-xcm-bridge = { workspace = true } [features] default = ["std"] @@ -67,6 +69,7 @@ std = [ "bp-relayers/std", "bp-runtime/std", "bp-test-utils/std", + "bp-xcm-bridge/std", "bp-xcm-bridge-hub/std", "codec/std", "cumulus-pallet-parachain-system/std", @@ -81,7 +84,7 @@ std = [ "pallet-bridge-relayers/std", "pallet-timestamp/std", "pallet-utility/std", - "pallet-xcm-bridge-hub/std", + "pallet-xcm-bridge/std", "pallet-xcm/std", "parachains-common/std", "parachains-runtimes-test-utils/std", diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs index 358c184c815d..b3c89b83c8bf 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_grandpa_chain.rs @@ -26,7 +26,7 @@ use alloc::{boxed::Box, vec}; use bp_header_chain::ChainWithGrandpa; use bp_messages::UnrewardedRelayersState; use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; -use bp_xcm_bridge_hub::XcmAsPlainPayload; +use bp_xcm_bridge::XcmAsPlainPayload; use frame_support::traits::{OnFinalize, OnInitialize}; use frame_system::pallet_prelude::BlockNumberFor; use pallet_bridge_messages::{BridgedChainOf, LaneIdOf, ThisChainOf}; diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs index d8fff55b4b50..ada2e95ea744 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/from_parachain.rs @@ -28,7 +28,7 @@ use bp_messages::UnrewardedRelayersState; use bp_polkadot_core::parachains::ParaHash; use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; use bp_runtime::{Chain, Parachain}; -use bp_xcm_bridge_hub::XcmAsPlainPayload; +use bp_xcm_bridge::XcmAsPlainPayload; use frame_support::traits::{OnFinalize, OnInitialize}; use frame_system::pallet_prelude::BlockNumberFor; use pallet_bridge_messages::{BridgedChainOf, LaneIdOf, ThisChainOf}; diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs index a99bda5bfdf4..0004c5a94534 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/helpers.rs @@ -23,7 +23,7 @@ use bp_messages::MessageNonce; use bp_polkadot_core::parachains::{ParaHash, ParaId}; use bp_relayers::RewardsAccountParams; use bp_runtime::Chain; -use bp_xcm_bridge_hub::BridgeLocations; +use bp_xcm_bridge::BridgeLocations; use codec::Decode; use core::marker::PhantomData; use frame_support::{ @@ -395,32 +395,32 @@ pub fn ensure_opened_bridge< XcmOverBridgePalletInstance, LocationToAccountId, TokenLocation> -(source: Location, destination: InteriorLocation, is_paid_xcm_execution: bool, bridge_opener: impl Fn(BridgeLocations, Option)) -> (BridgeLocations, pallet_xcm_bridge_hub::LaneIdOf) +(source: Location, destination: InteriorLocation, is_paid_xcm_execution: bool, bridge_opener: impl Fn(BridgeLocations, Option)) -> (BridgeLocations, pallet_xcm_bridge::LaneIdOf) where Runtime: BasicParachainRuntime + BridgeXcmOverBridgeConfig, XcmOverBridgePalletInstance: 'static, ::RuntimeCall: GetDispatchInfo + From>, - ::Balance: From<<>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::Balance>, + ::Balance: From<<>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::Balance>, ::Balance: From, LocationToAccountId: ConvertLocation>, TokenLocation: Get{ // construct expected bridge configuration let locations = - pallet_xcm_bridge_hub::Pallet::::bridge_locations( + pallet_xcm_bridge::Pallet::::bridge_locations( source.clone().into(), destination.clone().into(), ) .expect("valid bridge locations"); - assert!(pallet_xcm_bridge_hub::Bridges::::get( + assert!(pallet_xcm_bridge::Bridges::::get( locations.bridge_id() ) .is_none()); // SA of source location needs to have some required balance - if !>::AllowWithoutBridgeDeposit::contains(&source) { + if !>::AllowWithoutBridgeDeposit::contains(&source) { // required balance: ED + fee + BridgeDeposit let bridge_deposit = - >::BridgeDeposit::get( + >::BridgeDeposit::get( ); let balance_needed = ::ExistentialDeposit::get() + bridge_deposit.into(); @@ -449,14 +449,14 @@ TokenLocation: Get{ bridge_opener(*locations.clone(), maybe_paid_execution); // check opened bridge - let bridge = pallet_xcm_bridge_hub::Bridges::::get( + let bridge = pallet_xcm_bridge::Bridges::::get( locations.bridge_id(), ) .expect("opened bridge"); // check state assert_ok!( - pallet_xcm_bridge_hub::Pallet::::do_try_state() + pallet_xcm_bridge::Pallet::::do_try_state() ); // return locations @@ -470,7 +470,7 @@ pub fn open_bridge_with_extrinsic( maybe_paid_execution: Option, ) where Runtime: frame_system::Config - + pallet_xcm_bridge_hub::Config + + pallet_xcm_bridge::Config + cumulus_pallet_parachain_system::Config + pallet_xcm::Config, XcmOverBridgePalletInstance: 'static, @@ -485,6 +485,7 @@ pub fn open_bridge_with_extrinsic( bridge_destination_universal_location: Box::new( bridge_destination_universal_location.clone().into(), ), + maybe_notify: None, }); // execute XCM as source origin would do with `Transact -> Origin::Xcm` @@ -500,17 +501,19 @@ pub fn open_bridge_with_extrinsic( /// purposes). pub fn open_bridge_with_storage( locations: BridgeLocations, - lane_id: pallet_xcm_bridge_hub::LaneIdOf, + lane_id: pallet_xcm_bridge::LaneIdOf, + maybe_notify: Option, ) where - Runtime: pallet_xcm_bridge_hub::Config, + Runtime: pallet_xcm_bridge::Config, XcmOverBridgePalletInstance: 'static, { // insert bridge data directly to the storage assert_ok!( - pallet_xcm_bridge_hub::Pallet::::do_open_bridge( + pallet_xcm_bridge::Pallet::::do_open_bridge( Box::new(locations), lane_id, - true + true, + maybe_notify, ) ); } @@ -525,18 +528,18 @@ pub fn close_bridge, XcmOverBridgePalletInstance: 'static, ::RuntimeCall: GetDispatchInfo + From>, - ::Balance: From<<>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::Balance>, + ::Balance: From<<>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::Balance>, ::Balance: From, LocationToAccountId: ConvertLocation>, TokenLocation: Get{ // construct expected bridge configuration let locations = - pallet_xcm_bridge_hub::Pallet::::bridge_locations( + pallet_xcm_bridge::Pallet::::bridge_locations( expected_source.clone().into(), bridge_destination_universal_location.clone().into(), ) .expect("valid bridge locations"); - assert!(pallet_xcm_bridge_hub::Bridges::::get( + assert!(pallet_xcm_bridge::Bridges::::get( locations.bridge_id() ) .is_some()); @@ -578,13 +581,13 @@ TokenLocation: Get{ .ensure_complete()); // bridge is closed - assert!(pallet_xcm_bridge_hub::Bridges::::get( + assert!(pallet_xcm_bridge::Bridges::::get( locations.bridge_id() ) .is_none()); // check state assert_ok!( - pallet_xcm_bridge_hub::Pallet::::do_try_state() + pallet_xcm_bridge::Pallet::::do_try_state() ); } diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs index f96d0bf405b9..3015d9892b50 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_cases/mod.rs @@ -32,7 +32,7 @@ use bp_messages::{ LaneState, MessageKey, MessagesOperatingMode, OutboundLaneData, }; use bp_runtime::BasicOperatingMode; -use bp_xcm_bridge_hub::{Bridge, BridgeState, XcmAsPlainPayload}; +use bp_xcm_bridge::{Bridge, BridgeState, XcmAsPlainPayload}; use codec::Encode; use frame_support::{ assert_ok, @@ -63,7 +63,7 @@ pub(crate) mod bridges_prelude { pub use pallet_bridge_parachains::{ Call as BridgeParachainsCall, Config as BridgeParachainsConfig, }; - pub use pallet_xcm_bridge_hub::{ + pub use pallet_xcm_bridge::{ Call as BridgeXcmOverBridgeCall, Config as BridgeXcmOverBridgeConfig, LanesManagerOf, XcmBlobMessageDispatchResult, }; @@ -662,15 +662,15 @@ pub fn open_and_close_bridge_works, XcmOverBridgePalletInstance: 'static, ::RuntimeCall: GetDispatchInfo + From>, - ::Balance: From<<>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::Balance>, + ::Balance: From<<>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::Balance>, ::Balance: From, - <>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::AccountId: From<::AccountId>, + <>::BridgeMessagesPalletInstance>>::ThisChain as bp_runtime::Chain>::AccountId: From<::AccountId>, LocationToAccountId: ConvertLocation>, TokenLocation: Get, { run_test::(collator_session_key, runtime_para_id, vec![], || { // construct expected bridge configuration - let locations = pallet_xcm_bridge_hub::Pallet::::bridge_locations( + let locations = pallet_xcm_bridge::Pallet::::bridge_locations( expected_source.clone().into(), destination.clone().into(), ).expect("valid bridge locations"); @@ -678,21 +678,26 @@ pub fn open_and_close_bridge_works::new(); - let expected_deposit = if >::AllowWithoutBridgeDeposit::contains( locations.bridge_origin_relative_location() ) { - Zero::zero() + None } else { - >::BridgeDeposit::get() + >>::BridgeDeposit::get(); + + Some(bp_xcm_bridge::Deposit::new(bridge_owner_account, deposit)) }; // check bridge/lane DOES not exist assert_eq!( - pallet_xcm_bridge_hub::Bridges::::get( + pallet_xcm_bridge::Bridges::::get( locations.bridge_id() ), None @@ -733,7 +738,7 @@ pub fn open_and_close_bridge_works::get( + pallet_xcm_bridge::Bridges::::get( locations.bridge_id() ), Some(Bridge { @@ -745,11 +750,9 @@ pub fn open_and_close_bridge_works::get( + pallet_xcm_bridge::Bridges::::get( locations.bridge_id() ), None diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs index 7461085330f2..72f16093773d 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_grandpa_chain.rs @@ -25,7 +25,7 @@ use bp_messages::{ }; use bp_runtime::{AccountIdOf, BlockNumberOf, Chain, HeaderOf, UnverifiedStorageProofParams}; use bp_test_utils::make_default_justification; -use bp_xcm_bridge_hub::XcmAsPlainPayload; +use bp_xcm_bridge::XcmAsPlainPayload; use codec::Encode; use pallet_bridge_grandpa::{BridgedChain, BridgedHeader}; use sp_runtime::traits::Header as HeaderT; diff --git a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs index a6659b8241df..4e78bbad8896 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/test-utils/src/test_data/from_parachain.rs @@ -28,7 +28,7 @@ use bp_runtime::{ AccountIdOf, BlockNumberOf, Chain, HeaderOf, Parachain, UnverifiedStorageProofParams, }; use bp_test_utils::prepare_parachain_heads_proof; -use bp_xcm_bridge_hub::XcmAsPlainPayload; +use bp_xcm_bridge::XcmAsPlainPayload; use codec::Encode; use pallet_bridge_grandpa::BridgedHeader; use sp_runtime::traits::Header as HeaderT; diff --git a/prdoc/pr_6231.prdoc b/prdoc/pr_6231.prdoc new file mode 100644 index 000000000000..e98abcb5413e --- /dev/null +++ b/prdoc/pr_6231.prdoc @@ -0,0 +1,45 @@ +title: Bridges - Add improved congestion control mechanism +doc: +- audience: Runtime Dev + description: |- + This release introduces an enhanced bridge congestion mechanism, decoupling bridge queues from XCMP and improving suspension handling with bridge-specific channel control. + Using `BridgeId`, `pallet-xcm-bridge-hub` and `pallet-xcm-bridge-hub-router` now support selective suspension and resumption of individual bridges. + A new congestion detection feature in `pallet-xcm-bridge-hub` triggers `fn suspend_bridge` and `fn resume_bridge` callbacks: + if the router is on a sibling chain, it sends `xcm::Transact(update_bridge_status(bridge_id, is_congested))` using stored callback data, while on local chains, it directly manages state. + Additionally, a new `stop_threshold` in `pallet-xcm-bridge-hub` enables or disables `ExportXcm::validate`, acting as a safeguard if the router ignores the `suspend` signal. + Finally, `pallet-xcm-bridge-hub-router` now flexibly supports message routing to either sibling chains (`ExportMessage`) or local deployments (`ExportXcm`), + enhancing modularity and compatibility across deployment setups. + +crates: +- name: pallet-xcm-bridge-hub-router + bump: major +- name: pallet-xcm-bridge-hub + bump: major +- name: bp-xcm-bridge-hub-router + bump: major +- name: asset-hub-rococo-runtime + bump: major +- name: asset-hub-westend-runtime + bump: major +- name: bp-xcm-bridge-hub + bump: major +- name: bridge-hub-rococo-runtime + bump: patch +- name: bridge-hub-westend-runtime + bump: patch +- name: asset-test-utils + bump: major +- name: cumulus-pallet-xcmp-queue + bump: patch +- name: bridge-hub-test-utils + bump: major +- name: emulated-integration-tests-common + bump: patch +- name: bp-asset-hub-rococo + bump: major +- name: bp-asset-hub-westend + bump: major +- name: bp-bridge-hub-rococo + bump: patch +- name: bp-bridge-hub-westend + bump: patch \ No newline at end of file diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index fc0b2d5a140e..b1245fa156f7 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -20,6 +20,8 @@ std = [ "bp-test-utils?/std", "bp-xcm-bridge-hub-router?/std", "bp-xcm-bridge-hub?/std", + "bp-xcm-bridge-router?/std", + "bp-xcm-bridge?/std", "bridge-hub-common?/std", "bridge-hub-test-utils?/std", "bridge-runtime-common?/std", @@ -155,6 +157,8 @@ std = [ "pallet-xcm-benchmarks?/std", "pallet-xcm-bridge-hub-router?/std", "pallet-xcm-bridge-hub?/std", + "pallet-xcm-bridge-router?/std", + "pallet-xcm-bridge?/std", "pallet-xcm?/std", "parachains-common?/std", "parachains-runtimes-test-utils?/std", @@ -337,6 +341,8 @@ runtime-benchmarks = [ "pallet-xcm-benchmarks?/runtime-benchmarks", "pallet-xcm-bridge-hub-router?/runtime-benchmarks", "pallet-xcm-bridge-hub?/runtime-benchmarks", + "pallet-xcm-bridge-router?/runtime-benchmarks", + "pallet-xcm-bridge?/runtime-benchmarks", "pallet-xcm?/runtime-benchmarks", "parachains-common?/runtime-benchmarks", "polkadot-cli?/runtime-benchmarks", @@ -475,6 +481,8 @@ try-runtime = [ "pallet-whitelist?/try-runtime", "pallet-xcm-bridge-hub-router?/try-runtime", "pallet-xcm-bridge-hub?/try-runtime", + "pallet-xcm-bridge-router?/try-runtime", + "pallet-xcm-bridge?/try-runtime", "pallet-xcm?/try-runtime", "polkadot-cli?/try-runtime", "polkadot-omni-node-lib?/try-runtime", @@ -546,7 +554,7 @@ with-tracing = [ "sp-tracing?/with-tracing", "sp-tracing?/with-tracing", ] -runtime-full = ["assets-common", "binary-merkle-tree", "bp-header-chain", "bp-messages", "bp-parachains", "bp-polkadot", "bp-polkadot-core", "bp-relayers", "bp-runtime", "bp-test-utils", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system-proc-macro", "cumulus-pallet-session-benchmarking", "cumulus-pallet-solo-to-para", "cumulus-pallet-weight-reclaim", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-executive", "frame-metadata-hash-extension", "frame-support", "frame-support-procedural", "frame-support-procedural-tools-derive", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", "pallet-alliance", "pallet-asset-conversion", "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", "pallet-asset-rewards", "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", "pallet-broker", "pallet-child-bounties", "pallet-collator-selection", "pallet-collective", "pallet-collective-content", "pallet-contracts", "pallet-contracts-proc-macro", "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", "pallet-node-authorization", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-revive", "pallet-revive-proc-macro", "pallet-revive-uapi", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", "pallet-society", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-reward-fn", "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-verify-signature", "pallet-vesting", "pallet-whitelist", "pallet-xcm", "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-metrics", "polkadot-runtime-parachains", "polkadot-sdk-frame", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", "snowbridge-outbound-queue-merkle-tree", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", "sp-consensus-pow", "sp-consensus-slots", "sp-core", "sp-crypto-ec-utils", "sp-crypto-hashing", "sp-crypto-hashing-proc-macro", "sp-debug-derive", "sp-externalities", "sp-genesis-builder", "sp-inherents", "sp-io", "sp-keyring", "sp-keystore", "sp-metadata-ir", "sp-mixnet", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-runtime-interface", "sp-runtime-interface-proc-macro", "sp-session", "sp-staking", "sp-state-machine", "sp-statement-store", "sp-std", "sp-storage", "sp-timestamp", "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", "xcm-procedural", "xcm-runtime-apis"] +runtime-full = ["assets-common", "binary-merkle-tree", "bp-header-chain", "bp-messages", "bp-parachains", "bp-polkadot", "bp-polkadot-core", "bp-relayers", "bp-runtime", "bp-test-utils", "bp-xcm-bridge", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bp-xcm-bridge-router", "bridge-hub-common", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system-proc-macro", "cumulus-pallet-session-benchmarking", "cumulus-pallet-solo-to-para", "cumulus-pallet-weight-reclaim", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-executive", "frame-metadata-hash-extension", "frame-support", "frame-support-procedural", "frame-support-procedural-tools-derive", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", "pallet-alliance", "pallet-asset-conversion", "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", "pallet-asset-rewards", "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", "pallet-broker", "pallet-child-bounties", "pallet-collator-selection", "pallet-collective", "pallet-collective-content", "pallet-contracts", "pallet-contracts-proc-macro", "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", "pallet-node-authorization", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-revive", "pallet-revive-proc-macro", "pallet-revive-uapi", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", "pallet-society", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-reward-fn", "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-verify-signature", "pallet-vesting", "pallet-whitelist", "pallet-xcm", "pallet-xcm-benchmarks", "pallet-xcm-bridge", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", "pallet-xcm-bridge-router", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-metrics", "polkadot-runtime-parachains", "polkadot-sdk-frame", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", "snowbridge-outbound-queue-merkle-tree", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", "sp-consensus-pow", "sp-consensus-slots", "sp-core", "sp-crypto-ec-utils", "sp-crypto-hashing", "sp-crypto-hashing-proc-macro", "sp-debug-derive", "sp-externalities", "sp-genesis-builder", "sp-inherents", "sp-io", "sp-keyring", "sp-keystore", "sp-metadata-ir", "sp-mixnet", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-runtime-interface", "sp-runtime-interface-proc-macro", "sp-session", "sp-staking", "sp-state-machine", "sp-statement-store", "sp-std", "sp-storage", "sp-timestamp", "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", "xcm-procedural", "xcm-runtime-apis"] runtime = [ "frame-benchmarking", "frame-benchmarking-pallet-pov", @@ -678,6 +686,11 @@ default-features = false optional = true path = "../bridges/primitives/test-utils" +[dependencies.bp-xcm-bridge] +default-features = false +optional = true +path = "../bridges/primitives/xcm-bridge" + [dependencies.bp-xcm-bridge-hub] default-features = false optional = true @@ -688,6 +701,11 @@ default-features = false optional = true path = "../bridges/primitives/xcm-bridge-hub-router" +[dependencies.bp-xcm-bridge-router] +default-features = false +optional = true +path = "../bridges/primitives/xcm-bridge-router" + [dependencies.bridge-hub-common] default-features = false optional = true @@ -1373,6 +1391,11 @@ default-features = false optional = true path = "../polkadot/xcm/pallet-xcm-benchmarks" +[dependencies.pallet-xcm-bridge] +default-features = false +optional = true +path = "../bridges/modules/xcm-bridge" + [dependencies.pallet-xcm-bridge-hub] default-features = false optional = true @@ -1383,6 +1406,11 @@ default-features = false optional = true path = "../bridges/modules/xcm-bridge-hub-router" +[dependencies.pallet-xcm-bridge-router] +default-features = false +optional = true +path = "../bridges/modules/xcm-bridge-router" + [dependencies.parachains-common] default-features = false optional = true diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index a132f16a2c33..54bdf17245e4 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -56,13 +56,21 @@ pub use bp_runtime; pub use bp_test_utils; /// Primitives of the xcm-bridge-hub pallet. +#[cfg(feature = "bp-xcm-bridge")] +pub use bp_xcm_bridge; + +/// Primitives of the xcm-bridge-hub pallet (deprecated: use bp-xcm-bridge). #[cfg(feature = "bp-xcm-bridge-hub")] pub use bp_xcm_bridge_hub; -/// Primitives of the xcm-bridge-hub fee pallet. +/// Primitives of the xcm-bridge-hub fee pallet (deprecated: use bp-xcm-bridge-router). #[cfg(feature = "bp-xcm-bridge-hub-router")] pub use bp_xcm_bridge_hub_router; +/// Primitives of the xcm-bridge-hub fee pallet. +#[cfg(feature = "bp-xcm-bridge-router")] +pub use bp_xcm_bridge_router; + /// Bridge hub common utilities. #[cfg(feature = "bridge-hub-common")] pub use bridge_hub_common; @@ -734,13 +742,21 @@ pub use pallet_xcm; pub use pallet_xcm_benchmarks; /// Module that adds dynamic bridges/lanes support to XCM infrastructure at the bridge hub. +#[cfg(feature = "pallet-xcm-bridge")] +pub use pallet_xcm_bridge; + +/// Module that adds dynamic bridges/lanes support to XCM infrastructure at the bridge hub (deprecated: use pallet-xcm-bridge). #[cfg(feature = "pallet-xcm-bridge-hub")] pub use pallet_xcm_bridge_hub; -/// Bridge hub interface for sibling/parent chains with dynamic fees support. +/// Bridge hub interface for sibling/parent chains with dynamic fees support (deprecated: use pallet-xcm-bridge-router). #[cfg(feature = "pallet-xcm-bridge-hub-router")] pub use pallet_xcm_bridge_hub_router; +/// Bridge hub interface for sibling/parent chains with dynamic fees support. +#[cfg(feature = "pallet-xcm-bridge-router")] +pub use pallet_xcm_bridge_router; + /// Logic which is common to all parachain runtimes. #[cfg(feature = "parachains-common")] pub use parachains_common;