Skip to content

Commit

Permalink
Local Pluralities Get Free Xcm Execution (#125)
Browse files Browse the repository at this point in the history
Local Root and Pluralities Get Free Xcm Execution

After running e2e tests, we discovered that xcm programs sent from local
pluralities such as Relay Chain Staking Admin or Collectives Fellowship
were failing due to insufficient balance to cover execution/delivery
fees. This PR addresses this issue by granting them a free execution.

Additionally, we encountered similar failures when attempting to
teleport slashed assets from Collectives to the Relay Chain Treasury. In
this PR, we resolve this problem by teleporting those assets on behalf
of the Collectives root location, allowing for a free delivery.

---------

Co-authored-by: joe petrowski <[email protected]>
Co-authored-by: Bastian Köcher <[email protected]>
Co-authored-by: Bastian Köcher <[email protected]>
  • Loading branch information
4 people authored Jan 5, 2024
1 parent 4b6c871 commit 101882e
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 38 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Election provider: use a geometric deposit base calculation for EPM signed submissions in Polkadot and Kusama ([polkadot-fellows/runtimes#56](https://github.com/polkadot-fellows/runtimes/pull/56))
- Make `IdentityInfo` generic in `pallet-identity` ([polkadot-fellows/runtimes#87](https://github.com/polkadot-fellows/runtimes/pull/87)). Context: https://github.com/paritytech/polkadot-sdk/pull/1661
- Whitelist `force_default_xcm_version` in XCM call filter ([polkadot-fellows/runtimes#45](https://github.com/polkadot-fellows/runtimes/pull/45))
- Set up an account ID for the local root location on Polkadot Collectives ([polkadot-fellows/runtimes#125](https://github.com/polkadot-fellows/runtimes/pull/125))
- Increase confirmation period for treasury spend tracks on Polkadot & Kusama ([polkadot-fellows/runtimes#119](https://github.com/polkadot-fellows/runtimes/pull/119))

### Added
Expand Down
12 changes: 10 additions & 2 deletions relay/kusama/src/xcm_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use super::{
};
use frame_support::{
match_types, parameter_types,
traits::{Contains, Everything, Nothing},
traits::{Contains, Equals, Everything, Nothing},
weights::Weight,
};
use frame_system::EnsureRoot;
Expand All @@ -47,6 +47,7 @@ use xcm_builder::{
use xcm_executor::traits::WithOriginFilter;

parameter_types! {
pub const RootLocation: MultiLocation = Here.into_location();
/// The location of the KSM token, from the context of this chain. Since this token is native to this
/// chain, we make it synonymous with it and thus it is the `Here` location, which means "equivalent to
/// the context".
Expand Down Expand Up @@ -147,6 +148,9 @@ match_types! {
pub type OnlyParachains: impl Contains<MultiLocation> = {
MultiLocation { parents: 0, interior: X1(Parachain(_)) }
};
pub type LocalPlurality: impl Contains<MultiLocation> = {
MultiLocation { parents: 0, interior: X1(Plurality { .. }) }
};
}

/// The barriers one of which must be passed for an XCM message to be executed.
Expand Down Expand Up @@ -321,6 +325,10 @@ impl Contains<RuntimeCall> for SafeCallFilter {
}
}

/// Locations that will not be charged fees in the executor, neither for execution nor delivery.
/// We only waive fees for system functions, which these locations represent.
pub type WaivedLocations = (SystemParachains, Equals<RootLocation>, LocalPlurality);

pub struct XcmConfig;
impl xcm_executor::Config for XcmConfig {
type RuntimeCall = RuntimeCall;
Expand All @@ -347,7 +355,7 @@ impl xcm_executor::Config for XcmConfig {
type SubscriptionService = XcmPallet;
type PalletInstancesInfo = AllPalletsWithSystem;
type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
type FeeManager = XcmFeesToAccount<Self, SystemParachains, AccountId, TreasuryAccount>;
type FeeManager = XcmFeesToAccount<Self, WaivedLocations, AccountId, TreasuryAccount>;
// No bridges yet...
type MessageExporter = ();
type UniversalAliases = Nothing;
Expand Down
12 changes: 10 additions & 2 deletions relay/polkadot/src/xcm_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use super::{
};
use frame_support::{
match_types, parameter_types,
traits::{Contains, Everything, Nothing},
traits::{Contains, Equals, Everything, Nothing},
weights::Weight,
};
use frame_system::EnsureRoot;
Expand Down Expand Up @@ -52,6 +52,7 @@ use xcm_builder::{
use xcm_executor::traits::WithOriginFilter;

parameter_types! {
pub const RootLocation: MultiLocation = Here.into_location();
/// The location of the DOT token, from the context of this chain. Since this token is native to this
/// chain, we make it synonymous with it and thus it is the `Here` location, which means "equivalent to
/// the context".
Expand Down Expand Up @@ -161,6 +162,9 @@ match_types! {
MultiLocation { parents: 0, interior: X1(Parachain(COLLECTIVES_ID)) } |
MultiLocation { parents: 0, interior: X2(Parachain(COLLECTIVES_ID), Plurality { id: BodyId::Technical, .. }) }
};
pub type LocalPlurality: impl Contains<MultiLocation> = {
MultiLocation { parents: 0, interior: X1(Plurality { .. }) }
};
}

/// The barriers one of which must be passed for an XCM message to be executed.
Expand Down Expand Up @@ -324,6 +328,10 @@ impl Contains<RuntimeCall> for SafeCallFilter {
}
}

/// Locations that will not be charged fees in the executor, neither for execution nor delivery.
/// We only waive fees for system functions, which these locations represent.
pub type WaivedLocations = (SystemParachains, Equals<RootLocation>, LocalPlurality);

pub struct XcmConfig;
impl xcm_executor::Config for XcmConfig {
type RuntimeCall = RuntimeCall;
Expand Down Expand Up @@ -351,7 +359,7 @@ impl xcm_executor::Config for XcmConfig {
type SubscriptionService = XcmPallet;
type PalletInstancesInfo = AllPalletsWithSystem;
type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
type FeeManager = XcmFeesToAccount<Self, SystemParachains, AccountId, TreasuryAccount>;
type FeeManager = XcmFeesToAccount<Self, WaivedLocations, AccountId, TreasuryAccount>;
// No bridges yet...
type MessageExporter = ();
type UniversalAliases = Nothing;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@
mod origins;
mod tracks;
use crate::{
impls::ToParentTreasury, weights, xcm_config::TreasurerBodyId, AccountId, AssetRate, Balance,
Balances, FellowshipReferenda, GovernanceLocation, PolkadotTreasuryAccount, Preimage, Runtime,
RuntimeCall, RuntimeEvent, RuntimeOrigin, Scheduler, DAYS,
impls::ToParentTreasury,
weights,
xcm_config::{LocationToAccountId, TreasurerBodyId},
AccountId, AssetRate, Balance, Balances, FellowshipReferenda, GovernanceLocation,
PolkadotTreasuryAccount, Preimage, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin,
Scheduler, DAYS,
};
use cumulus_primitives_core::Junction::GeneralIndex;
use frame_support::{
Expand All @@ -44,10 +47,7 @@ use polkadot_runtime_common::impls::{
use polkadot_runtime_constants::{currency::GRAND, time::HOURS, xcm::body::FELLOWSHIP_ADMIN_INDEX};
use sp_arithmetic::Permill;
use sp_core::{ConstU128, ConstU32};
use sp_runtime::traits::{
AccountIdConversion, ConstU16, ConvertToValue, IdentityLookup, Replace, TakeFirst,
};
use system_parachains_constants::polkadot::account;
use sp_runtime::traits::{ConstU16, ConvertToValue, IdentityLookup, Replace, TakeFirst};
use xcm::latest::BodyId;
use xcm_builder::{AliasesIntoAccountId32, LocatableAssetId, PayOverXcm};

Expand All @@ -70,8 +70,6 @@ pub mod ranks {
}

parameter_types! {
// Referenda pallet account, used to temporarily deposit slashed imbalance before teleporting.
pub ReferendaPalletAccount: AccountId = account::REFERENDA_PALLET_ID.into_account_truncating();
pub const FellowshipAdminBodyId: BodyId = BodyId::Index(FELLOWSHIP_ADMIN_INDEX);
}

Expand Down Expand Up @@ -101,7 +99,7 @@ impl pallet_referenda::Config<FellowshipReferendaInstance> for Runtime {
>;
type CancelOrigin = Architects;
type KillOrigin = Masters;
type Slash = ToParentTreasury<PolkadotTreasuryAccount, ReferendaPalletAccount, Runtime>;
type Slash = ToParentTreasury<PolkadotTreasuryAccount, LocationToAccountId, Runtime>;
type Votes = pallet_ranked_collective::Votes;
type Tally = pallet_ranked_collective::TallyOf<Runtime, FellowshipCollectiveInstance>;
type SubmissionDeposit = ConstU128<0>;
Expand Down
33 changes: 21 additions & 12 deletions system-parachains/collectives/collectives-polkadot/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ use pallet_alliance::{ProposalIndex, ProposalProvider};
use parachains_common::impls::NegativeImbalance;
use sp_runtime::DispatchError;
use sp_std::{cmp::Ordering, marker::PhantomData, prelude::*};
use xcm::latest::{Fungibility, Junction, Parent, WeightLimit};
use xcm::latest::{Fungibility, Junction, Junctions::Here, MultiLocation, Parent, WeightLimit};
use xcm_executor::traits::ConvertLocation;

type AccountIdOf<T> = <T as frame_system::Config>::AccountId;

Expand All @@ -38,36 +39,44 @@ pub type BalanceOf<T> =

/// Implements `OnUnbalanced::on_unbalanced` to teleport slashed assets to relay chain treasury
/// account.
pub struct ToParentTreasury<TreasuryAccount, PalletAccount, T>(
PhantomData<(TreasuryAccount, PalletAccount, T)>,
pub struct ToParentTreasury<TreasuryAccount, AccountIdConverter, T>(
PhantomData<(TreasuryAccount, AccountIdConverter, T)>,
);

impl<TreasuryAccount, PalletAccount, T> OnUnbalanced<NegativeImbalance<T>>
for ToParentTreasury<TreasuryAccount, PalletAccount, T>
impl<TreasuryAccount, AccountIdConverter, T> OnUnbalanced<NegativeImbalance<T>>
for ToParentTreasury<TreasuryAccount, AccountIdConverter, T>
where
T: pallet_balances::Config + pallet_xcm::Config + frame_system::Config,
<<T as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId: From<AccountIdOf<T>>,
[u8; 32]: From<<T as frame_system::Config>::AccountId>,
TreasuryAccount: Get<AccountIdOf<T>>,
PalletAccount: Get<AccountIdOf<T>>,
BalanceOf<T>: Into<Fungibility>,
AccountIdConverter: ConvertLocation<AccountIdOf<T>>,
{
fn on_unbalanced(amount: NegativeImbalance<T>) {
let amount = match amount.drop_zero() {
Ok(..) => return,
Err(amount) => amount,
};
let imbalance = amount.peek();
let pallet_acc: AccountIdOf<T> = PalletAccount::get();
let treasury_acc: AccountIdOf<T> = TreasuryAccount::get();

<pallet_balances::Pallet<T>>::resolve_creating(&pallet_acc, amount);
let root_location: MultiLocation = Here.into();
let root_account: AccountIdOf<T> =
match AccountIdConverter::convert_location(&root_location) {
Some(a) => a,
None => {
log::warn!("Failed to convert root origin into account id");
return
},
};
let treasury_account: AccountIdOf<T> = TreasuryAccount::get();

<pallet_balances::Pallet<T>>::resolve_creating(&root_account, amount);

let result = <pallet_xcm::Pallet<T>>::limited_teleport_assets(
<<T as frame_system::Config>::RuntimeOrigin>::signed(pallet_acc.into()),
<<T as frame_system::Config>::RuntimeOrigin>::root(),
Box::new(Parent.into()),
Box::new(
Junction::AccountId32 { network: None, id: treasury_acc.into() }
Junction::AccountId32 { network: None, id: treasury_account.into() }
.into_location()
.into(),
),
Expand Down
8 changes: 3 additions & 5 deletions system-parachains/collectives/collectives-polkadot/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ use system_parachains_constants::{
SLOT_DURATION,
};
use xcm_config::{
GovernanceLocation, TreasurerBodyId, XcmConfig, XcmOriginToTransactDispatchOrigin,
GovernanceLocation, LocationToAccountId, TreasurerBodyId, XcmConfig,
XcmOriginToTransactDispatchOrigin,
};

#[cfg(any(feature = "std", test))]
Expand Down Expand Up @@ -509,9 +510,6 @@ pub const MAX_ALLIES: u32 = 100;

parameter_types! {
pub const AllyDeposit: Balance = 1_000 * UNITS; // 1,000 DOT bond to join as an Ally
// The Alliance pallet account, used as a temporary place to deposit a slashed imbalance
// before the teleport to the Treasury.
pub AlliancePalletAccount: AccountId = ALLIANCE_PALLET_ID.into_account_truncating();
pub PolkadotTreasuryAccount: AccountId = POLKADOT_TREASURY_PALLET_ID.into_account_truncating();
// The number of blocks a member must wait between giving a retirement notice and retiring.
// Supposed to be greater than time required to `kick_member` with alliance motion.
Expand All @@ -525,7 +523,7 @@ impl pallet_alliance::Config for Runtime {
type MembershipManager = RootOrAllianceTwoThirdsMajority;
type AnnouncementOrigin = RootOrAllianceTwoThirdsMajority;
type Currency = Balances;
type Slashed = ToParentTreasury<PolkadotTreasuryAccount, AlliancePalletAccount, Runtime>;
type Slashed = ToParentTreasury<PolkadotTreasuryAccount, LocationToAccountId, Runtime>;
type InitializeMembers = AllianceMotion;
type MembershipChanged = AllianceMotion;
type RetirementPeriod = AllianceRetirementPeriod;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,16 @@ use xcm_builder::{
AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses,
AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, CurrencyAdapter,
DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily,
EnsureXcmOrigin, FixedWeightBounds, HashedDescription, IsConcrete, OriginToPluralityVoice,
ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative,
SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32,
SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents,
WithComputedOrigin, WithUniqueTopic, XcmFeesToAccount,
DescribeTerminus, EnsureXcmOrigin, FixedWeightBounds, HashedDescription, IsConcrete,
OriginToPluralityVoice, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative,
SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative,
SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId,
UsingComponents, WithComputedOrigin, WithUniqueTopic, XcmFeesToAccount,
};
use xcm_executor::{traits::WithOriginFilter, XcmExecutor};

parameter_types! {
pub const RootLocation: MultiLocation = MultiLocation::here();
pub const DotLocation: MultiLocation = MultiLocation::parent();
pub const RelayNetwork: Option<NetworkId> = Some(NetworkId::Polkadot);
pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into();
Expand All @@ -71,6 +72,8 @@ pub type LocationToAccountId = (
AccountId32Aliases<RelayNetwork, AccountId>,
// Foreign locations alias into accounts according to a hash of their standard description.
HashedDescription<AccountId, DescribeFamily<DescribeAllTerminal>>,
// Here/local root location to `AccountId`.
HashedDescription<AccountId, DescribeTerminus>,
);

/// Means for transacting the native currency on this chain.
Expand Down Expand Up @@ -132,6 +135,9 @@ match_types! {
MultiLocation { parents: 1, interior: Here } |
MultiLocation { parents: 1, interior: X1(_) }
};
pub type LocalPlurality: impl Contains<MultiLocation> = {
MultiLocation { parents: 0, interior: X1(Plurality { .. }) }
};
}

/// A call filter for the XCM Transact instruction. This is a temporary measure until we properly
Expand Down Expand Up @@ -261,8 +267,12 @@ match_types! {
/// Locations that will not be charged fees in the executor,
/// either execution or delivery.
/// We only waive fees for system functions, which these locations represent.
pub type WaivedLocations =
(RelayOrOtherSystemParachains<SystemParachains, Runtime>, Equals<RelayTreasuryLocation>);
pub type WaivedLocations = (
RelayOrOtherSystemParachains<SystemParachains, Runtime>,
Equals<RelayTreasuryLocation>,
Equals<RootLocation>,
LocalPlurality,
);

/// Cases where a remote origin is accepted as trusted Teleporter for a given asset:
/// - DOT with the parent Relay Chain and sibling parachains.
Expand Down

0 comments on commit 101882e

Please sign in to comment.