Skip to content

Commit

Permalink
Allow communities to send XCM (#360)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
olanod and pandres95 authored Apr 13, 2024
1 parent 631cb18 commit 12932a5
Show file tree
Hide file tree
Showing 11 changed files with 116 additions and 33 deletions.
3 changes: 3 additions & 0 deletions node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
3 changes: 2 additions & 1 deletion pallets/communities/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
30 changes: 27 additions & 3 deletions pallets/communities/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -196,13 +196,20 @@ pub mod pallet {

/// The overarching call type.
type RuntimeCall: Parameter
+ Dispatchable<RuntimeOrigin = Self::RuntimeOrigin, PostInfo = PostDispatchInfo>
+ Dispatchable<RuntimeOrigin = RuntimeOriginFor<Self>, PostInfo = PostDispatchInfo>
+ GetDispatchInfo
+ From<Call<Self>>
+ From<frame_system::Call<Self>>
+ IsSubType<Call<Self>>
+ IsType<<Self as frame_system::Config>::RuntimeCall>;

/// The `RuntimeOrigin` type used by dispatchable calls.
type RuntimeOrigin: Into<Result<frame_system::Origin<Self>, RuntimeOriginFor<Self>>>
+ From<frame_system::Origin<Self>>
+ From<Origin<Self>>
+ Clone
+ OriginTrait<Call = RuntimeCallFor<Self>, AccountId = Self::AccountId>;

/// The overarching hold reason.
type RuntimeHoldReason: From<HoldReason>;

Expand Down Expand Up @@ -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<T>, call: Box<RuntimeCallFor<T>>) -> DispatchResultWithPostInfo {
let community_id = T::MemberMgmtOrigin::ensure_origin(origin)?;
let origin = crate::Origin::<T>::new(community_id);
let post = call.dispatch(origin.into()).map_err(|e| e.error)?;
Ok(post)
}
}
}
1 change: 1 addition & 0 deletions pallets/communities/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ impl pallet_communities::Config for Test {
type MemberMgmtOrigin = EnsureCommunity<Self>;

type RuntimeCall = RuntimeCall;
type RuntimeOrigin = RuntimeOrigin;
type RuntimeEvent = RuntimeEvent;
type RuntimeHoldReason = RuntimeHoldReason;
type WeightInfo = WeightInfo;
Expand Down
39 changes: 29 additions & 10 deletions pallets/communities/src/origin.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<T>(PhantomData<T>);

impl<T> EnsureOrigin<T::RuntimeOrigin> for EnsureCommunity<T>
impl<T> EnsureOrigin<RuntimeOriginFor<T>> for EnsureCommunity<T>
where
T::RuntimeOrigin: OriginTrait + Into<Result<RawOrigin<T>, T::RuntimeOrigin>> + From<RawOrigin<T>>,
RuntimeOriginFor<T>: OriginTrait + Into<Result<RawOrigin<T>, RuntimeOriginFor<T>>> + From<RawOrigin<T>>,
T: Config,
{
type Success = T::CommunityId;

fn try_origin(o: T::RuntimeOrigin) -> Result<Self::Success, T::RuntimeOrigin> {
fn try_origin(o: RuntimeOriginFor<T>) -> Result<Self::Success, RuntimeOriginFor<T>> {
use frame_system::RawOrigin::{None, Root};
if matches!(o.as_system_ref(), Some(Root) | Some(None)) {
return Err(o);
Expand All @@ -37,22 +37,25 @@ where
}

#[cfg(feature = "runtime-benchmarks")]
fn try_successful_origin() -> Result<T::RuntimeOrigin, ()> {
fn try_successful_origin() -> Result<RuntimeOriginFor<T>, ()> {
use crate::BenchmarkHelper;
Ok(RawOrigin::new(T::BenchmarkHelper::community_id()).into())
}
}

pub struct EnsureMember<T>(PhantomData<T>);

impl<T> EnsureOriginWithArg<T::RuntimeOrigin, CommunityIdOf<T>> for EnsureMember<T>
impl<T> EnsureOriginWithArg<RuntimeOriginFor<T>, CommunityIdOf<T>> for EnsureMember<T>
where
T: Config,
T::RuntimeOrigin: OriginTrait + From<frame_system::Origin<T>>,
RuntimeOriginFor<T>: OriginTrait + From<frame_system::Origin<T>>,
{
type Success = ();

fn try_origin(o: T::RuntimeOrigin, community_id: &CommunityIdOf<T>) -> Result<Self::Success, T::RuntimeOrigin> {
fn try_origin(
o: RuntimeOriginFor<T>,
community_id: &CommunityIdOf<T>,
) -> Result<Self::Success, RuntimeOriginFor<T>> {
use frame_system::RawOrigin::Signed;

match o.clone().into() {
Expand All @@ -68,7 +71,7 @@ where
}

#[cfg(feature = "runtime-benchmarks")]
fn try_successful_origin(_community_id: &CommunityIdOf<T>) -> Result<T::RuntimeOrigin, ()> {
fn try_successful_origin(_community_id: &CommunityIdOf<T>) -> Result<RuntimeOriginFor<T>, ()> {
todo!("Find an account that is a member of this community");
}
}
Expand Down Expand Up @@ -117,6 +120,22 @@ pub enum DecisionMethod<AssetId> {
Rank,
}

#[cfg(feature = "xcm")]
impl<T> TryConvert<RuntimeOriginFor<T>, xcm::v3::MultiLocation> for RawOrigin<T>
where
T: Config,
RuntimeOriginFor<T>: Into<Result<RawOrigin<T>, RuntimeOriginFor<T>>>,
xcm::v3::Junction: TryFrom<RawOrigin<T>>,
{
fn try_convert(o: RuntimeOriginFor<T>) -> Result<xcm::v3::MultiLocation, RuntimeOriginFor<T>> {
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<T> TryFrom<RawOrigin<T>> for xcm::v3::Junction
where
Expand Down
4 changes: 2 additions & 2 deletions pallets/communities/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ pub type VoteOf<T> = Vote<AssetIdOf<T>, AssetBalanceOf<T>, NativeBalanceOf<T>>;
pub type DecisionMethodFor<T> = DecisionMethod<AssetIdOf<T>>;
pub type PollIndexOf<T> = <<T as Config>::Polls as Polling<Tally<T>>>::Index;
pub type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
pub type PalletsOriginOf<T> =
<<T as frame_system::Config>::RuntimeOrigin as frame_support::traits::OriginTrait>::PalletsOrigin;
pub type PalletsOriginOf<T> = <<T as Config>::RuntimeOrigin as frame_support::traits::OriginTrait>::PalletsOrigin;
pub type MembershipIdOf<T> = <T as Config>::MembershipId;
pub type RuntimeCallFor<T> = <T as Config>::RuntimeCall;
pub type RuntimeOriginFor<T> = <T as Config>::RuntimeOrigin;

pub type SizedField<S> = BoundedVec<u8, S>;
pub type ConstSizedField<const S: u32> = SizedField<ConstU32<S>>;
Expand Down
6 changes: 5 additions & 1 deletion runtime/kreivo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down Expand Up @@ -253,3 +253,7 @@ try-runtime = [
"runtime-common/try-runtime",
"sp-runtime/try-runtime",
]

testnet = [
"pallet-communities/testnet"
]
9 changes: 6 additions & 3 deletions runtime/kreivo/src/communities/governance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl pallet_referenda_tracks::Config<CommunityTracksInstance> for Runtime {

pub struct EnsureCommunityMember<T, I: 'static = ()>(PhantomData<T>, PhantomData<I>);

impl<T, I> EnsureOriginWithArg<T::RuntimeOrigin, PalletsOriginOf<T>> for EnsureCommunityMember<T, I>
impl<T, I> EnsureOriginWithArg<RuntimeOriginFor<T>, PalletsOriginOf<T>> for EnsureCommunityMember<T, I>
where
T: pallet_communities::Config + pallet_referenda::Config<I>,
T::Tracks: TracksInfo<
Expand All @@ -62,7 +62,10 @@ where
{
type Success = T::AccountId;

fn try_origin(o: T::RuntimeOrigin, track_origin: &PalletsOriginOf<T>) -> Result<Self::Success, T::RuntimeOrigin> {
fn try_origin(
o: RuntimeOriginFor<T>,
track_origin: &PalletsOriginOf<T>,
) -> Result<Self::Success, RuntimeOriginFor<T>> {
use fc_traits_memberships::Inspect;
use frame_system::RawOrigin::Signed;
let community_id = T::Tracks::track_for(track_origin).map_err(|_| o.clone())?;
Expand All @@ -80,7 +83,7 @@ where
}

#[cfg(feature = "runtime-benchmarks")]
fn try_successful_origin(_track_origin: &PalletsOriginOf<T>) -> Result<T::RuntimeOrigin, ()> {
fn try_successful_origin(_track_origin: &PalletsOriginOf<T>) -> Result<RuntimeOriginFor<T>, ()> {
todo!()
}
}
Expand Down
12 changes: 7 additions & 5 deletions runtime/kreivo/src/communities/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -41,14 +41,15 @@ parameter_types! {

pub struct EnsureCommunityAccountId<T>(PhantomData<T>);

impl<T> EnsureOrigin<T::RuntimeOrigin> for EnsureCommunityAccountId<T>
impl<T> EnsureOrigin<RuntimeOriginFor<T>> for EnsureCommunityAccountId<T>
where
T::RuntimeOrigin: OriginTrait + From<frame_system::RawOrigin<T::AccountId>> + From<pallet_communities::Origin<T>>,
RuntimeOriginFor<T>:
OriginTrait + From<frame_system::RawOrigin<T::AccountId>> + From<pallet_communities::Origin<T>>,
T: pallet_communities::Config,
{
type Success = T::CommunityId;

fn try_origin(o: T::RuntimeOrigin) -> Result<Self::Success, T::RuntimeOrigin> {
fn try_origin(o: RuntimeOriginFor<T>) -> Result<Self::Success, RuntimeOriginFor<T>> {
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())?;
Expand All @@ -59,7 +60,7 @@ where
}

#[cfg(feature = "runtime-benchmarks")]
fn try_successful_origin() -> Result<T::RuntimeOrigin, ()> {
fn try_successful_origin() -> Result<RuntimeOriginFor<T>, ()> {
Ok(Origin::new(T::BenchmarkHelper::community_id()).into())
}
}
Expand All @@ -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<Runtime>;
Expand Down
20 changes: 12 additions & 8 deletions runtime/kreivo/src/xcm_config.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand All @@ -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<NetworkId> = None;
Expand All @@ -54,6 +56,8 @@ pub type LocationToAccountId = (
ParentIsPreset<AccountId>,
// Sibling parachain origins convert to AccountId via the `ParaId::into`.
SiblingParachainConvertsVia<Sibling, AccountId>,
// Plurality origins convert to community AccountId via the `Communities::community_account`.
PluralityConvertsToCommunityAccountId,
// Straight up local `AccountId32` origins just alias directly to `AccountId`.
AccountId32Aliases<RelayNetwork, AccountId>,
);
Expand Down Expand Up @@ -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<RuntimeOrigin, AccountId, RelayNetwork>;
/// Only communities are allowed to dispatch xcm messages
pub type ConvertCommunityOrigin = pallet_communities::Origin<Runtime>;

/// The means for routing XCM messages which are not for local execution into
/// the right message queues.
Expand All @@ -244,9 +248,9 @@ pub type XcmRouter = (

impl pallet_xcm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, ConvertCommunityOrigin>;
type XcmRouter = XcmRouter;
type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, ConvertCommunityOrigin>;
type XcmExecuteFilter = Nothing;
// ^ Disable dispatchable execute on the XCM pallet.
type XcmExecutor = XcmExecutor<XcmConfig>;
Expand Down
22 changes: 22 additions & 0 deletions runtime/kreivo/src/xcm_config/plurality_community.rs
Original file line number Diff line number Diff line change
@@ -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<AccountId> for PluralityConvertsToCommunityAccountId {
fn convert_location(location: &MultiLocation) -> Option<AccountId> {
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,
}
}
}

0 comments on commit 12932a5

Please sign in to comment.