diff --git a/.github/workflows/command-backport.yml b/.github/workflows/command-backport.yml index d7b01000855ad..67de5418434f9 100644 --- a/.github/workflows/command-backport.yml +++ b/.github/workflows/command-backport.yml @@ -31,10 +31,10 @@ jobs: - name: Generate token id: generate_token - uses: tibdex/github-app-token@v2.1.0 + uses: actions/create-github-app-token@v1 with: - app_id: ${{ secrets.CMD_BOT_APP_ID }} - private_key: ${{ secrets.CMD_BOT_APP_KEY }} + app_id: ${{ secrets.RELEASE_BACKPORT_AUTOMATION_APP_ID }} + private_key: ${{ secrets.RELEASE_BACKPORT_AUTOMATION_APP_PRIVATE_KEY }} - name: Create backport pull requests uses: korthout/backport-action@v3 diff --git a/Cargo.lock b/Cargo.lock index 2b0448a3baeed..1fee693385ecb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1009,6 +1009,7 @@ dependencies = [ "sp-core 28.0.0", "sp-genesis-builder", "sp-inherents", + "sp-io 30.0.0", "sp-keyring", "sp-offchain", "sp-runtime 31.0.1", @@ -1149,6 +1150,7 @@ dependencies = [ "sp-core 28.0.0", "sp-genesis-builder", "sp-inherents", + "sp-io 30.0.0", "sp-keyring", "sp-offchain", "sp-runtime 31.0.1", @@ -2374,6 +2376,7 @@ dependencies = [ "sp-core 28.0.0", "sp-runtime 31.0.1", "staging-xcm", + "staging-xcm-builder", "staging-xcm-executor", "testnet-parachains-constants", "xcm-runtime-apis", @@ -2569,6 +2572,7 @@ dependencies = [ "sp-core 28.0.0", "sp-runtime 31.0.1", "staging-xcm", + "staging-xcm-builder", "staging-xcm-executor", "testnet-parachains-constants", "xcm-runtime-apis", @@ -21752,6 +21756,7 @@ dependencies = [ "sp-runtime 31.0.1", "sp-std 14.0.0", "staging-xcm", + "staging-xcm-builder", "staging-xcm-executor", ] diff --git a/bridges/snowbridge/primitives/router/Cargo.toml b/bridges/snowbridge/primitives/router/Cargo.toml index e44cca077ef32..44e8cbdfc30c2 100644 --- a/bridges/snowbridge/primitives/router/Cargo.toml +++ b/bridges/snowbridge/primitives/router/Cargo.toml @@ -23,6 +23,7 @@ sp-runtime = { workspace = true } sp-std = { workspace = true } xcm = { workspace = true } +xcm-builder = { workspace = true } xcm-executor = { workspace = true } snowbridge-core = { workspace = true } @@ -43,6 +44,7 @@ std = [ "sp-io/std", "sp-runtime/std", "sp-std/std", + "xcm-builder/std", "xcm-executor/std", "xcm/std", ] @@ -50,6 +52,7 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "snowbridge-core/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", "xcm/runtime-benchmarks", ] diff --git a/bridges/snowbridge/primitives/router/src/inbound/mod.rs b/bridges/snowbridge/primitives/router/src/inbound/mod.rs index f2d5b02e8bbdf..98a563f1894ee 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/mod.rs @@ -466,6 +466,7 @@ where } } +/// DEPRECATED in favor of [xcm_builder::ExternalConsensusLocationsConverterFor] pub struct EthereumLocationsConverterFor(PhantomData); impl ConvertLocation for EthereumLocationsConverterFor where diff --git a/bridges/snowbridge/primitives/router/src/inbound/tests.rs b/bridges/snowbridge/primitives/router/src/inbound/tests.rs index 11d7928602c6e..1d8ac7393cdd9 100644 --- a/bridges/snowbridge/primitives/router/src/inbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/inbound/tests.rs @@ -2,11 +2,16 @@ use super::EthereumLocationsConverterFor; use crate::inbound::{ mock::*, Command, ConvertMessage, Destination, MessageV1, VersionedMessage, H160, }; -use frame_support::assert_ok; +use frame_support::{assert_ok, parameter_types}; use hex_literal::hex; use xcm::prelude::*; +use xcm_builder::ExternalConsensusLocationsConverterFor; use xcm_executor::traits::ConvertLocation; +parameter_types! { + pub UniversalLocation: InteriorLocation = [GlobalConsensus(ByGenesis([9; 32])), Parachain(1234)].into(); +} + #[test] fn test_ethereum_network_converts_successfully() { let expected_account: [u8; 32] = @@ -15,7 +20,12 @@ fn test_ethereum_network_converts_successfully() { let account = EthereumLocationsConverterFor::<[u8; 32]>::convert_location(&contract_location).unwrap(); - + assert_eq!(account, expected_account); + let account = + ExternalConsensusLocationsConverterFor::::convert_location( + &contract_location, + ) + .unwrap(); assert_eq!(account, expected_account); } @@ -30,7 +40,12 @@ fn test_contract_location_with_network_converts_successfully() { let account = EthereumLocationsConverterFor::<[u8; 32]>::convert_location(&contract_location).unwrap(); - + assert_eq!(account, expected_account); + let account = + ExternalConsensusLocationsConverterFor::::convert_location( + &contract_location, + ) + .unwrap(); assert_eq!(account, expected_account); } 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 35ceffe4c6953..4b229fd311b4a 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 @@ -27,6 +27,7 @@ sp-runtime = { workspace = true } # Polkadot pallet-xcm = { workspace = true } xcm = { workspace = true } +xcm-builder = { workspace = true } xcm-executor = { workspace = true } xcm-runtime-apis = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs index f84d42cb29f8e..f89880b284bcc 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/lib.rs @@ -25,6 +25,7 @@ mod imports { latest::{ParentThen, ROCOCO_GENESIS_HASH, WESTEND_GENESIS_HASH}, prelude::{AccountId32 as AccountId32Junction, *}, }; + pub use xcm_builder::ExternalConsensusLocationsConverterFor; pub use xcm_executor::traits::TransferType; // Cumulus diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs index 7f242bab5a9da..9acdf20e2a955 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs @@ -13,6 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. use crate::imports::*; +use ahr_xcm_config::UniversalLocation as AssetHubRococoUniversalLocation; use codec::{Decode, Encode}; use emulated_integration_tests_common::xcm_emulator::ConvertLocation; use frame_support::pallet_prelude::TypeInfo; @@ -24,9 +25,7 @@ use snowbridge_pallet_inbound_queue_fixtures::{ send_token::make_send_token_message, send_token_to_penpal::make_send_token_to_penpal_message, }; use snowbridge_pallet_system; -use snowbridge_router_primitives::inbound::{ - Command, Destination, EthereumLocationsConverterFor, MessageV1, VersionedMessage, -}; +use snowbridge_router_primitives::inbound::{Command, Destination, MessageV1, VersionedMessage}; use sp_core::H256; use sp_runtime::{DispatchError::Token, TokenError::FundsUnavailable}; use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; @@ -319,8 +318,13 @@ fn send_weth_from_ethereum_to_penpal() { let origin_location = (Parent, Parent, ethereum_network_v5).into(); // Fund ethereum sovereign on AssetHub - let ethereum_sovereign: AccountId = - EthereumLocationsConverterFor::::convert_location(&origin_location).unwrap(); + let ethereum_sovereign: AccountId = AssetHubRococo::execute_with(|| { + ExternalConsensusLocationsConverterFor::< + AssetHubRococoUniversalLocation, + AccountId, + >::convert_location(&origin_location) + .unwrap() + }); AssetHubRococo::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]); // Create asset on the Penpal parachain. @@ -526,8 +530,13 @@ fn send_eth_asset_from_asset_hub_to_ethereum_and_back() { use ahr_xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee; let assethub_location = BridgeHubRococo::sibling_location_of(AssetHubRococo::para_id()); let assethub_sovereign = BridgeHubRococo::sovereign_account_id_of(assethub_location); - let ethereum_sovereign: AccountId = - EthereumLocationsConverterFor::::convert_location(&origin_location).unwrap(); + let ethereum_sovereign: AccountId = AssetHubRococo::execute_with(|| { + ExternalConsensusLocationsConverterFor::< + AssetHubRococoUniversalLocation, + AccountId, + >::convert_location(&origin_location) + .unwrap() + }); AssetHubRococo::force_default_xcm_version(Some(XCM_VERSION)); BridgeHubRococo::force_default_xcm_version(Some(XCM_VERSION)); 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 05c7021d380ae..facd837cbc080 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 @@ -28,6 +28,7 @@ sp-runtime = { workspace = true } # Polkadot pallet-xcm = { workspace = true } xcm = { workspace = true } +xcm-builder = { workspace = true } xcm-executor = { workspace = true } xcm-runtime-apis = { workspace = true } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs index cd5e22372f0e6..a61bc0d5adc6e 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/lib.rs @@ -26,6 +26,7 @@ mod imports { prelude::{AccountId32 as AccountId32Junction, *}, v5, }; + pub use xcm_builder::ExternalConsensusLocationsConverterFor; pub use xcm_executor::traits::TransferType; // Cumulus diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs index 6789aae83ffe4..ea3ede20590c8 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge.rs @@ -13,7 +13,10 @@ // See the License for the specific language governing permissions and // limitations under the License. use crate::{imports::*, tests::penpal_emulated_chain::penpal_runtime}; -use asset_hub_westend_runtime::xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee; +use asset_hub_westend_runtime::xcm_config::{ + bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee, + UniversalLocation as AssetHubWestendUniversalLocation, +}; use bridge_hub_westend_runtime::{ bridge_to_ethereum_config::EthereumGatewayAddress, EthereumBeaconClient, EthereumInboundQueue, }; @@ -24,9 +27,7 @@ use hex_literal::hex; use rococo_westend_system_emulated_network::asset_hub_westend_emulated_chain::genesis::AssetHubWestendAssetOwner; use snowbridge_core::{inbound::InboundQueueFixture, AssetMetadata, TokenIdOf}; use snowbridge_pallet_inbound_queue_fixtures::send_native_eth::make_send_native_eth_message; -use snowbridge_router_primitives::inbound::{ - Command, Destination, EthereumLocationsConverterFor, MessageV1, VersionedMessage, -}; +use snowbridge_router_primitives::inbound::{Command, Destination, MessageV1, VersionedMessage}; use sp_core::H256; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; use xcm_executor::traits::ConvertLocation; @@ -161,8 +162,13 @@ fn register_weth_token_from_ethereum_to_asset_hub() { fn send_weth_token_from_ethereum_to_asset_hub() { let ethereum_network: NetworkId = EthereumNetwork::get().into(); let origin_location = Location::new(2, ethereum_network); - let ethereum_sovereign: AccountId = - EthereumLocationsConverterFor::::convert_location(&origin_location).unwrap(); + let ethereum_sovereign: AccountId = AssetHubWestend::execute_with(|| { + ExternalConsensusLocationsConverterFor::< + AssetHubWestendUniversalLocation, + AccountId, + >::convert_location(&origin_location) + .unwrap() + }); BridgeHubWestend::fund_para_sovereign(AssetHubWestend::para_id().into(), INITIAL_FUND); @@ -280,8 +286,13 @@ fn send_weth_from_ethereum_to_penpal() { let origin_location = (Parent, Parent, ethereum_network_v5).into(); // Fund ethereum sovereign on AssetHub - let ethereum_sovereign: AccountId = - EthereumLocationsConverterFor::::convert_location(&origin_location).unwrap(); + let ethereum_sovereign: AccountId = AssetHubWestend::execute_with(|| { + ExternalConsensusLocationsConverterFor::< + AssetHubWestendUniversalLocation, + AccountId, + >::convert_location(&origin_location) + .unwrap() + }); AssetHubWestend::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]); // Create asset on the Penpal parachain. @@ -387,8 +398,13 @@ fn send_eth_asset_from_asset_hub_to_ethereum_and_back() { use asset_hub_westend_runtime::xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee; let assethub_location = BridgeHubWestend::sibling_location_of(AssetHubWestend::para_id()); let assethub_sovereign = BridgeHubWestend::sovereign_account_id_of(assethub_location); - let ethereum_sovereign: AccountId = - EthereumLocationsConverterFor::::convert_location(&origin_location).unwrap(); + let ethereum_sovereign: AccountId = AssetHubWestend::execute_with(|| { + ExternalConsensusLocationsConverterFor::< + AssetHubWestendUniversalLocation, + AccountId, + >::convert_location(&origin_location) + .unwrap() + }); AssetHubWestend::force_default_xcm_version(Some(XCM_VERSION)); BridgeHubWestend::force_default_xcm_version(Some(XCM_VERSION)); @@ -934,13 +950,17 @@ fn transfer_relay_token() { let expected_token_id = TokenIdOf::convert_location(&expected_asset_id).unwrap(); - let ethereum_sovereign: AccountId = - EthereumLocationsConverterFor::<[u8; 32]>::convert_location(&Location::new( + let ethereum_sovereign: AccountId = AssetHubWestend::execute_with(|| { + ExternalConsensusLocationsConverterFor::< + AssetHubWestendUniversalLocation, + [u8; 32], + >::convert_location(&Location::new( 2, [GlobalConsensus(EthereumNetwork::get())], )) .unwrap() - .into(); + .into() + }); // Register token BridgeHubWestend::execute_with(|| { @@ -1082,10 +1102,14 @@ fn transfer_ah_token() { let ethereum_destination = Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]); - let ethereum_sovereign: AccountId = - EthereumLocationsConverterFor::<[u8; 32]>::convert_location(ðereum_destination) - .unwrap() - .into(); + let ethereum_sovereign: AccountId = AssetHubWestend::execute_with(|| { + ExternalConsensusLocationsConverterFor::< + AssetHubWestendUniversalLocation, + [u8; 32], + >::convert_location(ðereum_destination) + .unwrap() + .into() + }); AssetHubWestend::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]); let asset_id: Location = diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index 3da8aa9b6cfea..f931706e4fc71 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -103,6 +103,7 @@ snowbridge-router-primitives = { workspace = true } [dev-dependencies] asset-test-utils = { workspace = true, default-features = true } parachains-runtimes-test-utils = { workspace = true, default-features = true } +sp-io = { workspace = true, default-features = true } [build-dependencies] substrate-wasm-builder = { optional = true, workspace = true, default-features = true } 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 84526b2e4f2ee..bf44d2408d030 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 @@ -42,7 +42,6 @@ use parachains_common::{ }; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; -use snowbridge_router_primitives::inbound::EthereumLocationsConverterFor; use sp_runtime::traits::{AccountIdConversion, ConvertInto, TryConvertInto}; use testnet_parachains_constants::rococo::snowbridge::{ EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX, @@ -52,16 +51,15 @@ use xcm_builder::{ AccountId32Aliases, AliasChildLocation, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyRecursively, DenyReserveTransferToRelayChain, DenyThenTry, - DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, FrameTransactionalProcessor, - FungibleAdapter, FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, - IsConcrete, LocalMint, MatchedConvertedConcreteId, NetworkExportTableItem, NoChecking, - NonFungiblesAdapter, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, - SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SingleAssetExchangeAdapter, - SovereignPaidRemoteExporter, SovereignSignedViaLocation, StartsWith, - StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WeightInfoBounds, WithComputedOrigin, WithLatestLocationConverter, WithUniqueTopic, - XcmFeeManagerFromComponents, + DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, ExternalConsensusLocationsConverterFor, + FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, HashedDescription, IsConcrete, + LocalMint, MatchedConvertedConcreteId, NetworkExportTableItem, NoChecking, NonFungiblesAdapter, + ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SingleAssetExchangeAdapter, SovereignPaidRemoteExporter, + SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, + TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, + WithLatestLocationConverter, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; @@ -105,12 +103,8 @@ pub type LocationToAccountId = ( AccountId32Aliases, // Foreign locations alias into accounts according to a hash of their standard description. HashedDescription>, - // Different global consensus parachain sovereign account. - // (Used for over-bridge transfers and reserve processing) - GlobalConsensusParachainConvertsFor, - // Ethereum contract sovereign account. - // (Used to get convert ethereum contract locations to sovereign account) - EthereumLocationsConverterFor, + // Different global consensus locations sovereign accounts. + ExternalConsensusLocationsConverterFor, ); /// Means for transacting the native currency on this chain. 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 0057459fc93c6..22e43ec7b3ed2 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/tests/tests.rs @@ -47,13 +47,17 @@ use frame_support::{ }, weights::{Weight, WeightToFee as WeightToFeeT}, }; +use hex_literal::hex; use parachains_common::{AccountId, AssetIdForTrustBackedAssets, AuraId, Balance}; use sp_consensus_aura::SlotDuration; use sp_core::crypto::Ss58Codec; -use sp_runtime::traits::MaybeEquivalence; +use sp_runtime::{traits::MaybeEquivalence, BuildStorage}; use std::convert::Into; use testnet_parachains_constants::rococo::{consensus::*, currency::UNITS, fee::WeightToFee}; -use xcm::latest::prelude::{Assets as XcmAssets, *}; +use xcm::latest::{ + prelude::{Assets as XcmAssets, *}, + WESTEND_GENESIS_HASH, +}; use xcm_builder::WithLatestLocationConverter; use xcm_executor::traits::{JustTry, WeightTrader}; use xcm_runtime_apis::conversions::LocationToAccountHelper; @@ -1514,18 +1518,143 @@ fn location_conversion_works() { ), expected_account_id_str: "5DBoExvojy8tYnHgLL97phNH975CyT45PWTZEeGoBZfAyRMH", }, + // ExternalConsensusLocationsConverterFor + TestCase { + description: "Describe Ethereum Location", + location: Location::new(2, [GlobalConsensus(Ethereum { chain_id: 11155111 })]), + expected_account_id_str: "5GjRnmh5o3usSYzVmsxBWzHEpvJyHK4tKNPhjpUR3ASrruBy", + }, + TestCase { + description: "Describe Ethereum AccountKey", + location: Location::new( + 2, + [ + GlobalConsensus(Ethereum { chain_id: 11155111 }), + AccountKey20 { + network: None, + key: hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"), + }, + ], + ), + expected_account_id_str: "5HV4j4AsqT349oLRZmTjhGKDofPBWmWaPUfWGaRkuvzkjW9i", + }, + TestCase { + description: "Describe Westend Location", + location: Location::new(2, [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH))]), + expected_account_id_str: "5Fb4pyqFuYLZ43USEAcVUBhFTfTckG9zv9kUaVnmR79YgBCe", + }, + TestCase { + description: "Describe Westend AccountID", + location: Location::new( + 2, + [ + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), + AccountId32 { network: None, id: AccountId::from(ALICE).into() }, + ], + ), + expected_account_id_str: "5CpcvNFY6jkMJrd7XQt3yTweRD1WxUeHXvHnbWuVM1MHKHPe", + }, + TestCase { + description: "Describe Westend AccountKey", + location: Location::new( + 2, + [ + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), + AccountKey20 { network: None, key: [0u8; 20] }, + ], + ), + expected_account_id_str: "5FzaTcFwUMyX5Sfe7wRGuc3zw1cbpGAGZpmAsxS4tBX6x6U3", + }, + TestCase { + description: "Describe Westend Treasury Plurality", + location: Location::new( + 2, + [ + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), + Plurality { id: BodyId::Treasury, part: BodyPart::Voice }, + ], + ), + expected_account_id_str: "5CpdRCmCYwnxS1mifwEddYHDJR8ydDfTpi1gwAQKQvfAjjzu", + }, + TestCase { + description: "Describe Westend Parachain Location", + location: Location::new( + 2, + [GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(1000)], + ), + expected_account_id_str: "5CkWf1L181BiSbvoofnzfSg8ZLiBK3i1U4sknzETHk8QS2mA", + }, + TestCase { + description: "Describe Westend Parachain AccountID", + location: Location::new( + 2, + [ + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), + Parachain(1000), + AccountId32 { network: None, id: AccountId::from(ALICE).into() }, + ], + ), + expected_account_id_str: "5G6JJUm6tgsxJhRn76VGme8WGukdUNiBBK6ABUtH9YXEjEk9", + }, + TestCase { + description: "Describe Westend Parachain AccountKey", + location: Location::new( + 2, + [ + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), + Parachain(1000), + AccountKey20 { network: None, key: [0u8; 20] }, + ], + ), + expected_account_id_str: "5EFpSvq8BUAjdjY4tuGhGXZ66P16iQnX7nxsNoHy7TM6NhMa", + }, + TestCase { + description: "Describe Westend Parachain Treasury Plurality", + location: Location::new( + 2, + [ + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), + Parachain(1000), + Plurality { id: BodyId::Treasury, part: BodyPart::Voice }, + ], + ), + expected_account_id_str: "5GfwA4qaz9wpQPPHmf5MSKqvsPyrfx1yYeeZB1SUkqDuRuZ1", + }, + TestCase { + description: "Describe Westend USDT Location", + location: Location::new( + 2, + [ + GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), + Parachain(1000), + PalletInstance(50), + GeneralIndex(1984), + ], + ), + expected_account_id_str: "5Hd77ZjbVRrYiRXER8qo9DRDB8ZzaKtRswZoypMnMLdixzMs", + }, ]; for tc in test_cases { - let expected = - AccountId::from_string(tc.expected_account_id_str).expect("Invalid AccountId string"); - - let got = LocationToAccountHelper::::convert_location( - tc.location.into(), - ) + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + parachain_info::GenesisConfig:: { + parachain_id: 1000.into(), + ..Default::default() + } + .assimilate_storage(&mut t) .unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + let expected = AccountId::from_string(tc.expected_account_id_str) + .expect("Invalid AccountId string"); - assert_eq!(got, expected, "{}", tc.description); + let got = LocationToAccountHelper::::convert_location( + tc.location.into(), + ) + .unwrap(); + + assert_eq!(got, expected, "{}", tc.description); + }); } } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index f7fb858de62e8..1c2b0c1fa4408 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -107,6 +107,7 @@ snowbridge-router-primitives = { workspace = true } [dev-dependencies] asset-test-utils = { workspace = true, default-features = true } parachains-runtimes-test-utils = { workspace = true, default-features = true } +sp-io = { workspace = true, default-features = true } [build-dependencies] substrate-wasm-builder = { optional = true, workspace = true, default-features = true } 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 46c4e4d0b28e4..cc093d6fa5752 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 @@ -42,7 +42,6 @@ use parachains_common::{ }; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; -use snowbridge_router_primitives::inbound::EthereumLocationsConverterFor; use sp_runtime::traits::{AccountIdConversion, ConvertInto, TryConvertInto}; use westend_runtime_constants::system_parachain::COLLECTIVES_ID; use xcm::latest::{prelude::*, ROCOCO_GENESIS_HASH, WESTEND_GENESIS_HASH}; @@ -50,16 +49,15 @@ use xcm_builder::{ AccountId32Aliases, AliasChildLocation, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyRecursively, DenyReserveTransferToRelayChain, DenyThenTry, - DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, FrameTransactionalProcessor, - FungibleAdapter, FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, - IsConcrete, LocalMint, MatchedConvertedConcreteId, NetworkExportTableItem, NoChecking, - NonFungiblesAdapter, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, - SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SingleAssetExchangeAdapter, - SovereignPaidRemoteExporter, SovereignSignedViaLocation, StartsWith, - StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WeightInfoBounds, WithComputedOrigin, WithLatestLocationConverter, WithUniqueTopic, - XcmFeeManagerFromComponents, + DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, ExternalConsensusLocationsConverterFor, + FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, HashedDescription, IsConcrete, + LocalMint, MatchedConvertedConcreteId, NetworkExportTableItem, NoChecking, NonFungiblesAdapter, + ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SingleAssetExchangeAdapter, SovereignPaidRemoteExporter, + SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, + TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, + WithLatestLocationConverter, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; @@ -99,12 +97,8 @@ pub type LocationToAccountId = ( AccountId32Aliases, // Foreign locations alias into accounts according to a hash of their standard description. HashedDescription>, - // Different global consensus parachain sovereign account. - // (Used for over-bridge transfers and reserve processing) - GlobalConsensusParachainConvertsFor, - // Ethereum contract sovereign account. - // (Used to get convert ethereum contract locations to sovereign account) - EthereumLocationsConverterFor, + // Different global consensus locations sovereign accounts. + ExternalConsensusLocationsConverterFor, ); /// Means for transacting the native currency on this chain. 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 38673606541b0..0c195332897c1 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/tests/tests.rs @@ -47,10 +47,11 @@ use frame_support::{ }, weights::{Weight, WeightToFee as WeightToFeeT}, }; +use hex_literal::hex; use parachains_common::{AccountId, AssetIdForTrustBackedAssets, AuraId, Balance}; use sp_consensus_aura::SlotDuration; use sp_core::crypto::Ss58Codec; -use sp_runtime::{traits::MaybeEquivalence, Either}; +use sp_runtime::{traits::MaybeEquivalence, BuildStorage, Either}; use std::{convert::Into, ops::Mul}; use testnet_parachains_constants::westend::{consensus::*, currency::UNITS, fee::WeightToFee}; use xcm::latest::{ @@ -1487,18 +1488,142 @@ fn location_conversion_works() { ), expected_account_id_str: "5DBoExvojy8tYnHgLL97phNH975CyT45PWTZEeGoBZfAyRMH", }, + // ExternalConsensusLocationsConverterFor + TestCase { + description: "Describe Ethereum Location", + location: Location::new(2, [GlobalConsensus(Ethereum { chain_id: 11155111 })]), + expected_account_id_str: "5GjRnmh5o3usSYzVmsxBWzHEpvJyHK4tKNPhjpUR3ASrruBy", + }, + TestCase { + description: "Describe Ethereum AccountKey", + location: Location::new( + 2, + [ + GlobalConsensus(Ethereum { chain_id: 11155111 }), + AccountKey20 { + network: None, + key: hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"), + }, + ], + ), + expected_account_id_str: "5HV4j4AsqT349oLRZmTjhGKDofPBWmWaPUfWGaRkuvzkjW9i", + }, + TestCase { + description: "Describe Rococo Location", + location: Location::new(2, [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH))]), + expected_account_id_str: "5FfpYGrFybJXFsQk7dabr1vEbQ5ycBBu85vrDjPJsF3q4A8P", + }, + TestCase { + description: "Describe Rococo AccountID", + location: Location::new( + 2, + [ + GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), + AccountId32 { network: None, id: AccountId::from(ALICE).into() }, + ], + ), + expected_account_id_str: "5CXVYinTeQKQGWAP9RqaPhitk7ybrqBZf66kCJmtAjV4Xwbg", + }, + TestCase { + description: "Describe Rococo AccountKey", + location: Location::new( + 2, + [ + GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), + AccountKey20 { network: None, key: [0u8; 20] }, + ], + ), + expected_account_id_str: "5GbRhbJWb2hZY7TCeNvTqZXaP3x3UY5xt4ccxpV1ZtJS1gFL", + }, + TestCase { + description: "Describe Rococo Treasury Plurality", + location: Location::new( + 2, + [ + GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), + Plurality { id: BodyId::Treasury, part: BodyPart::Voice }, + ], + ), + expected_account_id_str: "5EGi9NgJNGoMawY8ubnCDLmbdEW6nt2W2U2G3j9E3jXmspT7", + }, + TestCase { + description: "Describe Rococo Parachain Location", + location: Location::new( + 2, + [GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), Parachain(1000)], + ), + expected_account_id_str: "5CQeLKM7XC1xNBiQLp26Wa948cudjYRD5VzvaTG3BjnmUvLL", + }, + TestCase { + description: "Describe Rococo Parachain AccountID", + location: Location::new( + 2, + [ + GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), + Parachain(1000), + AccountId32 { network: None, id: AccountId::from(ALICE).into() }, + ], + ), + expected_account_id_str: "5H8HsK17dV7i7J8fZBNd438rvwd7rHviZxJqyZpLEGJn6vb6", + }, + TestCase { + description: "Describe Rococo Parachain AccountKey", + location: Location::new( + 2, + [ + GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), + Parachain(1000), + AccountKey20 { network: None, key: [0u8; 20] }, + ], + ), + expected_account_id_str: "5G121Rtddxn6zwMD2rZZGXxFHZ2xAgzFUgM9ki4A8wMGo4e2", + }, + TestCase { + description: "Describe Rococo Parachain Treasury Plurality", + location: Location::new( + 2, + [ + GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), + Parachain(1000), + Plurality { id: BodyId::Treasury, part: BodyPart::Voice }, + ], + ), + expected_account_id_str: "5FNk7za2pQ71NHnN1jA63hJxJwdQywiVGnK6RL3nYjCdkWDF", + }, + TestCase { + description: "Describe Rococo USDT Location", + location: Location::new( + 2, + [ + GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), + Parachain(1000), + PalletInstance(50), + GeneralIndex(1984), + ], + ), + expected_account_id_str: "5HNfT779KHeAL7PaVBTQDVxrT6dfJZJoQMTScxLSahBc9kxF", + }, ]; for tc in test_cases { - let expected = - AccountId::from_string(tc.expected_account_id_str).expect("Invalid AccountId string"); - - let got = LocationToAccountHelper::::convert_location( - tc.location.into(), - ) + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + parachain_info::GenesisConfig:: { + parachain_id: 1000.into(), + ..Default::default() + } + .assimilate_storage(&mut t) .unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + let expected = AccountId::from_string(tc.expected_account_id_str) + .expect("Invalid AccountId string"); + let got = LocationToAccountHelper::::convert_location( + tc.location.into(), + ) + .unwrap(); - assert_eq!(got, expected, "{}", tc.description); + assert_eq!(got, expected, "{}", tc.description); + }); } } diff --git a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs index 82db8829e516a..65e2c7e738d30 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -56,15 +56,14 @@ use pallet_xcm::XcmPassthrough; use parachains_common::{xcm_config::AssetFeeAsExistentialDepositMultiplier, TREASURY_PALLET_ID}; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::{impls::ToAuthor, xcm_sender::ExponentialPrice}; -use snowbridge_router_primitives::inbound::EthereumLocationsConverterFor; use sp_runtime::traits::{AccountIdConversion, ConvertInto, Identity, TryConvertInto}; use xcm::latest::{prelude::*, WESTEND_GENESIS_HASH}; use xcm_builder::{ AccountId32Aliases, AliasOriginRootUsingFilter, AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, AsPrefixedGeneralIndex, ConvertedConcreteId, DescribeAllTerminal, DescribeFamily, - EnsureXcmOrigin, FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, - FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, + EnsureXcmOrigin, ExternalConsensusLocationsConverterFor, FixedWeightBounds, + FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, HashedDescription, IsConcrete, LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SingleAssetExchangeAdapter, @@ -106,12 +105,8 @@ pub type LocationToAccountId = ( AccountId32Aliases, // Foreign locations alias into accounts according to a hash of their standard description. HashedDescription>, - // Different global consensus parachain sovereign account. - // (Used for over-bridge transfers and reserve processing) - GlobalConsensusParachainConvertsFor, - // Ethereum contract sovereign account. - // (Used to get convert ethereum contract locations to sovereign account) - EthereumLocationsConverterFor, + // Different global consensus locations sovereign accounts. + ExternalConsensusLocationsConverterFor, ); /// Means for transacting assets on this chain. diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs index 1c08c875eb21c..8f6eb9d642962 100644 --- a/polkadot/xcm/xcm-builder/src/lib.rs +++ b/polkadot/xcm/xcm-builder/src/lib.rs @@ -83,8 +83,9 @@ pub use location_conversion::{ ChildParachainConvertsVia, DescribeAccountId32Terminal, DescribeAccountIdTerminal, DescribeAccountKey20Terminal, DescribeAllTerminal, DescribeBodyTerminal, DescribeFamily, DescribeLocation, DescribePalletTerminal, DescribeTerminus, DescribeTreasuryVoiceTerminal, - GlobalConsensusConvertsFor, GlobalConsensusParachainConvertsFor, HashedDescription, - LocalTreasuryVoiceConvertsVia, ParentIsPreset, SiblingParachainConvertsVia, + ExternalConsensusLocationsConverterFor, GlobalConsensusConvertsFor, + GlobalConsensusParachainConvertsFor, HashedDescription, LocalTreasuryVoiceConvertsVia, + ParentIsPreset, SiblingParachainConvertsVia, }; mod matches_location; diff --git a/polkadot/xcm/xcm-builder/src/location_conversion.rs b/polkadot/xcm/xcm-builder/src/location_conversion.rs index c7aa0c8b5041d..0f4703dca89ca 100644 --- a/polkadot/xcm/xcm-builder/src/location_conversion.rs +++ b/polkadot/xcm/xcm-builder/src/location_conversion.rs @@ -427,6 +427,8 @@ impl GlobalConsensusConvertsFor( PhantomData<(UniversalLocation, AccountId)>, ); @@ -458,6 +460,55 @@ impl } } +/// Converts locations from external global consensus systems (e.g., Ethereum, other parachains) +/// into `AccountId`. +/// +/// Replaces `GlobalConsensusParachainConvertsFor` and `EthereumLocationsConverterFor` in a +/// backwards-compatible way, and extends them for also handling child locations (e.g., +/// `AccountId(Alice)`). +pub struct ExternalConsensusLocationsConverterFor( + PhantomData<(UniversalLocation, AccountId)>, +); + +impl, AccountId: From<[u8; 32]> + Clone> + ConvertLocation + for ExternalConsensusLocationsConverterFor +{ + fn convert_location(location: &Location) -> Option { + let universal_source = UniversalLocation::get(); + tracing::trace!( + target: "xcm::location_conversion", + "ExternalConsensusLocationsConverterFor universal_source: {:?}, location: {:?}", + universal_source, location, + ); + let (remote_network, remote_location) = + ensure_is_remote(universal_source, location.clone()).ok()?; + + // replaces and extends `EthereumLocationsConverterFor` and + // `GlobalConsensusParachainConvertsFor` + let acc_id: AccountId = if let Ethereum { chain_id } = &remote_network { + match remote_location.as_slice() { + // equivalent to `EthereumLocationsConverterFor` + [] => (b"ethereum-chain", chain_id).using_encoded(blake2_256).into(), + // equivalent to `EthereumLocationsConverterFor` + [AccountKey20 { network: _, key }] => + (b"ethereum-chain", chain_id, *key).using_encoded(blake2_256).into(), + // extends `EthereumLocationsConverterFor` + tail => (b"ethereum-chain", chain_id, tail).using_encoded(blake2_256).into(), + } + } else { + match remote_location.as_slice() { + // equivalent to `GlobalConsensusParachainConvertsFor` + [Parachain(para_id)] => + (b"glblcnsnss/prchn_", remote_network, para_id).using_encoded(blake2_256).into(), + // converts everything else based on hash of encoded location tail + tail => (b"glblcnsnss", remote_network, tail).using_encoded(blake2_256).into(), + } + }; + Some(acc_id) + } +} + #[cfg(test)] mod tests { use super::*; @@ -608,32 +659,32 @@ mod tests { GlobalConsensusConvertsFor::::convert_location( &Location::new(2, [GlobalConsensus(network_3)]), ) - .expect("conversion is ok"); + .unwrap(); let res_2_gc_network_3 = GlobalConsensusConvertsFor::::convert_location( &Location::new(2, [GlobalConsensus(network_3)]), ) - .expect("conversion is ok"); + .unwrap(); let res_1_gc_network_4 = GlobalConsensusConvertsFor::::convert_location( &Location::new(2, [GlobalConsensus(network_4)]), ) - .expect("conversion is ok"); + .unwrap(); let res_2_gc_network_4 = GlobalConsensusConvertsFor::::convert_location( &Location::new(2, [GlobalConsensus(network_4)]), ) - .expect("conversion is ok"); + .unwrap(); let res_1_gc_network_5 = GlobalConsensusConvertsFor::::convert_location( &Location::new(2, [GlobalConsensus(network_5)]), ) - .expect("conversion is ok"); + .unwrap(); let res_2_gc_network_5 = GlobalConsensusConvertsFor::::convert_location( &Location::new(2, [GlobalConsensus(network_5)]), ) - .expect("conversion is ok"); + .unwrap(); assert_ne!(res_1_gc_network_3, res_1_gc_network_4); assert_ne!(res_1_gc_network_4, res_1_gc_network_5); @@ -678,6 +729,10 @@ mod tests { GlobalConsensusParachainConvertsFor::::convert_location( &location, ); + let result2 = + ExternalConsensusLocationsConverterFor::::convert_location( + &location, + ); match result { Some(account) => { assert_eq!( @@ -707,29 +762,64 @@ mod tests { ); }, } + if expected_result { + assert_eq!(result, result2); + } } // all success + let location = Location::new(2, [GlobalConsensus(ByGenesis([3; 32])), Parachain(1000)]); let res_gc_a_p1000 = GlobalConsensusParachainConvertsFor::::convert_location( - &Location::new(2, [GlobalConsensus(ByGenesis([3; 32])), Parachain(1000)]), + &location, ) - .expect("conversion is ok"); + .unwrap(); + assert_eq!( + res_gc_a_p1000, + ExternalConsensusLocationsConverterFor::::convert_location( + &location, + ).unwrap() + ); + + let location = Location::new(2, [GlobalConsensus(ByGenesis([3; 32])), Parachain(1001)]); let res_gc_a_p1001 = GlobalConsensusParachainConvertsFor::::convert_location( - &Location::new(2, [GlobalConsensus(ByGenesis([3; 32])), Parachain(1001)]), + &location, ) - .expect("conversion is ok"); + .unwrap(); + assert_eq!( + res_gc_a_p1001, + ExternalConsensusLocationsConverterFor::::convert_location( + &location, + ).unwrap() + ); + + let location = Location::new(2, [GlobalConsensus(ByGenesis([4; 32])), Parachain(1000)]); let res_gc_b_p1000 = GlobalConsensusParachainConvertsFor::::convert_location( - &Location::new(2, [GlobalConsensus(ByGenesis([4; 32])), Parachain(1000)]), + &location, ) - .expect("conversion is ok"); + .unwrap(); + assert_eq!( + res_gc_b_p1000, + ExternalConsensusLocationsConverterFor::::convert_location( + &location, + ).unwrap() + ); + + let location = Location::new(2, [GlobalConsensus(ByGenesis([4; 32])), Parachain(1001)]); let res_gc_b_p1001 = GlobalConsensusParachainConvertsFor::::convert_location( - &Location::new(2, [GlobalConsensus(ByGenesis([4; 32])), Parachain(1001)]), + &location, ) - .expect("conversion is ok"); + .unwrap(); + assert_eq!( + res_gc_b_p1001, + ExternalConsensusLocationsConverterFor::::convert_location( + &location, + ).unwrap() + ); + assert_ne!(res_gc_a_p1000, res_gc_a_p1001); assert_ne!(res_gc_a_p1000, res_gc_b_p1000); assert_ne!(res_gc_a_p1000, res_gc_b_p1001); diff --git a/prdoc/pr_7313.prdoc b/prdoc/pr_7313.prdoc new file mode 100644 index 0000000000000..d1114534cc239 --- /dev/null +++ b/prdoc/pr_7313.prdoc @@ -0,0 +1,21 @@ +title: "[XCM] add generic location to account converter that also works with external ecosystems" + +doc: +- audience: Runtime Dev + description: | + Adds a new `ExternalConsensusLocationsConverterFor` struct to handle external global + consensus locations and their child locations. + This struct extends the functionality of existing converters (`GlobalConsensusParachainConvertsFor` + and `EthereumLocationsConverterFor`) while maintaining backward compatibility. + +crates: + - name: snowbridge-router-primitives + bump: minor + - name: staging-xcm-builder + bump: minor + - name: asset-hub-rococo-runtime + bump: minor + - name: asset-hub-westend-runtime + bump: minor + - name: penpal-runtime + bump: minor diff --git a/prdoc/pr_7790.prdoc b/prdoc/pr_7790.prdoc new file mode 100644 index 0000000000000..09a8ff52c8d13 --- /dev/null +++ b/prdoc/pr_7790.prdoc @@ -0,0 +1,9 @@ +title: 'pallet-scheduler: Put back postponed tasks into the agenda' +doc: +- audience: Runtime Dev + description: "Right now `pallet-scheduler` is not putting back postponed tasks into\ + \ the agenda when the early weight check is failing. This pull request ensures\ + \ that these tasks are put back into the agenda and are not just \"lost\".\r\n" +crates: +- name: pallet-scheduler + bump: patch diff --git a/prdoc/pr_7802.prdoc b/prdoc/pr_7802.prdoc new file mode 100644 index 0000000000000..71c1256e75c74 --- /dev/null +++ b/prdoc/pr_7802.prdoc @@ -0,0 +1,11 @@ +title: '[AHM] child bounties and recovery: make more stuff public' +doc: +- audience: Runtime Dev + description: | + Make some items in the child-bounties and recovery pallet public to reduce code-duplication for + the Asset Hub migration. +crates: +- name: pallet-child-bounties + bump: minor +- name: pallet-recovery + bump: minor diff --git a/substrate/frame/child-bounties/src/lib.rs b/substrate/frame/child-bounties/src/lib.rs index 9fca26510989a..b20ef72fda4b1 100644 --- a/substrate/frame/child-bounties/src/lib.rs +++ b/substrate/frame/child-bounties/src/lib.rs @@ -88,26 +88,26 @@ pub use weights::WeightInfo; pub use pallet::*; -type BalanceOf = pallet_treasury::BalanceOf; -type BountiesError = pallet_bounties::Error; -type BountyIndex = pallet_bounties::BountyIndex; -type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; -type BlockNumberFor = +pub type BalanceOf = pallet_treasury::BalanceOf; +pub type BountiesError = pallet_bounties::Error; +pub type BountyIndex = pallet_bounties::BountyIndex; +pub type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; +pub type BlockNumberFor = <::BlockNumberProvider as BlockNumberProvider>::BlockNumber; /// A child bounty proposal. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct ChildBounty { /// The parent of this child-bounty. - parent_bounty: BountyIndex, + pub parent_bounty: BountyIndex, /// The (total) amount that should be paid if this child-bounty is rewarded. - value: Balance, + pub value: Balance, /// The child bounty curator fee. - fee: Balance, + pub fee: Balance, /// The deposit of child-bounty curator. - curator_deposit: Balance, + pub curator_deposit: Balance, /// The status of this child-bounty. - status: ChildBountyStatus, + pub status: ChildBountyStatus, } /// The status of a child-bounty. diff --git a/substrate/frame/recovery/src/lib.rs b/substrate/frame/recovery/src/lib.rs index 8159bbefa76b1..80c897dfef700 100644 --- a/substrate/frame/recovery/src/lib.rs +++ b/substrate/frame/recovery/src/lib.rs @@ -181,23 +181,24 @@ mod mock; mod tests; pub mod weights; -type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; -type BalanceOf = +pub type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; +pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; -type BlockNumberFromProviderOf = +pub type BlockNumberFromProviderOf = <::BlockNumberProvider as BlockNumberProvider>::BlockNumber; -type FriendsOf = BoundedVec<::AccountId, ::MaxFriends>; +pub type FriendsOf = + BoundedVec<::AccountId, ::MaxFriends>; /// An active recovery process. #[derive(Clone, Eq, PartialEq, Encode, Decode, Default, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct ActiveRecovery { /// The block number when the recovery process started. - created: BlockNumber, + pub created: BlockNumber, /// The amount held in reserve of the `depositor`, /// to be returned once this recovery process is closed. - deposit: Balance, + pub deposit: Balance, /// The friends which have vouched so far. Always sorted. - friends: Friends, + pub friends: Friends, } /// Configuration for recovering an account. @@ -205,14 +206,14 @@ pub struct ActiveRecovery { pub struct RecoveryConfig { /// The minimum number of blocks since the start of the recovery process before the account /// can be recovered. - delay_period: BlockNumber, + pub delay_period: BlockNumber, /// The amount held in reserve of the `depositor`, /// to be returned once this configuration is removed. - deposit: Balance, + pub deposit: Balance, /// The list of friends which can help recover an account. Always sorted. - friends: Friends, + pub friends: Friends, /// The number of approving friends needed to recover an account. - threshold: u16, + pub threshold: u16, } #[frame_support::pallet] diff --git a/substrate/frame/scheduler/src/lib.rs b/substrate/frame/scheduler/src/lib.rs index 4990a7066e101..89775350b249a 100644 --- a/substrate/frame/scheduler/src/lib.rs +++ b/substrate/frame/scheduler/src/lib.rs @@ -323,6 +323,7 @@ pub mod pallet { type BlockNumberProvider: BlockNumberProvider; } + /// Block number at which the agenda began incomplete execution. #[pallet::storage] pub type IncompleteSince = StorageValue<_, BlockNumberFor>; @@ -386,6 +387,8 @@ pub mod pallet { RetryFailed { task: TaskAddress>, id: Option }, /// The given task can never be executed since it is overweight. PermanentlyOverweight { task: TaskAddress>, id: Option }, + /// Agenda is incomplete from `when`. + AgendaIncomplete { when: BlockNumberFor }, } #[pallet::error] @@ -1225,6 +1228,7 @@ impl Pallet { } incomplete_since = incomplete_since.min(when); if incomplete_since <= now { + Self::deposit_event(Event::AgendaIncomplete { when: incomplete_since }); IncompleteSince::::put(incomplete_since); } } @@ -1258,10 +1262,7 @@ impl Pallet { let mut dropped = 0; for (agenda_index, _) in ordered.into_iter().take(max as usize) { - let task = match agenda[agenda_index as usize].take() { - None => continue, - Some(t) => t, - }; + let Some(task) = agenda[agenda_index as usize].take() else { continue }; let base_weight = T::WeightInfo::service_task( task.call.lookup_len().map(|x| x as usize), task.maybe_id.is_some(), @@ -1269,6 +1270,7 @@ impl Pallet { ); if !weight.can_consume(base_weight) { postponed += 1; + agenda[agenda_index as usize] = Some(task); break } let result = Self::service_task(weight, now, when, agenda_index, *executed == 0, task); diff --git a/substrate/frame/scheduler/src/mock.rs b/substrate/frame/scheduler/src/mock.rs index a9aea97542acd..28b5ee6d6083c 100644 --- a/substrate/frame/scheduler/src/mock.rs +++ b/substrate/frame/scheduler/src/mock.rs @@ -212,7 +212,7 @@ impl WeightInfo for TestWeightInfo { } } parameter_types! { - pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * + pub storage MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * BlockWeights::get().max_block; } diff --git a/substrate/frame/scheduler/src/tests.rs b/substrate/frame/scheduler/src/tests.rs index d0a3acc05ac7e..1b7739e855ea7 100644 --- a/substrate/frame/scheduler/src/tests.rs +++ b/substrate/frame/scheduler/src/tests.rs @@ -1264,8 +1264,8 @@ fn cancel_named_periodic_scheduling_works() { #[test] fn scheduler_respects_weight_limits() { - let max_weight: Weight = ::MaximumWeight::get(); new_test_ext().execute_with(|| { + let max_weight: Weight = ::MaximumWeight::get(); let call = RuntimeCall::Logger(LoggerCall::log { i: 42, weight: max_weight / 3 * 2 }); assert_ok!(Scheduler::do_schedule( DispatchTime::At(4), @@ -1292,8 +1292,8 @@ fn scheduler_respects_weight_limits() { #[test] fn retry_respects_weight_limits() { - let max_weight: Weight = ::MaximumWeight::get(); new_test_ext().execute_with(|| { + let max_weight: Weight = ::MaximumWeight::get(); // schedule 42 let call = RuntimeCall::Logger(LoggerCall::log { i: 42, weight: max_weight / 3 * 2 }); assert_ok!(Scheduler::do_schedule( @@ -1344,8 +1344,8 @@ fn retry_respects_weight_limits() { #[test] fn try_schedule_retry_respects_weight_limits() { - let max_weight: Weight = ::MaximumWeight::get(); new_test_ext().execute_with(|| { + let max_weight: Weight = ::MaximumWeight::get(); let service_agendas_weight = ::WeightInfo::service_agendas_base(); let service_agenda_weight = ::WeightInfo::service_agenda_base( ::MaxScheduledPerBlock::get(), @@ -1404,8 +1404,8 @@ fn try_schedule_retry_respects_weight_limits() { /// Permanently overweight calls are not deleted but also not executed. #[test] fn scheduler_does_not_delete_permanently_overweight_call() { - let max_weight: Weight = ::MaximumWeight::get(); new_test_ext().execute_with(|| { + let max_weight: Weight = ::MaximumWeight::get(); let call = RuntimeCall::Logger(LoggerCall::log { i: 42, weight: max_weight }); assert_ok!(Scheduler::do_schedule( DispatchTime::At(4), @@ -1430,10 +1430,10 @@ fn scheduler_does_not_delete_permanently_overweight_call() { #[test] fn scheduler_handles_periodic_failure() { - let max_weight: Weight = ::MaximumWeight::get(); - let max_per_block = ::MaxScheduledPerBlock::get(); - new_test_ext().execute_with(|| { + let max_weight: Weight = ::MaximumWeight::get(); + let max_per_block = ::MaxScheduledPerBlock::get(); + let call = RuntimeCall::Logger(LoggerCall::log { i: 42, weight: (max_weight / 3) * 2 }); let bound = Preimage::bound(call).unwrap(); @@ -1472,9 +1472,9 @@ fn scheduler_handles_periodic_failure() { #[test] fn scheduler_handles_periodic_unavailable_preimage() { - let max_weight: Weight = ::MaximumWeight::get(); - new_test_ext().execute_with(|| { + let max_weight: Weight = ::MaximumWeight::get(); + let call = RuntimeCall::Logger(LoggerCall::log { i: 42, weight: (max_weight / 3) * 2 }); let hash = ::Hashing::hash_of(&call); let len = call.using_encoded(|x| x.len()) as u32; @@ -1518,8 +1518,8 @@ fn scheduler_handles_periodic_unavailable_preimage() { #[test] fn scheduler_respects_priority_ordering() { - let max_weight: Weight = ::MaximumWeight::get(); new_test_ext().execute_with(|| { + let max_weight: Weight = ::MaximumWeight::get(); let call = RuntimeCall::Logger(LoggerCall::log { i: 42, weight: max_weight / 3 }); assert_ok!(Scheduler::do_schedule( DispatchTime::At(4), @@ -3039,3 +3039,40 @@ fn unavailable_call_is_detected() { assert!(!Preimage::is_requested(&hash)); }); } + +#[test] +fn postponed_task_is_still_available() { + new_test_ext().execute_with(|| { + let service_agendas_weight = ::WeightInfo::service_agendas_base(); + let service_agenda_weight = ::WeightInfo::service_agenda_base( + ::MaxScheduledPerBlock::get(), + ); + + assert_ok!(Scheduler::schedule( + RuntimeOrigin::root(), + 4, + None, + 128, + Box::new(RuntimeCall::from(frame_system::Call::remark { + remark: vec![0u8; 3 * 1024 * 1024], + })) + )); + System::run_to_block::(3); + // Scheduled calls are in the agenda. + assert_eq!(Agenda::::get(4).len(), 1); + + let old_weight = MaximumSchedulerWeight::get(); + MaximumSchedulerWeight::set(&service_agenda_weight.saturating_add(service_agendas_weight)); + + System::run_to_block::(4); + + // The task should still be there. + assert_eq!(Agenda::::get(4).iter().filter(|a| a.is_some()).count(), 1); + System::assert_last_event(crate::Event::AgendaIncomplete { when: 4 }.into()); + + // Now it should get executed + MaximumSchedulerWeight::set(&old_weight); + System::run_to_block::(5); + assert!(Agenda::::get(4).is_empty()); + }); +}