From 12932a5883e02e6dec39b8bf84c5a1537a3cfed9 Mon Sep 17 00:00:00 2001 From: Daniel Olano Date: Sat, 13 Apr 2024 03:56:40 +0200 Subject: [PATCH] Allow communities to send XCM (#360) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Allow communities to send XCM * change(runtime-kreivo): convert Plurality beneficiary into community AccountId * Allow communities to send XCM * wip(pallet-communities): dispatch_as_origin * fix(kreivo-runtime): comment fixup * change: gate certain pallet-communities calls behind "testnet" feature --------- Co-authored-by: Pablo Andrés Dorado Suárez --- node/Cargo.toml | 3 ++ pallets/communities/Cargo.toml | 3 +- pallets/communities/src/lib.rs | 30 ++++++++++++-- pallets/communities/src/mock.rs | 1 + pallets/communities/src/origin.rs | 39 ++++++++++++++----- pallets/communities/src/types.rs | 4 +- runtime/kreivo/Cargo.toml | 6 ++- runtime/kreivo/src/communities/governance.rs | 9 +++-- runtime/kreivo/src/communities/mod.rs | 12 +++--- runtime/kreivo/src/xcm_config.rs | 20 ++++++---- .../src/xcm_config/plurality_community.rs | 22 +++++++++++ 11 files changed, 116 insertions(+), 33 deletions(-) create mode 100644 runtime/kreivo/src/xcm_config/plurality_community.rs diff --git a/node/Cargo.toml b/node/Cargo.toml index b717f937..942e261d 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -101,6 +101,9 @@ polkadot-cli = { workspace = true } [features] default = ["kreivo-runtime"] +testnet = [ + "kreivo-runtime?/testnet" +] runtime-benchmarks = [ "cumulus-primitives-core/runtime-benchmarks", "frame-benchmarking/runtime-benchmarks", diff --git a/pallets/communities/Cargo.toml b/pallets/communities/Cargo.toml index 54b803db..7bb0ac8c 100644 --- a/pallets/communities/Cargo.toml +++ b/pallets/communities/Cargo.toml @@ -40,7 +40,8 @@ pallet-scheduler = { workspace = true } virto-common = { workspace = true, default-features = false, features = ["runtime"] } [features] -default = ["std", "xcm"] +default = ["std", "xcm", "testnet"] +testnet = [] std = [ "frame-benchmarking?/std", "frame-support/std", diff --git a/pallets/communities/src/lib.rs b/pallets/communities/src/lib.rs index 6d60810a..54a72ecb 100644 --- a/pallets/communities/src/lib.rs +++ b/pallets/communities/src/lib.rs @@ -142,13 +142,13 @@ pub mod pallet { use frame_support::{ dispatch::{DispatchResultWithPostInfo, GetDispatchInfo, PostDispatchInfo}, pallet_prelude::*, - traits::{fungible, fungibles, EnsureOrigin, IsSubType, Polling}, + traits::{fungible, fungibles, EnsureOrigin, IsSubType, OriginTrait, Polling}, Blake2_128Concat, Parameter, }; use frame_system::pallet_prelude::{OriginFor, *}; use sp_runtime::traits::{Dispatchable, StaticLookup}; use sp_std::prelude::Box; - use types::{PollIndexOf, RuntimeCallFor, *}; + use types::{PollIndexOf, RuntimeCallFor, RuntimeOriginFor, *}; const ONE: NonZeroU8 = NonZeroU8::MIN; #[pallet::pallet] @@ -196,13 +196,20 @@ pub mod pallet { /// The overarching call type. type RuntimeCall: Parameter - + Dispatchable + + Dispatchable, PostInfo = PostDispatchInfo> + GetDispatchInfo + From> + From> + IsSubType> + IsType<::RuntimeCall>; + /// The `RuntimeOrigin` type used by dispatchable calls. + type RuntimeOrigin: Into, RuntimeOriginFor>> + + From> + + From> + + Clone + + OriginTrait, AccountId = Self::AccountId>; + /// The overarching hold reason. type RuntimeHoldReason: From; @@ -524,5 +531,22 @@ pub mod pallet { let community_id = T::MemberMgmtOrigin::ensure_origin(origin)?; Self::do_dispatch_as_community_account(&community_id, *call) } + + /// Dispatch a callable as the community account + #[cfg(any(test, feature = "testnet"))] + #[pallet::call_index(12)] + #[pallet::weight({ + let di = call.get_dispatch_info(); + let weight = T::WeightInfo::dispatch_as_account() + .saturating_add(T::DbWeight::get().reads_writes(1, 1)) + .saturating_add(di.weight); + (weight, di.class) + })] + pub fn dispatch_as_origin(origin: OriginFor, call: Box>) -> DispatchResultWithPostInfo { + let community_id = T::MemberMgmtOrigin::ensure_origin(origin)?; + let origin = crate::Origin::::new(community_id); + let post = call.dispatch(origin.into()).map_err(|e| e.error)?; + Ok(post) + } } } diff --git a/pallets/communities/src/mock.rs b/pallets/communities/src/mock.rs index 75ce82f3..0011cb9b 100644 --- a/pallets/communities/src/mock.rs +++ b/pallets/communities/src/mock.rs @@ -420,6 +420,7 @@ impl pallet_communities::Config for Test { type MemberMgmtOrigin = EnsureCommunity; type RuntimeCall = RuntimeCall; + type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; type RuntimeHoldReason = RuntimeHoldReason; type WeightInfo = WeightInfo; diff --git a/pallets/communities/src/origin.rs b/pallets/communities/src/origin.rs index 8acc1447..1c259b17 100644 --- a/pallets/communities/src/origin.rs +++ b/pallets/communities/src/origin.rs @@ -1,5 +1,5 @@ use crate::{ - types::{CommunityIdOf, CommunityState::Active, MembershipIdOf}, + types::{CommunityIdOf, CommunityState::Active, MembershipIdOf, RuntimeOriginFor}, CommunityIdFor, Config, Info, Pallet, }; use core::marker::PhantomData; @@ -8,18 +8,18 @@ use frame_support::{ pallet_prelude::*, traits::{membership::GenericRank, EnsureOriginWithArg, OriginTrait}, }; -use sp_runtime::Permill; +use sp_runtime::{traits::TryConvert, Permill}; pub struct EnsureCommunity(PhantomData); -impl EnsureOrigin for EnsureCommunity +impl EnsureOrigin> for EnsureCommunity where - T::RuntimeOrigin: OriginTrait + Into, T::RuntimeOrigin>> + From>, + RuntimeOriginFor: OriginTrait + Into, RuntimeOriginFor>> + From>, T: Config, { type Success = T::CommunityId; - fn try_origin(o: T::RuntimeOrigin) -> Result { + fn try_origin(o: RuntimeOriginFor) -> Result> { use frame_system::RawOrigin::{None, Root}; if matches!(o.as_system_ref(), Some(Root) | Some(None)) { return Err(o); @@ -37,7 +37,7 @@ where } #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin() -> Result { + fn try_successful_origin() -> Result, ()> { use crate::BenchmarkHelper; Ok(RawOrigin::new(T::BenchmarkHelper::community_id()).into()) } @@ -45,14 +45,17 @@ where pub struct EnsureMember(PhantomData); -impl EnsureOriginWithArg> for EnsureMember +impl EnsureOriginWithArg, CommunityIdOf> for EnsureMember where T: Config, - T::RuntimeOrigin: OriginTrait + From>, + RuntimeOriginFor: OriginTrait + From>, { type Success = (); - fn try_origin(o: T::RuntimeOrigin, community_id: &CommunityIdOf) -> Result { + fn try_origin( + o: RuntimeOriginFor, + community_id: &CommunityIdOf, + ) -> Result> { use frame_system::RawOrigin::Signed; match o.clone().into() { @@ -68,7 +71,7 @@ where } #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin(_community_id: &CommunityIdOf) -> Result { + fn try_successful_origin(_community_id: &CommunityIdOf) -> Result, ()> { todo!("Find an account that is a member of this community"); } } @@ -117,6 +120,22 @@ pub enum DecisionMethod { Rank, } +#[cfg(feature = "xcm")] +impl TryConvert, xcm::v3::MultiLocation> for RawOrigin +where + T: Config, + RuntimeOriginFor: Into, RuntimeOriginFor>>, + xcm::v3::Junction: TryFrom>, +{ + fn try_convert(o: RuntimeOriginFor) -> Result> { + let Ok(community @ RawOrigin { .. }) = o.clone().into() else { + return Err(o); + }; + let j = xcm::v3::Junction::try_from(community).map_err(|_| o)?; + Ok(j.into()) + } +} + #[cfg(feature = "xcm")] impl TryFrom> for xcm::v3::Junction where diff --git a/pallets/communities/src/types.rs b/pallets/communities/src/types.rs index 07755b1e..cba98866 100644 --- a/pallets/communities/src/types.rs +++ b/pallets/communities/src/types.rs @@ -19,10 +19,10 @@ pub type VoteOf = Vote, AssetBalanceOf, NativeBalanceOf>; pub type DecisionMethodFor = DecisionMethod>; pub type PollIndexOf = <::Polls as Polling>>::Index; pub type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; -pub type PalletsOriginOf = - <::RuntimeOrigin as frame_support::traits::OriginTrait>::PalletsOrigin; +pub type PalletsOriginOf = <::RuntimeOrigin as frame_support::traits::OriginTrait>::PalletsOrigin; pub type MembershipIdOf = ::MembershipId; pub type RuntimeCallFor = ::RuntimeCall; +pub type RuntimeOriginFor = ::RuntimeOrigin; pub type SizedField = BoundedVec; pub type ConstSizedField = SizedField>; diff --git a/runtime/kreivo/Cargo.toml b/runtime/kreivo/Cargo.toml index d32af6da..17e53617 100644 --- a/runtime/kreivo/Cargo.toml +++ b/runtime/kreivo/Cargo.toml @@ -23,7 +23,7 @@ smallvec = { workspace = true } # Local pallet-payments = { workspace = true } -pallet-communities = { workspace = true } +pallet-communities = { workspace = true, features = ["xcm"] } # Local: Common virto-common = { workspace = true, default-features = false, features = ["runtime"] } @@ -253,3 +253,7 @@ try-runtime = [ "runtime-common/try-runtime", "sp-runtime/try-runtime", ] + +testnet = [ + "pallet-communities/testnet" +] \ No newline at end of file diff --git a/runtime/kreivo/src/communities/governance.rs b/runtime/kreivo/src/communities/governance.rs index 050b2500..745203c4 100644 --- a/runtime/kreivo/src/communities/governance.rs +++ b/runtime/kreivo/src/communities/governance.rs @@ -50,7 +50,7 @@ impl pallet_referenda_tracks::Config for Runtime { pub struct EnsureCommunityMember(PhantomData, PhantomData); -impl EnsureOriginWithArg> for EnsureCommunityMember +impl EnsureOriginWithArg, PalletsOriginOf> for EnsureCommunityMember where T: pallet_communities::Config + pallet_referenda::Config, T::Tracks: TracksInfo< @@ -62,7 +62,10 @@ where { type Success = T::AccountId; - fn try_origin(o: T::RuntimeOrigin, track_origin: &PalletsOriginOf) -> Result { + fn try_origin( + o: RuntimeOriginFor, + track_origin: &PalletsOriginOf, + ) -> Result> { use fc_traits_memberships::Inspect; use frame_system::RawOrigin::Signed; let community_id = T::Tracks::track_for(track_origin).map_err(|_| o.clone())?; @@ -80,7 +83,7 @@ where } #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin(_track_origin: &PalletsOriginOf) -> Result { + fn try_successful_origin(_track_origin: &PalletsOriginOf) -> Result, ()> { todo!() } } diff --git a/runtime/kreivo/src/communities/mod.rs b/runtime/kreivo/src/communities/mod.rs index b06e4613..ea0e54bd 100644 --- a/runtime/kreivo/src/communities/mod.rs +++ b/runtime/kreivo/src/communities/mod.rs @@ -4,7 +4,7 @@ use frame_support::{ pallet_prelude::{EnsureOrigin, PhantomData}, traits::OriginTrait, }; -use pallet_communities::origin::EnsureCommunity; +use pallet_communities::{origin::EnsureCommunity, types::RuntimeOriginFor}; use sp_runtime::traits::AccountIdConversion; use virto_common::{CommunityId, MembershipId}; @@ -41,14 +41,15 @@ parameter_types! { pub struct EnsureCommunityAccountId(PhantomData); -impl EnsureOrigin for EnsureCommunityAccountId +impl EnsureOrigin> for EnsureCommunityAccountId where - T::RuntimeOrigin: OriginTrait + From> + From>, + RuntimeOriginFor: + OriginTrait + From> + From>, T: pallet_communities::Config, { type Success = T::CommunityId; - fn try_origin(o: T::RuntimeOrigin) -> Result { + fn try_origin(o: RuntimeOriginFor) -> Result> { match o.clone().into() { Ok(frame_system::RawOrigin::Signed(account_id)) => { let (_, community_id) = PalletId::try_from_sub_account(&account_id).ok_or(o.clone())?; @@ -59,7 +60,7 @@ where } #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin() -> Result { + fn try_successful_origin() -> Result, ()> { Ok(Origin::new(T::BenchmarkHelper::community_id()).into()) } } @@ -78,6 +79,7 @@ impl pallet_communities::Config for Runtime { type Balances = Balances; type RuntimeCall = RuntimeCall; + type RuntimeOrigin = RuntimeOrigin; type RuntimeHoldReason = RuntimeHoldReason; type RuntimeEvent = RuntimeEvent; type WeightInfo = crate::weights::pallet_communities::WeightInfo; diff --git a/runtime/kreivo/src/xcm_config.rs b/runtime/kreivo/src/xcm_config.rs index 99590d41..04fd4b32 100644 --- a/runtime/kreivo/src/xcm_config.rs +++ b/runtime/kreivo/src/xcm_config.rs @@ -1,10 +1,9 @@ -use virto_common::AsFungibleAssetLocation; - use super::{ AccountId, AllPalletsWithSystem, Assets, Balance, Balances, FungibleAssetLocation, KreivoAssetsInstance, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, Treasury, WeightToFee, XcmpQueue, }; +use virto_common::AsFungibleAssetLocation; use crate::constants::locations::STATEMINE_PARA_ID; use frame_support::{ @@ -24,12 +23,15 @@ use xcm_builder::{ AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowTopLevelPaidExecutionFrom, ConvertedConcreteId, CurrencyAdapter, EnsureXcmOrigin, FungiblesAdapter, IsConcrete, LocalMint, MintLocation, NativeAsset, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, StartsWith, TakeWeightCredit, - UsingComponents, WeightInfoBounds, WithComputedOrigin, + SignedAccountId32AsNative, SovereignSignedViaLocation, StartsWith, TakeWeightCredit, UsingComponents, + WeightInfoBounds, WithComputedOrigin, }; use xcm_executor::traits::JustTry; use xcm_executor::XcmExecutor; +mod plurality_community; +use plurality_community::*; + parameter_types! { pub const RelayLocation: MultiLocation = MultiLocation::parent(); pub const RelayNetwork: Option = None; @@ -54,6 +56,8 @@ pub type LocationToAccountId = ( ParentIsPreset, // Sibling parachain origins convert to AccountId via the `ParaId::into`. SiblingParachainConvertsVia, + // Plurality origins convert to community AccountId via the `Communities::community_account`. + PluralityConvertsToCommunityAccountId, // Straight up local `AccountId32` origins just alias directly to `AccountId`. AccountId32Aliases, ); @@ -230,8 +234,8 @@ impl xcm_executor::Config for XcmConfig { type Aliasers = Nothing; } -/// No local origins on this chain are allowed to dispatch XCM sends/executions. -pub type LocalOriginToLocation = SignedToAccountId32; +/// Only communities are allowed to dispatch xcm messages +pub type ConvertCommunityOrigin = pallet_communities::Origin; /// The means for routing XCM messages which are not for local execution into /// the right message queues. @@ -244,9 +248,9 @@ pub type XcmRouter = ( impl pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type SendXcmOrigin = EnsureXcmOrigin; + type SendXcmOrigin = EnsureXcmOrigin; type XcmRouter = XcmRouter; - type ExecuteXcmOrigin = EnsureXcmOrigin; + type ExecuteXcmOrigin = EnsureXcmOrigin; type XcmExecuteFilter = Nothing; // ^ Disable dispatchable execute on the XCM pallet. type XcmExecutor = XcmExecutor; diff --git a/runtime/kreivo/src/xcm_config/plurality_community.rs b/runtime/kreivo/src/xcm_config/plurality_community.rs new file mode 100644 index 00000000..78ac43d5 --- /dev/null +++ b/runtime/kreivo/src/xcm_config/plurality_community.rs @@ -0,0 +1,22 @@ +use super::*; + +use crate::Communities; +use sp_runtime::SaturatedConversion; +use xcm::v3::{BodyId, Junction::Plurality}; +use xcm_executor::traits::ConvertLocation; + +pub struct PluralityConvertsToCommunityAccountId; +impl ConvertLocation for PluralityConvertsToCommunityAccountId { + fn convert_location(location: &MultiLocation) -> Option { + log::trace!("Attempting to convert {:?} into AccountId if plurality", location); + match location { + MultiLocation { + parents: 0, + interior: X1(Plurality { + id: BodyId::Index(id), .. + }), + } => Some(Communities::community_account(&(*id).saturated_into())), + _ => None, + } + } +}