diff --git a/Cargo.lock b/Cargo.lock index 863a1cb059..9cd7c55099 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1344,11 +1344,13 @@ dependencies = [ "pallet-utility 27.0.0", "pallet-xcm 6.0.0", "pallet-xcm-benchmarks 6.0.0", + "pallet-xcm-bridge-hub", "parachains-common 6.0.0", "parity-scale-codec", "polkadot-core-primitives 6.0.0", "polkadot-parachain-primitives 5.0.0", "polkadot-runtime-common 6.0.0", + "polkadot-runtime-constants", "scale-info", "serde", "smallvec", @@ -1410,6 +1412,7 @@ dependencies = [ "frame-system-rpc-runtime-api 25.0.0", "frame-try-runtime 0.33.0", "hex-literal", + "kusama-runtime-constants", "log", "pallet-aura 26.0.0", "pallet-authorship 27.0.0", @@ -1428,6 +1431,7 @@ dependencies = [ "pallet-utility 27.0.0", "pallet-xcm 6.0.0", "pallet-xcm-benchmarks 6.0.0", + "pallet-xcm-bridge-hub", "parachains-common 6.0.0", "parity-scale-codec", "polkadot-core-primitives 6.0.0", @@ -8760,6 +8764,30 @@ dependencies = [ "staging-xcm-executor 6.0.0", ] +[[package]] +name = "pallet-xcm-bridge-hub" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ac06adbe492bda1c840e3a775b69d619391fe1a631cebd76010dc8394fed75f" +dependencies = [ + "bp-messages", + "bp-runtime", + "bp-xcm-bridge-hub", + "bridge-runtime-common", + "frame-support 27.0.0", + "frame-system 27.0.0", + "log", + "pallet-bridge-messages", + "parity-scale-codec", + "scale-info", + "sp-core 27.0.0", + "sp-runtime 30.0.1", + "sp-std 13.0.0", + "staging-xcm 6.0.0", + "staging-xcm-builder 6.0.0", + "staging-xcm-executor 6.0.0", +] + [[package]] name = "pallet-xcm-bridge-hub-router" version = "0.4.0" diff --git a/system-parachains/asset-hubs/asset-hub-kusama/tests/tests.rs b/system-parachains/asset-hubs/asset-hub-kusama/tests/tests.rs index a70901b5b4..a499e97c10 100644 --- a/system-parachains/asset-hubs/asset-hub-kusama/tests/tests.rs +++ b/system-parachains/asset-hubs/asset-hub-kusama/tests/tests.rs @@ -530,12 +530,6 @@ asset_test_utils::include_teleports_for_native_asset_works!( _ => None, } }), - Box::new(|runtime_event_encoded: Vec| { - match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { - Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), - _ => None, - } - }), 1000 ); @@ -656,7 +650,7 @@ fn bridging_to_asset_hub_polkadot() -> TestBridgingConfig { #[test] fn limited_reserve_transfer_assets_for_native_asset_to_asset_hub_polkadot_works() { - missing_asset_test_utils_test_cases_over_bridge::limited_reserve_transfer_assets_for_native_asset_works::< + asset_test_utils::test_cases_over_bridge::limited_reserve_transfer_assets_for_native_asset_works::< Runtime, AllPalletsWithoutSystem, XcmConfig, @@ -682,7 +676,7 @@ fn limited_reserve_transfer_assets_for_native_asset_to_asset_hub_polkadot_works( bridging_to_asset_hub_polkadot, WeightLimit::Unlimited, Some(XcmBridgeHubRouterFeeAssetId::get()), - TreasuryAccount::get(), + Some(TreasuryAccount::get()), ) } @@ -713,7 +707,7 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_polkadot_works() { #[test] fn report_bridge_status_from_xcm_bridge_router_for_polkadot_works() { - missing_asset_test_utils_test_cases_over_bridge::report_bridge_status_from_xcm_bridge_router_works::< + asset_test_utils::test_cases_over_bridge::report_bridge_status_from_xcm_bridge_router_works::< Runtime, AllPalletsWithoutSystem, XcmConfig, @@ -791,455 +785,3 @@ fn change_xcm_bridge_hub_router_byte_fee_by_governance_works() { }, ) } - -// missing stuff from asset_test_utils::test_cases_over_bridge -// TODO: replace me with direct usages of `asset_test_utils` after deps are bumped to (at least) 1.4 -mod missing_asset_test_utils_test_cases_over_bridge { - use asset_test_utils::test_cases_over_bridge::TestBridgingConfig; - use codec::Encode; - use cumulus_primitives_core::XcmpMessageSource; - use frame_support::{ - assert_ok, - traits::{Currency, Get, OnFinalize, OnInitialize, OriginTrait, ProcessMessageError}, - }; - use frame_system::pallet_prelude::BlockNumberFor; - use parachains_common::{AccountId, Balance}; - use parachains_runtimes_test_utils::{ - mock_open_hrmp_channel, AccountIdOf, BalanceOf, CollatorSessionKeys, ExtBuilder, - RuntimeHelper, ValidatorIdOf, XcmReceivedFrom, - }; - use sp_runtime::{traits::StaticLookup, Saturating}; - use xcm::{latest::prelude::*, VersionedMultiAssets}; - use xcm_builder::{CreateMatcher, MatchXcm}; - use xcm_executor::{ - traits::{ConvertLocation, TransactAsset}, - XcmExecutor, - }; - - /// Helper function to verify `xcm` contains all relevant instructions expected on destination - /// chain as part of a reserve-asset-transfer. - fn assert_matches_reserve_asset_deposited_instructions( - xcm: &mut Xcm, - expected_reserve_assets_deposited: &MultiAssets, - expected_beneficiary: &MultiLocation, - ) { - let _ = xcm - .0 - .matcher() - .skip_inst_while(|inst| !matches!(inst, ReserveAssetDeposited(..))) - .expect("no instruction ReserveAssetDeposited?") - .match_next_inst(|instr| match instr { - ReserveAssetDeposited(reserve_assets) => { - assert_eq!(reserve_assets, expected_reserve_assets_deposited); - Ok(()) - }, - _ => Err(ProcessMessageError::BadFormat), - }) - .expect("expected instruction ReserveAssetDeposited") - .match_next_inst(|instr| match instr { - ClearOrigin => Ok(()), - _ => Err(ProcessMessageError::BadFormat), - }) - .expect("expected instruction ClearOrigin") - .match_next_inst(|instr| match instr { - BuyExecution { .. } => Ok(()), - _ => Err(ProcessMessageError::BadFormat), - }) - .expect("expected instruction BuyExecution") - .match_next_inst(|instr| match instr { - DepositAsset { assets: _, beneficiary } if beneficiary == expected_beneficiary => - Ok(()), - _ => Err(ProcessMessageError::BadFormat), - }) - .expect("expected instruction DepositAsset"); - } - - pub fn limited_reserve_transfer_assets_for_native_asset_works< - Runtime, - AllPalletsWithoutSystem, - XcmConfig, - HrmpChannelOpener, - HrmpChannelSource, - LocationToAccountId, - >( - collator_session_keys: CollatorSessionKeys, - existential_deposit: BalanceOf, - alice_account: AccountIdOf, - unwrap_pallet_xcm_event: Box) -> Option>>, - unwrap_xcmp_queue_event: Box< - dyn Fn(Vec) -> Option>, - >, - prepare_configuration: fn() -> TestBridgingConfig, - weight_limit: WeightLimit, - maybe_paid_export_message: Option, - delivery_fees_account: Option>, - ) 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, - 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, - HrmpChannelOpener: frame_support::inherent::ProvideInherent< - Call = cumulus_pallet_parachain_system::Call, - >, - HrmpChannelSource: XcmpMessageSource, - { - let runtime_para_id = 1000; - ExtBuilder::::default() - .with_collators(collator_session_keys.collators()) - .with_session_keys(collator_session_keys.session_keys()) - .with_tracing() - .with_safe_xcm_version(3) - .with_para_id(runtime_para_id.into()) - .build() - .execute_with(|| { - let mut alice = [0u8; 32]; - alice[0] = 1; - let included_head = RuntimeHelper::::run_to_block( - 2, - AccountId::from(alice).into(), - ); - - // prepare bridge config - let TestBridgingConfig { - bridged_network, - local_bridge_hub_para_id, - bridged_target_location: target_location_from_different_consensus, - .. - } = prepare_configuration(); - - let reserve_account = LocationToAccountId::convert_location( - &target_location_from_different_consensus, - ) - .expect("Sovereign account for reserves"); - let balance_to_transfer = 1_000_000_000_000_u128; - let native_asset = MultiLocation::parent(); - - // open HRMP to bridge hub - mock_open_hrmp_channel::( - runtime_para_id.into(), - local_bridge_hub_para_id.into(), - included_head, - &alice, - ); - - // drip ED to account - let alice_account_init_balance = existential_deposit + balance_to_transfer.into(); - let _ = >::deposit_creating( - &alice_account, - alice_account_init_balance, - ); - // SA of target location needs to have at least ED, otherwise making reserve fails - let _ = >::deposit_creating( - &reserve_account, - existential_deposit, - ); - - // we just check here, that user retains enough balance after withdrawal - // and also we check if `balance_to_transfer` is more than `existential_deposit`, - assert!( - (>::free_balance(&alice_account) - - balance_to_transfer.into()) >= - existential_deposit - ); - // SA has just ED - assert_eq!( - >::free_balance(&reserve_account), - existential_deposit - ); - - let delivery_fees_account_balance_before = delivery_fees_account - .as_ref() - .map(|dfa| >::free_balance(dfa)) - .unwrap_or(0.into()); - - // local native asset (pallet_balances) - let asset_to_transfer = MultiAsset { - fun: Fungible(balance_to_transfer.into()), - id: Concrete(native_asset), - }; - - // destination is (some) account relative to the destination different consensus - let target_destination_account = MultiLocation { - parents: 0, - interior: X1(AccountId32 { - network: Some(bridged_network), - id: sp_runtime::AccountId32::new([3; 32]).into(), - }), - }; - - let assets_to_transfer = MultiAssets::from(asset_to_transfer); - let mut expected_assets = assets_to_transfer.clone(); - let context = XcmConfig::UniversalLocation::get(); - expected_assets - .reanchor(&target_location_from_different_consensus, context) - .unwrap(); - - let expected_beneficiary = target_destination_account; - - // Make sure sender has enough funds for paying delivery fees - let handling_delivery_fees = { - // Probable XCM with `ReserveAssetDeposited`. - let mut expected_reserve_asset_deposited_message = Xcm(vec![ - ReserveAssetDeposited(MultiAssets::from(expected_assets.clone())), - ClearOrigin, - BuyExecution { - fees: MultiAsset { - id: Concrete(Default::default()), - fun: Fungible(balance_to_transfer), - }, - weight_limit: Unlimited, - }, - DepositAsset { - assets: Wild(AllCounted(1)), - beneficiary: expected_beneficiary, - }, - SetTopic([ - 220, 188, 144, 32, 213, 83, 111, 175, 44, 210, 111, 19, 90, 165, 191, - 112, 140, 247, 192, 124, 42, 17, 153, 141, 114, 34, 189, 20, 83, 69, - 237, 173, - ]), - ]); - assert_matches_reserve_asset_deposited_instructions( - &mut expected_reserve_asset_deposited_message, - &expected_assets, - &expected_beneficiary, - ); - - // Call `SendXcm::validate` to get delivery fees. - let (_, delivery_fees): (_, MultiAssets) = XcmConfig::XcmSender::validate( - &mut Some(target_location_from_different_consensus), - &mut Some(expected_reserve_asset_deposited_message), - ) - .expect("validate passes"); - // Drip delivery fee to Alice account. - let mut delivery_fees_added = false; - for delivery_fee in delivery_fees.inner() { - assert_ok!(::deposit_asset( - &delivery_fee, - &MultiLocation { - parents: 0, - interior: X1(AccountId32 { - network: None, - id: alice_account.clone().into(), - }), - }, - None, - )); - delivery_fees_added = true; - } - delivery_fees_added - }; - - // do pallet_xcm call reserve transfer - assert_ok!(>::limited_reserve_transfer_assets( - RuntimeHelper::::origin_of( - alice_account.clone() - ), - Box::new(target_location_from_different_consensus.into_versioned()), - Box::new(target_destination_account.into_versioned()), - Box::new(VersionedMultiAssets::from(assets_to_transfer)), - 0, - weight_limit, - )); - - // check events - // check pallet_xcm attempted - RuntimeHelper::::assert_pallet_xcm_event_outcome( - &unwrap_pallet_xcm_event, - |outcome| { - assert_ok!(outcome.ensure_complete()); - }, - ); - - // check that xcm was sent - let xcm_sent_message_hash = >::events() - .into_iter() - .filter_map(|e| unwrap_xcmp_queue_event(e.event.encode())) - .find_map(|e| match e { - cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { message_hash } => - Some(message_hash), - _ => None, - }); - - // read xcm - let xcm_sent = - RuntimeHelper::::take_xcm( - local_bridge_hub_para_id.into(), - ) - .unwrap(); - assert_eq!( - xcm_sent_message_hash, - Some(xcm_sent.using_encoded(sp_io::hashing::blake2_256)) - ); - let mut xcm_sent: Xcm<()> = xcm_sent.try_into().expect("versioned xcm"); - - // check sent XCM ExportMessage to BridgeHub - - // 1. check paid or unpaid - if let Some(expected_fee_asset_id) = maybe_paid_export_message { - xcm_sent - .0 - .matcher() - .match_next_inst(|instr| match instr { - WithdrawAsset(_) => Ok(()), - _ => Err(ProcessMessageError::BadFormat), - }) - .expect("contains WithdrawAsset") - .match_next_inst(|instr| match instr { - BuyExecution { fees, .. } if fees.id.eq(&expected_fee_asset_id) => - Ok(()), - _ => Err(ProcessMessageError::BadFormat), - }) - .expect("contains BuyExecution") - } else { - xcm_sent - .0 - .matcher() - .match_next_inst(|instr| match instr { - // first instruction could be UnpaidExecution (because we could have - // explicit unpaid execution on BridgeHub) - UnpaidExecution { weight_limit, check_origin } - if weight_limit == &Unlimited && check_origin.is_none() => - Ok(()), - _ => Err(ProcessMessageError::BadFormat), - }) - .expect("contains UnpaidExecution") - } - // 2. check ExportMessage - .match_next_inst(|instr| match instr { - // next instruction is ExportMessage - ExportMessage { network, destination, xcm: inner_xcm } => { - assert_eq!(network, &bridged_network); - let (_, target_location_junctions_without_global_consensus) = - target_location_from_different_consensus - .interior - .split_global() - .expect("split works"); - assert_eq!( - destination, - &target_location_junctions_without_global_consensus - ); - assert_matches_reserve_asset_deposited_instructions( - inner_xcm, - &expected_assets, - &expected_beneficiary, - ); - Ok(()) - }, - _ => Err(ProcessMessageError::BadFormat), - }) - .expect("contains ExportMessage"); - - // check alice account decreased by balance_to_transfer - assert_eq!( - >::free_balance(&alice_account), - alice_account_init_balance - .saturating_sub(existential_deposit) - .saturating_sub(balance_to_transfer.into()) - ); - - // check reserve account increased by balance_to_transfer - assert_eq!( - >::free_balance(&reserve_account), - existential_deposit + balance_to_transfer.into() - ); - - // check dedicated account increased by delivery fees (if configured) - if handling_delivery_fees { - if let Some(delivery_fees_account) = delivery_fees_account { - let delivery_fees_account_balance_after = - >::free_balance( - &delivery_fees_account, - ); - assert!( - delivery_fees_account_balance_after > - delivery_fees_account_balance_before - ); - } - } - }) - } - - pub fn report_bridge_status_from_xcm_bridge_router_works< - Runtime, - AllPalletsWithoutSystem, - XcmConfig, - LocationToAccountId, - XcmBridgeHubRouterInstance, - >( - collator_session_keys: CollatorSessionKeys, - prepare_configuration: fn() -> TestBridgingConfig, - congested_message: fn() -> Xcm, - uncongested_message: fn() -> 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, - 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 report_bridge_status = |is_congested: bool| { - // prepare bridge config - let TestBridgingConfig { local_bridge_hub_location, .. } = prepare_configuration(); - - // Call received XCM execution - let xcm = if is_congested { congested_message() } else { uncongested_message() }; - let hash = xcm.using_encoded(sp_io::hashing::blake2_256); - - // execute xcm as XcmpQueue would do - let outcome = XcmExecutor::::execute_xcm( - local_bridge_hub_location, - xcm, - hash, - RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Sibling), - ); - assert_eq!(outcome.ensure_complete(), Ok(())); - assert_eq!(is_congested, pallet_xcm_bridge_hub_router::Pallet::::bridge().is_congested); - }; - - report_bridge_status(true); - report_bridge_status(false); - }) - } -} diff --git a/system-parachains/asset-hubs/asset-hub-polkadot/tests/tests.rs b/system-parachains/asset-hubs/asset-hub-polkadot/tests/tests.rs index e5c53f39b2..66fc1fa067 100644 --- a/system-parachains/asset-hubs/asset-hub-polkadot/tests/tests.rs +++ b/system-parachains/asset-hubs/asset-hub-polkadot/tests/tests.rs @@ -544,12 +544,6 @@ asset_test_utils::include_teleports_for_native_asset_works!( _ => None, } }), - Box::new(|runtime_event_encoded: Vec| { - match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { - Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), - _ => None, - } - }), 1000 ); @@ -682,7 +676,7 @@ fn bridging_to_asset_hub_kusama() -> TestBridgingConfig { #[test] fn limited_reserve_transfer_assets_for_native_asset_to_asset_hub_kusama_works() { - missing_asset_test_utils_test_cases_over_bridge::limited_reserve_transfer_assets_for_native_asset_works::< + asset_test_utils::test_cases_over_bridge::limited_reserve_transfer_assets_for_native_asset_works::< Runtime, AllPalletsWithoutSystem, XcmConfig, @@ -708,7 +702,7 @@ fn limited_reserve_transfer_assets_for_native_asset_to_asset_hub_kusama_works() bridging_to_asset_hub_kusama, WeightLimit::Unlimited, Some(XcmBridgeHubRouterFeeAssetId::get()), - TreasuryAccount::get(), + Some(TreasuryAccount::get()), ) } #[test] @@ -737,7 +731,7 @@ fn receive_reserve_asset_deposited_roc_from_asset_hub_kusama_works() { } #[test] fn report_bridge_status_from_xcm_bridge_router_for_kusama_works() { - missing_asset_test_utils_test_cases_over_bridge::report_bridge_status_from_xcm_bridge_router_works::< + asset_test_utils::test_cases_over_bridge::report_bridge_status_from_xcm_bridge_router_works::< Runtime, AllPalletsWithoutSystem, XcmConfig, @@ -816,455 +810,3 @@ fn change_xcm_bridge_hub_router_byte_fee_by_governance_works() { }, ) } - -// missing stuff from asset_test_utils::test_cases_over_bridge -// TODO: replace me with direct usages of `asset_test_utils` after deps are bumped to (at least) 1.4 -mod missing_asset_test_utils_test_cases_over_bridge { - use asset_test_utils::test_cases_over_bridge::TestBridgingConfig; - use codec::Encode; - use cumulus_primitives_core::XcmpMessageSource; - use frame_support::{ - assert_ok, - traits::{Currency, Get, OnFinalize, OnInitialize, OriginTrait, ProcessMessageError}, - }; - use frame_system::pallet_prelude::BlockNumberFor; - use parachains_common::{AccountId, Balance}; - use parachains_runtimes_test_utils::{ - mock_open_hrmp_channel, AccountIdOf, BalanceOf, CollatorSessionKeys, ExtBuilder, - RuntimeHelper, ValidatorIdOf, XcmReceivedFrom, - }; - use sp_runtime::{traits::StaticLookup, Saturating}; - use xcm::{latest::prelude::*, VersionedMultiAssets}; - use xcm_builder::{CreateMatcher, MatchXcm}; - use xcm_executor::{ - traits::{ConvertLocation, TransactAsset}, - XcmExecutor, - }; - - /// Helper function to verify `xcm` contains all relevant instructions expected on destination - /// chain as part of a reserve-asset-transfer. - fn assert_matches_reserve_asset_deposited_instructions( - xcm: &mut Xcm, - expected_reserve_assets_deposited: &MultiAssets, - expected_beneficiary: &MultiLocation, - ) { - let _ = xcm - .0 - .matcher() - .skip_inst_while(|inst| !matches!(inst, ReserveAssetDeposited(..))) - .expect("no instruction ReserveAssetDeposited?") - .match_next_inst(|instr| match instr { - ReserveAssetDeposited(reserve_assets) => { - assert_eq!(reserve_assets, expected_reserve_assets_deposited); - Ok(()) - }, - _ => Err(ProcessMessageError::BadFormat), - }) - .expect("expected instruction ReserveAssetDeposited") - .match_next_inst(|instr| match instr { - ClearOrigin => Ok(()), - _ => Err(ProcessMessageError::BadFormat), - }) - .expect("expected instruction ClearOrigin") - .match_next_inst(|instr| match instr { - BuyExecution { .. } => Ok(()), - _ => Err(ProcessMessageError::BadFormat), - }) - .expect("expected instruction BuyExecution") - .match_next_inst(|instr| match instr { - DepositAsset { assets: _, beneficiary } if beneficiary == expected_beneficiary => - Ok(()), - _ => Err(ProcessMessageError::BadFormat), - }) - .expect("expected instruction DepositAsset"); - } - - pub fn limited_reserve_transfer_assets_for_native_asset_works< - Runtime, - AllPalletsWithoutSystem, - XcmConfig, - HrmpChannelOpener, - HrmpChannelSource, - LocationToAccountId, - >( - collator_session_keys: CollatorSessionKeys, - existential_deposit: BalanceOf, - alice_account: AccountIdOf, - unwrap_pallet_xcm_event: Box) -> Option>>, - unwrap_xcmp_queue_event: Box< - dyn Fn(Vec) -> Option>, - >, - prepare_configuration: fn() -> TestBridgingConfig, - weight_limit: WeightLimit, - maybe_paid_export_message: Option, - delivery_fees_account: Option>, - ) 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, - 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, - HrmpChannelOpener: frame_support::inherent::ProvideInherent< - Call = cumulus_pallet_parachain_system::Call, - >, - HrmpChannelSource: XcmpMessageSource, - { - let runtime_para_id = 1000; - ExtBuilder::::default() - .with_collators(collator_session_keys.collators()) - .with_session_keys(collator_session_keys.session_keys()) - .with_tracing() - .with_safe_xcm_version(3) - .with_para_id(runtime_para_id.into()) - .build() - .execute_with(|| { - let mut alice = [0u8; 32]; - alice[0] = 1; - let included_head = RuntimeHelper::::run_to_block( - 2, - AccountId::from(alice).into(), - ); - - // prepare bridge config - let TestBridgingConfig { - bridged_network, - local_bridge_hub_para_id, - bridged_target_location: target_location_from_different_consensus, - .. - } = prepare_configuration(); - - let reserve_account = LocationToAccountId::convert_location( - &target_location_from_different_consensus, - ) - .expect("Sovereign account for reserves"); - let balance_to_transfer = 1_000_000_000_000_u128; - let native_asset = MultiLocation::parent(); - - // open HRMP to bridge hub - mock_open_hrmp_channel::( - runtime_para_id.into(), - local_bridge_hub_para_id.into(), - included_head, - &alice, - ); - - // drip ED to account - let alice_account_init_balance = existential_deposit + balance_to_transfer.into(); - let _ = >::deposit_creating( - &alice_account, - alice_account_init_balance, - ); - // SA of target location needs to have at least ED, otherwise making reserve fails - let _ = >::deposit_creating( - &reserve_account, - existential_deposit, - ); - - // we just check here, that user retains enough balance after withdrawal - // and also we check if `balance_to_transfer` is more than `existential_deposit`, - assert!( - (>::free_balance(&alice_account) - - balance_to_transfer.into()) >= - existential_deposit - ); - // SA has just ED - assert_eq!( - >::free_balance(&reserve_account), - existential_deposit - ); - - let delivery_fees_account_balance_before = delivery_fees_account - .as_ref() - .map(|dfa| >::free_balance(dfa)) - .unwrap_or(0.into()); - - // local native asset (pallet_balances) - let asset_to_transfer = MultiAsset { - fun: Fungible(balance_to_transfer.into()), - id: Concrete(native_asset), - }; - - // destination is (some) account relative to the destination different consensus - let target_destination_account = MultiLocation { - parents: 0, - interior: X1(AccountId32 { - network: Some(bridged_network), - id: sp_runtime::AccountId32::new([3; 32]).into(), - }), - }; - - let assets_to_transfer = MultiAssets::from(asset_to_transfer); - let mut expected_assets = assets_to_transfer.clone(); - let context = XcmConfig::UniversalLocation::get(); - expected_assets - .reanchor(&target_location_from_different_consensus, context) - .unwrap(); - - let expected_beneficiary = target_destination_account; - - // Make sure sender has enough funds for paying delivery fees - let handling_delivery_fees = { - // Probable XCM with `ReserveAssetDeposited`. - let mut expected_reserve_asset_deposited_message = Xcm(vec![ - ReserveAssetDeposited(MultiAssets::from(expected_assets.clone())), - ClearOrigin, - BuyExecution { - fees: MultiAsset { - id: Concrete(Default::default()), - fun: Fungible(balance_to_transfer), - }, - weight_limit: Unlimited, - }, - DepositAsset { - assets: Wild(AllCounted(1)), - beneficiary: expected_beneficiary, - }, - SetTopic([ - 220, 188, 144, 32, 213, 83, 111, 175, 44, 210, 111, 19, 90, 165, 191, - 112, 140, 247, 192, 124, 42, 17, 153, 141, 114, 34, 189, 20, 83, 69, - 237, 173, - ]), - ]); - assert_matches_reserve_asset_deposited_instructions( - &mut expected_reserve_asset_deposited_message, - &expected_assets, - &expected_beneficiary, - ); - - // Call `SendXcm::validate` to get delivery fees. - let (_, delivery_fees): (_, MultiAssets) = XcmConfig::XcmSender::validate( - &mut Some(target_location_from_different_consensus), - &mut Some(expected_reserve_asset_deposited_message), - ) - .expect("validate passes"); - // Drip delivery fee to Alice account. - let mut delivery_fees_added = false; - for delivery_fee in delivery_fees.inner() { - assert_ok!(::deposit_asset( - &delivery_fee, - &MultiLocation { - parents: 0, - interior: X1(AccountId32 { - network: None, - id: alice_account.clone().into(), - }), - }, - None, - )); - delivery_fees_added = true; - } - delivery_fees_added - }; - - // do pallet_xcm call reserve transfer - assert_ok!(>::limited_reserve_transfer_assets( - RuntimeHelper::::origin_of( - alice_account.clone() - ), - Box::new(target_location_from_different_consensus.into_versioned()), - Box::new(target_destination_account.into_versioned()), - Box::new(VersionedMultiAssets::from(assets_to_transfer)), - 0, - weight_limit, - )); - - // check events - // check pallet_xcm attempted - RuntimeHelper::::assert_pallet_xcm_event_outcome( - &unwrap_pallet_xcm_event, - |outcome| { - assert_ok!(outcome.ensure_complete()); - }, - ); - - // check that xcm was sent - let xcm_sent_message_hash = >::events() - .into_iter() - .filter_map(|e| unwrap_xcmp_queue_event(e.event.encode())) - .find_map(|e| match e { - cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { message_hash } => - Some(message_hash), - _ => None, - }); - - // read xcm - let xcm_sent = - RuntimeHelper::::take_xcm( - local_bridge_hub_para_id.into(), - ) - .unwrap(); - assert_eq!( - xcm_sent_message_hash, - Some(xcm_sent.using_encoded(sp_io::hashing::blake2_256)) - ); - let mut xcm_sent: Xcm<()> = xcm_sent.try_into().expect("versioned xcm"); - - // check sent XCM ExportMessage to BridgeHub - - // 1. check paid or unpaid - if let Some(expected_fee_asset_id) = maybe_paid_export_message { - xcm_sent - .0 - .matcher() - .match_next_inst(|instr| match instr { - WithdrawAsset(_) => Ok(()), - _ => Err(ProcessMessageError::BadFormat), - }) - .expect("contains WithdrawAsset") - .match_next_inst(|instr| match instr { - BuyExecution { fees, .. } if fees.id.eq(&expected_fee_asset_id) => - Ok(()), - _ => Err(ProcessMessageError::BadFormat), - }) - .expect("contains BuyExecution") - } else { - xcm_sent - .0 - .matcher() - .match_next_inst(|instr| match instr { - // first instruction could be UnpaidExecution (because we could have - // explicit unpaid execution on BridgeHub) - UnpaidExecution { weight_limit, check_origin } - if weight_limit == &Unlimited && check_origin.is_none() => - Ok(()), - _ => Err(ProcessMessageError::BadFormat), - }) - .expect("contains UnpaidExecution") - } - // 2. check ExportMessage - .match_next_inst(|instr| match instr { - // next instruction is ExportMessage - ExportMessage { network, destination, xcm: inner_xcm } => { - assert_eq!(network, &bridged_network); - let (_, target_location_junctions_without_global_consensus) = - target_location_from_different_consensus - .interior - .split_global() - .expect("split works"); - assert_eq!( - destination, - &target_location_junctions_without_global_consensus - ); - assert_matches_reserve_asset_deposited_instructions( - inner_xcm, - &expected_assets, - &expected_beneficiary, - ); - Ok(()) - }, - _ => Err(ProcessMessageError::BadFormat), - }) - .expect("contains ExportMessage"); - - // check alice account decreased by balance_to_transfer - assert_eq!( - >::free_balance(&alice_account), - alice_account_init_balance - .saturating_sub(existential_deposit) - .saturating_sub(balance_to_transfer.into()) - ); - - // check reserve account increased by balance_to_transfer - assert_eq!( - >::free_balance(&reserve_account), - existential_deposit + balance_to_transfer.into() - ); - - // check dedicated account increased by delivery fees (if configured) - if handling_delivery_fees { - if let Some(delivery_fees_account) = delivery_fees_account { - let delivery_fees_account_balance_after = - >::free_balance( - &delivery_fees_account, - ); - assert!( - delivery_fees_account_balance_after > - delivery_fees_account_balance_before - ); - } - } - }) - } - - pub fn report_bridge_status_from_xcm_bridge_router_works< - Runtime, - AllPalletsWithoutSystem, - XcmConfig, - LocationToAccountId, - XcmBridgeHubRouterInstance, - >( - collator_session_keys: CollatorSessionKeys, - prepare_configuration: fn() -> TestBridgingConfig, - congested_message: fn() -> Xcm, - uncongested_message: fn() -> 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, - 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 report_bridge_status = |is_congested: bool| { - // prepare bridge config - let TestBridgingConfig { local_bridge_hub_location, .. } = prepare_configuration(); - - // Call received XCM execution - let xcm = if is_congested { congested_message() } else { uncongested_message() }; - let hash = xcm.using_encoded(sp_io::hashing::blake2_256); - - // execute xcm as XcmpQueue would do - let outcome = XcmExecutor::::execute_xcm( - local_bridge_hub_location, - xcm, - hash, - RuntimeHelper::::xcm_max_weight(XcmReceivedFrom::Sibling), - ); - assert_eq!(outcome.ensure_complete(), Ok(())); - assert_eq!(is_congested, pallet_xcm_bridge_hub_router::Pallet::::bridge().is_congested); - }; - - report_bridge_status(true); - report_bridge_status(false); - }) - } -} diff --git a/system-parachains/bridge-hubs/bridge-hub-kusama/Cargo.toml b/system-parachains/bridge-hubs/bridge-hub-kusama/Cargo.toml index 267fa95e83..349c773efc 100644 --- a/system-parachains/bridge-hubs/bridge-hub-kusama/Cargo.toml +++ b/system-parachains/bridge-hubs/bridge-hub-kusama/Cargo.toml @@ -25,6 +25,7 @@ bp-asset-hub-polkadot = { path = "../../asset-hubs/asset-hub-polkadot/primitives bp-bridge-hub-kusama = { path = "./primitives", default-features = false} bp-bridge-hub-polkadot = { path = "../bridge-hub-polkadot/primitives", default-features = false} kusama-runtime-constants = { path = "../../../relay/kusama/constants", default-features = false} +polkadot-runtime-constants = { path = "../../../relay/polkadot/constants", default-features = false} # Substrate frame-benchmarking = { default-features = false, optional = true, version = "27.0.0" } @@ -97,6 +98,7 @@ pallet-bridge-grandpa = { default-features = false , version = "0.6.0" } pallet-bridge-messages = { default-features = false , version = "0.6.0" } pallet-bridge-parachains = { default-features = false , version = "0.6.0" } pallet-bridge-relayers = { default-features = false , version = "0.6.0" } +pallet-xcm-bridge-hub = { default-features = false , version = "0.1.0" } [dev-dependencies] bridge-hub-test-utils = { version = "0.6.0" } @@ -154,12 +156,14 @@ std = [ "pallet-transaction-payment/std", "pallet-utility/std", "pallet-xcm-benchmarks?/std", + "pallet-xcm-bridge-hub/std", "pallet-xcm/std", "parachain-info/std", "parachains-common/std", "polkadot-core-primitives/std", "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", + "polkadot-runtime-constants/std", "scale-info/std", "serde", "sp-api/std", @@ -205,6 +209,7 @@ runtime-benchmarks = [ "pallet-timestamp/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", + "pallet-xcm-bridge-hub/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", @@ -238,6 +243,7 @@ try-runtime = [ "pallet-timestamp/try-runtime", "pallet-transaction-payment/try-runtime", "pallet-utility/try-runtime", + "pallet-xcm-bridge-hub/try-runtime", "pallet-xcm/try-runtime", "parachain-info/try-runtime", "polkadot-runtime-common/try-runtime", diff --git a/system-parachains/bridge-hubs/bridge-hub-kusama/src/bridge_to_polkadot_config.rs b/system-parachains/bridge-hubs/bridge-hub-kusama/src/bridge_to_polkadot_config.rs index 7379898b60..90642c0fee 100644 --- a/system-parachains/bridge-hubs/bridge-hub-kusama/src/bridge_to_polkadot_config.rs +++ b/system-parachains/bridge-hubs/bridge-hub-kusama/src/bridge_to_polkadot_config.rs @@ -20,7 +20,7 @@ use crate::{ weights, xcm_config::{UniversalLocation, XcmRouter}, AccountId, Balance, Balances, BlockNumber, BridgePolkadotMessages, Runtime, RuntimeEvent, - RuntimeOrigin, + RuntimeOrigin, XcmOverBridgeHubPolkadot, }; use bp_messages::LaneId; use bp_parachains::SingleParaStoredHeaderDataBuilder; @@ -48,7 +48,7 @@ use xcm::{ latest::prelude::*, prelude::{InteriorMultiLocation, NetworkId}, }; -use xcm_builder::{BridgeBlobDispatcher, HaulBlobExporter}; +use xcm_builder::BridgeBlobDispatcher; /// Lane identifier, used to connect Kusama Asset Hub and Polkadot Asset Hub. pub const XCM_LANE_FOR_ASSET_HUB_KUSAMA_TO_ASSET_HUB_POLKADOT: LaneId = LaneId([0, 0, 0, 1]); @@ -82,12 +82,30 @@ parameter_types! { /// Identifier of the sibling Kusama Asset Hub parachain. pub AssetHubKusamaParaId: cumulus_primitives_core::ParaId = kusama_runtime_constants::system_parachain::ASSET_HUB_ID.into(); + /// Identifier of the bridged Polkadot Asset Hub parachain. + pub AssetHubPolkadotParaId: cumulus_primitives_core::ParaId = polkadot_runtime_constants::system_parachain::ASSET_HUB_ID.into(); + /// A route (XCM location and bridge lane) that the Kusama Asset Hub -> Polkadot Asset Hub /// message is following. pub FromAssetHubKusamaToAssetHubPolkadotRoute: SenderAndLane = SenderAndLane::new( ParentThen(X1(Parachain(AssetHubKusamaParaId::get().into()))).into(), XCM_LANE_FOR_ASSET_HUB_KUSAMA_TO_ASSET_HUB_POLKADOT, ); + + /// Lane identifier, used to connect Kusama Asset Hub and Polkadot Asset Hub. + pub const AssetHubKusamaToAssetHubPolkadotMessagesLane: bp_messages::LaneId + = XCM_LANE_FOR_ASSET_HUB_KUSAMA_TO_ASSET_HUB_POLKADOT; + /// All active lanes that the current bridge supports. + pub ActiveOutboundLanesToBridgeHubPolkadot: &'static [bp_messages::LaneId] + = &[XCM_LANE_FOR_ASSET_HUB_KUSAMA_TO_ASSET_HUB_POLKADOT]; + + /// Lanes + pub ActiveLanes: sp_std::vec::Vec<(SenderAndLane, (NetworkId, InteriorMultiLocation))> = sp_std::vec![ + ( + FromAssetHubKusamaToAssetHubPolkadotRoute::get(), + (PolkadotGlobalConsensusNetwork::get(), X1(Parachain(AssetHubPolkadotParaId::get().into()))) + ) + ]; } // Parameters, used by bridge transport code. @@ -120,13 +138,6 @@ parameter_types! { pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce = bp_bridge_hub_polkadot::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; - /// Lane identifier, used to connect Kusama Asset Hub and Polkadot Asset Hub. - pub const AssetHubKusamaToAssetHubPolkadotMessagesLane: bp_messages::LaneId - = XCM_LANE_FOR_ASSET_HUB_KUSAMA_TO_ASSET_HUB_POLKADOT; - /// All active lanes that the current bridge supports. - pub ActiveOutboundLanesToBridgeHubPolkadot: &'static [bp_messages::LaneId] - = &[XCM_LANE_FOR_ASSET_HUB_KUSAMA_TO_ASSET_HUB_POLKADOT]; - /// Reserve identifier, used by the `pallet_bridge_relayers` to hold funds of registered relayer. pub const RelayerStakeReserveId: [u8; 8] = *b"brdgrlrs"; /// Minimal period of relayer registration. Roughly, it is the 1 hour of real time. @@ -231,24 +242,30 @@ type FromPolkadotMessageBlobDispatcher = BridgeBlobDispatcher< >; /// Export XCM messages to be relayed to the other side -pub type ToBridgeHubPolkadotHaulBlobExporter = HaulBlobExporter< - XcmBlobHaulerAdapter, - PolkadotGlobalConsensusNetwork, - (), ->; +pub type ToBridgeHubPolkadotHaulBlobExporter = XcmOverBridgeHubPolkadot; pub struct ToBridgeHubPolkadotXcmBlobHauler; impl XcmBlobHauler for ToBridgeHubPolkadotXcmBlobHauler { type Runtime = Runtime; type MessagesInstance = WithBridgeHubPolkadotMessagesInstance; - type SenderAndLane = FromAssetHubKusamaToAssetHubPolkadotRoute; type ToSourceChainSender = XcmRouter; type CongestedMessage = bp_asset_hub_kusama::CongestedMessage; type UncongestedMessage = bp_asset_hub_kusama::UncongestedMessage; } +/// Add support for the export and dispatch of XCM programs. +pub type XcmOverBridgeHubPolkadotInstance = pallet_xcm_bridge_hub::Instance1; +impl pallet_xcm_bridge_hub::Config for Runtime { + type UniversalLocation = UniversalLocation; + type BridgedNetworkId = PolkadotGlobalConsensusNetwork; + type BridgeMessagesPalletInstance = WithBridgeHubPolkadotMessagesInstance; + type MessageExportPrice = (); + type Lanes = ActiveLanes; + type LanesSupport = ToBridgeHubPolkadotXcmBlobHauler; +} + /// On messages delivered callback. -type OnMessagesDeliveredFromPolkadot = XcmBlobHaulerAdapter; +type OnMessagesDeliveredFromPolkadot = XcmBlobHaulerAdapter; /// Messaging Bridge configuration for BridgeHubKusama -> BridgeHubPolkadot pub struct WithBridgeHubPolkadotMessageBridge; diff --git a/system-parachains/bridge-hubs/bridge-hub-kusama/src/lib.rs b/system-parachains/bridge-hubs/bridge-hub-kusama/src/lib.rs index 27ce847709..4cd803ff14 100644 --- a/system-parachains/bridge-hubs/bridge-hub-kusama/src/lib.rs +++ b/system-parachains/bridge-hubs/bridge-hub-kusama/src/lib.rs @@ -506,6 +506,7 @@ construct_runtime!( BridgePolkadotGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage, Event, Config} = 51, BridgePolkadotParachains: pallet_bridge_parachains::::{Pallet, Call, Storage, Event} = 52, BridgePolkadotMessages: pallet_bridge_messages::::{Pallet, Call, Storage, Event, Config} = 53, + XcmOverBridgeHubPolkadot: pallet_xcm_bridge_hub::::{Pallet} = 54, } ); @@ -938,7 +939,13 @@ impl_runtime_apis! { fn export_message_origin_and_destination( ) -> Result<(MultiLocation, NetworkId, InteriorMultiLocation), BenchmarkError> { - Ok((KsmRelayLocation::get(), NetworkId::Polkadot, X1(Parachain(1000)))) + Ok( + ( + bridge_to_polkadot_config::FromAssetHubKusamaToAssetHubPolkadotRoute::get().location, + NetworkId::Polkadot, + X1(Parachain(bridge_to_polkadot_config::AssetHubPolkadotParaId::get().into())) + ) + ) } fn alias_origin() -> Result<(MultiLocation, MultiLocation), BenchmarkError> { diff --git a/system-parachains/bridge-hubs/bridge-hub-kusama/tests/tests.rs b/system-parachains/bridge-hubs/bridge-hub-kusama/tests/tests.rs index 54a4192851..c015e573a3 100644 --- a/system-parachains/bridge-hubs/bridge-hub-kusama/tests/tests.rs +++ b/system-parachains/bridge-hubs/bridge-hub-kusama/tests/tests.rs @@ -17,7 +17,7 @@ use bp_polkadot_core::Signature; use bridge_hub_kusama_runtime::{ bridge_to_polkadot_config::{ - BridgeGrandpaPolkadotInstance, BridgeHubPolkadotChainId, BridgeParachainPolkadotInstance, + AssetHubPolkadotParaId, BridgeGrandpaPolkadotInstance, BridgeHubPolkadotChainId, BridgeParachainPolkadotInstance, DeliveryRewardInBalance, PolkadotGlobalConsensusNetwork, RefundBridgeHubPolkadotMessages, RequiredStakeForStakeAndSlash, WithBridgeHubPolkadotMessageBridge, WithBridgeHubPolkadotMessagesInstance, XCM_LANE_FOR_ASSET_HUB_KUSAMA_TO_ASSET_HUB_POLKADOT, @@ -114,12 +114,6 @@ bridge_hub_test_utils::test_cases::include_teleports_for_native_asset_works!( _ => None, } }), - Box::new(|runtime_event_encoded: Vec| { - match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { - Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), - _ => None, - } - }), 1002 ); @@ -181,7 +175,7 @@ fn handle_export_message_from_system_parachain_add_to_outbound_queue_works() { _ => None, } }), - || ExportMessage { network: Polkadot, destination: X1(Parachain(4321)), xcm: Xcm(vec![]) }, + || ExportMessage { network: Polkadot, destination: X1(Parachain(AssetHubPolkadotParaId::get().into())), xcm: Xcm(vec![]) }, XCM_LANE_FOR_ASSET_HUB_KUSAMA_TO_ASSET_HUB_POLKADOT, Some((KsmRelayLocation::get(), ExistentialDeposit::get()).into()), // value should be >= than value generated by `can_calculate_weight_for_paid_export_message_with_reserve_transfer` diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/Cargo.toml b/system-parachains/bridge-hubs/bridge-hub-polkadot/Cargo.toml index 6164e584fe..aed1076cca 100644 --- a/system-parachains/bridge-hubs/bridge-hub-polkadot/Cargo.toml +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/Cargo.toml @@ -24,6 +24,7 @@ bp-asset-hub-kusama = { path = "../../asset-hubs/asset-hub-kusama/primitives", d bp-asset-hub-polkadot = { path = "../../asset-hubs/asset-hub-polkadot/primitives", default-features = false} bp-bridge-hub-kusama = { path = "../bridge-hub-kusama/primitives", default-features = false} bp-bridge-hub-polkadot = { path = "../bridge-hub-polkadot/primitives", default-features = false} +kusama-runtime-constants = { path = "../../../relay/kusama/constants", default-features = false} polkadot-runtime-constants = { path = "../../../relay/polkadot/constants", default-features = false} # Substrate @@ -97,6 +98,7 @@ pallet-bridge-grandpa = { default-features = false , version = "0.6.0" } pallet-bridge-messages = { default-features = false , version = "0.6.0" } pallet-bridge-parachains = { default-features = false , version = "0.6.0" } pallet-bridge-relayers = { default-features = false , version = "0.6.0" } +pallet-xcm-bridge-hub = { default-features = false , version = "0.1.0" } [dev-dependencies] bridge-hub-test-utils = { version = "0.6.0" } @@ -137,6 +139,7 @@ std = [ "frame-system/std", "frame-try-runtime?/std", "log/std", + "kusama-runtime-constants/std", "pallet-aura/std", "pallet-authorship/std", "pallet-balances/std", @@ -153,6 +156,7 @@ std = [ "pallet-transaction-payment/std", "pallet-utility/std", "pallet-xcm-benchmarks?/std", + "pallet-xcm-bridge-hub/std", "pallet-xcm/std", "parachain-info/std", "parachains-common/std", @@ -205,6 +209,7 @@ runtime-benchmarks = [ "pallet-timestamp/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-xcm-benchmarks/runtime-benchmarks", + "pallet-xcm-bridge-hub/runtime-benchmarks", "pallet-xcm/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", @@ -237,6 +242,7 @@ try-runtime = [ "pallet-timestamp/try-runtime", "pallet-transaction-payment/try-runtime", "pallet-utility/try-runtime", + "pallet-xcm-bridge-hub/try-runtime", "pallet-xcm/try-runtime", "parachain-info/try-runtime", "polkadot-runtime-common/try-runtime", diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/src/bridge_to_kusama_config.rs b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/bridge_to_kusama_config.rs index 4911e3444f..9a592445e4 100644 --- a/system-parachains/bridge-hubs/bridge-hub-polkadot/src/bridge_to_kusama_config.rs +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/bridge_to_kusama_config.rs @@ -20,7 +20,7 @@ use crate::{ weights, xcm_config::{UniversalLocation, XcmRouter}, AccountId, Balance, Balances, BlockNumber, BridgeKusamaMessages, Runtime, RuntimeEvent, - RuntimeOrigin, + RuntimeOrigin, XcmOverBridgeHubKusama, }; use bp_messages::LaneId; use bp_parachains::SingleParaStoredHeaderDataBuilder; @@ -48,7 +48,7 @@ use xcm::{ latest::prelude::*, prelude::{InteriorMultiLocation, NetworkId}, }; -use xcm_builder::{BridgeBlobDispatcher, HaulBlobExporter}; +use xcm_builder::BridgeBlobDispatcher; /// Lane identifier, used to connect Polkadot Asset Hub and Kusama Asset Hub. pub const XCM_LANE_FOR_ASSET_HUB_POLKADOT_TO_ASSET_HUB_KUSAMA: LaneId = LaneId([0, 0, 0, 1]); @@ -82,12 +82,30 @@ parameter_types! { /// Identifier of the sibling Polkadot Asset Hub parachain. pub AssetHubPolkadotParaId: cumulus_primitives_core::ParaId = polkadot_runtime_constants::system_parachain::ASSET_HUB_ID.into(); + /// Identifier of the bridged Kusama Asset Hub parachain. + pub AssetHubKusamaParaId: cumulus_primitives_core::ParaId = kusama_runtime_constants::system_parachain::ASSET_HUB_ID.into(); + /// A route (XCM location and bridge lane) that the Polkadot Asset Hub -> Kusama Asset Hub /// message is following. pub FromAssetHubPolkadotToAssetHubKusamaRoute: SenderAndLane = SenderAndLane::new( ParentThen(X1(Parachain(AssetHubPolkadotParaId::get().into()))).into(), XCM_LANE_FOR_ASSET_HUB_POLKADOT_TO_ASSET_HUB_KUSAMA, ); + + /// Lane identifier, used to connect Polkadot Asset Hub and Kusama Asset Hub. + pub const AssetHubPolkadotToAssetHubKusamaMessagesLane: bp_messages::LaneId + = XCM_LANE_FOR_ASSET_HUB_POLKADOT_TO_ASSET_HUB_KUSAMA; + /// All active lanes that the current bridge supports. + pub ActiveOutboundLanesToBridgeHubKusama: &'static [bp_messages::LaneId] + = &[XCM_LANE_FOR_ASSET_HUB_POLKADOT_TO_ASSET_HUB_KUSAMA]; + + /// Lanes + pub ActiveLanes: sp_std::vec::Vec<(SenderAndLane, (NetworkId, InteriorMultiLocation))> = sp_std::vec![ + ( + FromAssetHubPolkadotToAssetHubKusamaRoute::get(), + (KusamaGlobalConsensusNetwork::get(), X1(Parachain(AssetHubKusamaParaId::get().into()))) + ) + ]; } // Parameters, used by bridge transport code. @@ -120,13 +138,6 @@ parameter_types! { pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce = bp_bridge_hub_kusama::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; - /// Lane identifier, used to connect Polkadot Asset Hub and Kusama Asset Hub. - pub const AssetHubPolkadotToAssetHubKusamaMessagesLane: bp_messages::LaneId - = XCM_LANE_FOR_ASSET_HUB_POLKADOT_TO_ASSET_HUB_KUSAMA; - /// All active lanes that the current bridge supports. - pub ActiveOutboundLanesToBridgeHubKusama: &'static [bp_messages::LaneId] - = &[XCM_LANE_FOR_ASSET_HUB_POLKADOT_TO_ASSET_HUB_KUSAMA]; - /// Reserve identifier, used by the `pallet_bridge_relayers` to hold funds of registered relayer. pub const RelayerStakeReserveId: [u8; 8] = *b"brdgrlrs"; /// Minimal period of relayer registration. Roughly, it is the 1 hour of real time. @@ -231,24 +242,30 @@ type FromKusamaMessageBlobDispatcher = BridgeBlobDispatcher< >; /// Export XCM messages to be relayed to the other side -pub type ToBridgeHubKusamaHaulBlobExporter = HaulBlobExporter< - XcmBlobHaulerAdapter, - KusamaGlobalConsensusNetwork, - (), ->; +pub type ToBridgeHubKusamaHaulBlobExporter = XcmOverBridgeHubKusama; pub struct ToBridgeHubKusamaXcmBlobHauler; impl XcmBlobHauler for ToBridgeHubKusamaXcmBlobHauler { type Runtime = Runtime; type MessagesInstance = WithBridgeHubKusamaMessagesInstance; - type SenderAndLane = FromAssetHubPolkadotToAssetHubKusamaRoute; type ToSourceChainSender = XcmRouter; type CongestedMessage = bp_asset_hub_polkadot::CongestedMessage; type UncongestedMessage = bp_asset_hub_polkadot::UncongestedMessage; } +/// Add support for the export and dispatch of XCM programs. +pub type XcmOverBridgeHubKusamaInstance = pallet_xcm_bridge_hub::Instance1; +impl pallet_xcm_bridge_hub::Config for Runtime { + type UniversalLocation = UniversalLocation; + type BridgedNetworkId = KusamaGlobalConsensusNetwork; + type BridgeMessagesPalletInstance = WithBridgeHubKusamaMessagesInstance; + type MessageExportPrice = (); + type Lanes = ActiveLanes; + type LanesSupport = ToBridgeHubKusamaXcmBlobHauler; +} + /// On messages delivered callback. -type OnMessagesDeliveredFromKusama = XcmBlobHaulerAdapter; +type OnMessagesDeliveredFromKusama = XcmBlobHaulerAdapter; /// Messaging Bridge configuration for BridgeHubPolkadot -> BridgeHubKusama pub struct WithBridgeHubKusamaMessageBridge; diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/src/lib.rs b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/lib.rs index 04e45e874d..dc2c0b2da2 100644 --- a/system-parachains/bridge-hubs/bridge-hub-polkadot/src/lib.rs +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/src/lib.rs @@ -506,6 +506,7 @@ construct_runtime!( BridgeKusamaGrandpa: pallet_bridge_grandpa::::{Pallet, Call, Storage, Event, Config} = 51, BridgeKusamaParachains: pallet_bridge_parachains::::{Pallet, Call, Storage, Event} = 52, BridgeKusamaMessages: pallet_bridge_messages::::{Pallet, Call, Storage, Event, Config} = 53, + XcmOverBridgeHubKusama: pallet_xcm_bridge_hub::::{Pallet} = 54, } ); @@ -938,7 +939,13 @@ impl_runtime_apis! { fn export_message_origin_and_destination( ) -> Result<(MultiLocation, NetworkId, InteriorMultiLocation), BenchmarkError> { - Ok((DotRelayLocation::get(), NetworkId::Kusama, X1(Parachain(1000)))) + Ok( + ( + bridge_to_kusama_config::FromAssetHubPolkadotToAssetHubKusamaRoute::get().location, + NetworkId::Kusama, + X1(Parachain(bridge_to_kusama_config::AssetHubKusamaParaId::get().into())) + ) + ) } fn alias_origin() -> Result<(MultiLocation, MultiLocation), BenchmarkError> { diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/tests/tests.rs b/system-parachains/bridge-hubs/bridge-hub-polkadot/tests/tests.rs index b5682ec790..aee5508371 100644 --- a/system-parachains/bridge-hubs/bridge-hub-polkadot/tests/tests.rs +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/tests/tests.rs @@ -17,7 +17,7 @@ use bp_polkadot_core::Signature; use bridge_hub_polkadot_runtime::{ bridge_to_kusama_config::{ - BridgeGrandpaKusamaInstance, BridgeHubKusamaChainId, BridgeParachainKusamaInstance, + AssetHubKusamaParaId, BridgeGrandpaKusamaInstance, BridgeHubKusamaChainId, BridgeParachainKusamaInstance, DeliveryRewardInBalance, KusamaGlobalConsensusNetwork, RefundBridgeHubKusamaMessages, RequiredStakeForStakeAndSlash, WithBridgeHubKusamaMessageBridge, WithBridgeHubKusamaMessagesInstance, XCM_LANE_FOR_ASSET_HUB_POLKADOT_TO_ASSET_HUB_KUSAMA, @@ -114,12 +114,6 @@ bridge_hub_test_utils::test_cases::include_teleports_for_native_asset_works!( _ => None, } }), - Box::new(|runtime_event_encoded: Vec| { - match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) { - Ok(RuntimeEvent::XcmpQueue(event)) => Some(event), - _ => None, - } - }), 1002 ); @@ -181,7 +175,7 @@ fn handle_export_message_from_system_parachain_add_to_outbound_queue_works() { _ => None, } }), - || ExportMessage { network: Kusama, destination: X1(Parachain(4321)), xcm: Xcm(vec![]) }, + || ExportMessage { network: Kusama, destination: X1(Parachain(AssetHubKusamaParaId::get().into())), xcm: Xcm(vec![]) }, XCM_LANE_FOR_ASSET_HUB_POLKADOT_TO_ASSET_HUB_KUSAMA, Some((DotRelayLocation::get(), ExistentialDeposit::get()).into()), // value should be >= than value generated by `can_calculate_weight_for_paid_export_message_with_reserve_transfer`