diff --git a/substrate/frame/collective/src/lib.rs b/substrate/frame/collective/src/lib.rs index f904b42af0..fc4ab0c4f1 100644 --- a/substrate/frame/collective/src/lib.rs +++ b/substrate/frame/collective/src/lib.rs @@ -53,9 +53,10 @@ use frame_support::{ DispatchError, DispatchResult, DispatchResultWithPostInfo, Dispatchable, GetDispatchInfo, Pays, PostDispatchInfo, }, - ensure, + ensure, impl_ensure_origin_with_arg_ignoring_arg, traits::{ - Backing, ChangeMembers, EnsureOrigin, Get, GetBacking, InitializeMembers, StorageVersion, + Backing, ChangeMembers, EnsureOrigin, EnsureOriginWithArg, Get, GetBacking, + InitializeMembers, StorageVersion, }, weights::Weight, }; @@ -1154,6 +1155,12 @@ impl< } } +impl_ensure_origin_with_arg_ignoring_arg! { + impl< { O: .., I: 'static, AccountId: Decode, T } > + EnsureOriginWithArg for EnsureMember + {} +} + pub struct EnsureMembers(PhantomData<(AccountId, I)>); impl< O: Into, O>> + From>, @@ -1176,6 +1183,12 @@ impl< } } +impl_ensure_origin_with_arg_ignoring_arg! { + impl< { O: .., I: 'static, const N: u32, AccountId, T } > + EnsureOriginWithArg for EnsureMembers + {} +} + pub struct EnsureProportionMoreThan( PhantomData<(AccountId, I)>, ); @@ -1201,6 +1214,12 @@ impl< } } +impl_ensure_origin_with_arg_ignoring_arg! { + impl< { O: .., I: 'static, const N: u32, const D: u32, AccountId, T } > + EnsureOriginWithArg for EnsureProportionMoreThan + {} +} + pub struct EnsureProportionAtLeast( PhantomData<(AccountId, I)>, ); @@ -1225,3 +1244,9 @@ impl< Ok(O::from(RawOrigin::Members(0u32, 0u32))) } } + +impl_ensure_origin_with_arg_ignoring_arg! { + impl< { O: .., I: 'static, const N: u32, const D: u32, AccountId, T } > + EnsureOriginWithArg for EnsureProportionAtLeast + {} +} diff --git a/substrate/frame/core-fellowship/src/lib.rs b/substrate/frame/core-fellowship/src/lib.rs index 9ca13db172..ba02627ec1 100644 --- a/substrate/frame/core-fellowship/src/lib.rs +++ b/substrate/frame/core-fellowship/src/lib.rs @@ -65,8 +65,10 @@ use sp_std::{marker::PhantomData, prelude::*}; use frame_support::{ dispatch::DispatchResultWithPostInfo, - ensure, - traits::{tokens::Balance as BalanceTrait, EnsureOrigin, Get, RankedMembers}, + ensure, impl_ensure_origin_with_arg_ignoring_arg, + traits::{ + tokens::Balance as BalanceTrait, EnsureOrigin, EnsureOriginWithArg, Get, RankedMembers, + }, BoundedVec, RuntimeDebug, }; @@ -570,7 +572,7 @@ impl, I: 'static, const MIN_RANK: u16> EnsureOrigin Result { - let who = frame_system::EnsureSigned::try_origin(o)?; + let who = as EnsureOrigin<_>>::try_origin(o)?; match T::Members::rank_of(&who) { Some(rank) if rank >= MIN_RANK && Member::::contains_key(&who) => Ok(who), _ => Err(frame_system::RawOrigin::Signed(who).into()), @@ -591,3 +593,9 @@ impl, I: 'static, const MIN_RANK: u16> EnsureOrigin, I: 'static, const MIN_RANK: u16, A } > + EnsureOriginWithArg for EnsureInducted + {} +} diff --git a/substrate/frame/ranked-collective/src/lib.rs b/substrate/frame/ranked-collective/src/lib.rs index 6296403d2a..6fe9fc221a 100644 --- a/substrate/frame/ranked-collective/src/lib.rs +++ b/substrate/frame/ranked-collective/src/lib.rs @@ -53,8 +53,8 @@ use sp_std::{marker::PhantomData, prelude::*}; use frame_support::{ codec::{Decode, Encode, MaxEncodedLen}, dispatch::{DispatchError, DispatchResultWithPostInfo, PostDispatchInfo}, - ensure, - traits::{EnsureOrigin, PollStatus, Polling, RankedMembers, VoteTally}, + ensure, impl_ensure_origin_with_arg_ignoring_arg, + traits::{EnsureOrigin, EnsureOriginWithArg, PollStatus, Polling, RankedMembers, VoteTally}, CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, }; @@ -263,7 +263,7 @@ impl, I: 'static, const MIN_RANK: u16> EnsureOrigin Result { - let who = frame_system::EnsureSigned::try_origin(o)?; + let who = as EnsureOrigin<_>>::try_origin(o)?; match Members::::get(&who) { Some(MemberRecord { rank, .. }) if rank >= MIN_RANK => Ok(rank), _ => Err(frame_system::RawOrigin::Signed(who).into()), @@ -272,7 +272,36 @@ impl, I: 'static, const MIN_RANK: u16> EnsureOrigin Result { - EnsureRankedMember::::try_successful_origin() + as EnsureOrigin<_>>::try_successful_origin() + } +} + +impl_ensure_origin_with_arg_ignoring_arg! { + impl<{ T: Config, I: 'static, const MIN_RANK: u16, A }> + EnsureOriginWithArg for EnsureRanked + {} +} + +/// Guard to ensure that the given origin is a member of the collective. The rank of the member is +/// the `Success` value. +pub struct EnsureOfRank(PhantomData<(T, I)>); +impl, I: 'static> EnsureOriginWithArg for EnsureOfRank { + type Success = (T::AccountId, Rank); + + fn try_origin(o: T::RuntimeOrigin, min_rank: &Rank) -> Result { + let who = as EnsureOrigin<_>>::try_origin(o)?; + match Members::::get(&who) { + Some(MemberRecord { rank, .. }) if rank >= *min_rank => Ok((who, rank)), + _ => Err(frame_system::RawOrigin::Signed(who).into()), + } + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(min_rank: &Rank) -> Result { + let who = frame_benchmarking::account::("successful_origin", 0, 0); + crate::Pallet::::do_add_member_to_rank(who.clone(), *min_rank) + .expect("Could not add members for benchmarks"); + Ok(frame_system::RawOrigin::Signed(who).into()) } } @@ -285,7 +314,7 @@ impl, I: 'static, const MIN_RANK: u16> EnsureOrigin Result { - let who = frame_system::EnsureSigned::try_origin(o)?; + let who = as EnsureOrigin<_>>::try_origin(o)?; match Members::::get(&who) { Some(MemberRecord { rank, .. }) if rank >= MIN_RANK => Ok(who), _ => Err(frame_system::RawOrigin::Signed(who).into()), @@ -294,10 +323,16 @@ impl, I: 'static, const MIN_RANK: u16> EnsureOrigin Result { - EnsureRankedMember::::try_successful_origin() + as EnsureOrigin<_>>::try_successful_origin() } } +impl_ensure_origin_with_arg_ignoring_arg! { + impl<{ T: Config, I: 'static, const MIN_RANK: u16, A }> + EnsureOriginWithArg for EnsureMember + {} +} + /// Guard to ensure that the given origin is a member of the collective. The pair of both the /// account ID and the rank of the member is the `Success` value. pub struct EnsureRankedMember(PhantomData<(T, I)>); @@ -307,7 +342,7 @@ impl, I: 'static, const MIN_RANK: u16> EnsureOrigin Result { - let who = frame_system::EnsureSigned::try_origin(o)?; + let who = as EnsureOrigin<_>>::try_origin(o)?; match Members::::get(&who) { Some(MemberRecord { rank, .. }) if rank >= MIN_RANK => Ok((who, rank)), _ => Err(frame_system::RawOrigin::Signed(who).into()), @@ -323,6 +358,12 @@ impl, I: 'static, const MIN_RANK: u16> EnsureOrigin, I: 'static, const MIN_RANK: u16, A }> + EnsureOriginWithArg for EnsureRankedMember + {} +} + #[frame_support::pallet] pub mod pallet { use super::*; @@ -708,6 +749,15 @@ pub mod pallet { } Ok(()) } + + /// Determine the rank of the account behind the `Signed` origin `o`, `None` if the account + /// is unknown to this collective or `o` is not `Signed`. + pub fn as_rank( + o: &::PalletsOrigin, + ) -> Option { + use frame_support::traits::CallerTrait; + o.as_signed().and_then(Self::rank_of) + } } impl, I: 'static> RankedMembers for Pallet { diff --git a/substrate/frame/ranked-collective/src/tests.rs b/substrate/frame/ranked-collective/src/tests.rs index 04519bc0f8..338dcaaced 100644 --- a/substrate/frame/ranked-collective/src/tests.rs +++ b/substrate/frame/ranked-collective/src/tests.rs @@ -464,34 +464,52 @@ fn ensure_ranked_works() { type Rank2 = EnsureRanked; type Rank3 = EnsureRanked; type Rank4 = EnsureRanked; - assert_eq!(Rank1::try_origin(RuntimeOrigin::signed(1)).unwrap(), 1); - assert_eq!(Rank1::try_origin(RuntimeOrigin::signed(2)).unwrap(), 2); - assert_eq!(Rank1::try_origin(RuntimeOrigin::signed(3)).unwrap(), 3); + assert_eq!(>::try_origin(RuntimeOrigin::signed(1)).unwrap(), 1); + assert_eq!(>::try_origin(RuntimeOrigin::signed(2)).unwrap(), 2); + assert_eq!(>::try_origin(RuntimeOrigin::signed(3)).unwrap(), 3); assert_eq!( - Rank2::try_origin(RuntimeOrigin::signed(1)).unwrap_err().as_signed().unwrap(), + >::try_origin(RuntimeOrigin::signed(1)) + .unwrap_err() + .into_signer() + .unwrap(), 1 ); - assert_eq!(Rank2::try_origin(RuntimeOrigin::signed(2)).unwrap(), 2); - assert_eq!(Rank2::try_origin(RuntimeOrigin::signed(3)).unwrap(), 3); + assert_eq!(>::try_origin(RuntimeOrigin::signed(2)).unwrap(), 2); + assert_eq!(>::try_origin(RuntimeOrigin::signed(3)).unwrap(), 3); assert_eq!( - Rank3::try_origin(RuntimeOrigin::signed(1)).unwrap_err().as_signed().unwrap(), + >::try_origin(RuntimeOrigin::signed(1)) + .unwrap_err() + .into_signer() + .unwrap(), 1 ); assert_eq!( - Rank3::try_origin(RuntimeOrigin::signed(2)).unwrap_err().as_signed().unwrap(), + >::try_origin(RuntimeOrigin::signed(2)) + .unwrap_err() + .into_signer() + .unwrap(), 2 ); - assert_eq!(Rank3::try_origin(RuntimeOrigin::signed(3)).unwrap(), 3); + assert_eq!(>::try_origin(RuntimeOrigin::signed(3)).unwrap(), 3); assert_eq!( - Rank4::try_origin(RuntimeOrigin::signed(1)).unwrap_err().as_signed().unwrap(), + >::try_origin(RuntimeOrigin::signed(1)) + .unwrap_err() + .into_signer() + .unwrap(), 1 ); assert_eq!( - Rank4::try_origin(RuntimeOrigin::signed(2)).unwrap_err().as_signed().unwrap(), + >::try_origin(RuntimeOrigin::signed(2)) + .unwrap_err() + .into_signer() + .unwrap(), 2 ); assert_eq!( - Rank4::try_origin(RuntimeOrigin::signed(3)).unwrap_err().as_signed().unwrap(), + >::try_origin(RuntimeOrigin::signed(3)) + .unwrap_err() + .into_signer() + .unwrap(), 3 ); }); diff --git a/substrate/frame/referenda/src/benchmarking.rs b/substrate/frame/referenda/src/benchmarking.rs index 288c65feae..4c332a3048 100644 --- a/substrate/frame/referenda/src/benchmarking.rs +++ b/substrate/frame/referenda/src/benchmarking.rs @@ -26,7 +26,7 @@ use frame_benchmarking::v1::{ use frame_support::{ assert_ok, dispatch::UnfilteredDispatchable, - traits::{Bounded, Currency, EnsureOrigin}, + traits::{Bounded, Currency, EnsureOrigin, EnsureOriginWithArg}, }; use frame_system::RawOrigin; use sp_runtime::traits::Bounded as ArithBounded; @@ -196,7 +196,7 @@ fn is_not_confirming, I: 'static>(index: ReferendumIndex) -> bool { benchmarks_instance_pallet! { submit { let origin = - T::SubmitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()).map_err(|_| BenchmarkError::Weightless)?; if let Ok(caller) = frame_system::ensure_signed(origin.clone()) { T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); whitelist_account!(caller); @@ -213,7 +213,7 @@ benchmarks_instance_pallet! { place_decision_deposit_preparing { let origin = - T::SubmitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()).map_err(|_| BenchmarkError::Weightless)?; let index = create_referendum::(origin.clone()); }: place_decision_deposit(origin, index) verify { @@ -222,7 +222,7 @@ benchmarks_instance_pallet! { place_decision_deposit_queued { let origin = - T::SubmitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()).map_err(|_| BenchmarkError::Weightless)?; let index = create_referendum::(origin.clone()); fill_queue::(origin.clone(), index, 1, 90); }: place_decision_deposit(origin, index) @@ -234,7 +234,7 @@ benchmarks_instance_pallet! { place_decision_deposit_not_queued { let origin = - T::SubmitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()).map_err(|_| BenchmarkError::Weightless)?; let index = create_referendum::(origin.clone()); fill_queue::(origin.clone(), index, 0, 90); let track = Referenda::::ensure_ongoing(index).unwrap().track; @@ -248,7 +248,7 @@ benchmarks_instance_pallet! { place_decision_deposit_passing { let origin = - T::SubmitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()).map_err(|_| BenchmarkError::Weightless)?; let index = create_referendum::(origin.clone()); skip_prepare_period::(index); make_passing::(index); @@ -259,7 +259,7 @@ benchmarks_instance_pallet! { place_decision_deposit_failing { let origin = - T::SubmitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()).map_err(|_| BenchmarkError::Weightless)?; let index = create_referendum::(origin.clone()); skip_prepare_period::(index); }: place_decision_deposit(origin, index) @@ -269,7 +269,7 @@ benchmarks_instance_pallet! { refund_decision_deposit { let origin = - T::SubmitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()).map_err(|_| BenchmarkError::Weightless)?; let index = create_referendum::(origin.clone()); place_deposit::(index); assert_ok!(Referenda::::cancel( @@ -284,7 +284,7 @@ benchmarks_instance_pallet! { refund_submission_deposit { let origin = - T::SubmitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()).map_err(|_| BenchmarkError::Weightless)?; let index = create_referendum::(origin.clone()); let caller = frame_system::ensure_signed(origin.clone()).unwrap(); let balance = T::Currency::free_balance(&caller); @@ -303,7 +303,7 @@ benchmarks_instance_pallet! { } cancel { - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); let index = create_referendum::(origin); place_deposit::(index); @@ -315,7 +315,7 @@ benchmarks_instance_pallet! { } kill { - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); let index = create_referendum::(origin); place_deposit::(index); @@ -327,7 +327,7 @@ benchmarks_instance_pallet! { } one_fewer_deciding_queue_empty { - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); let index = create_referendum::(origin); place_deposit::(index); @@ -346,7 +346,7 @@ benchmarks_instance_pallet! { } one_fewer_deciding_failing { - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); let index = create_referendum::(origin.clone()); // No spaces free in the queue. @@ -371,7 +371,7 @@ benchmarks_instance_pallet! { } one_fewer_deciding_passing { - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); let index = create_referendum::(origin.clone()); // No spaces free in the queue. @@ -396,7 +396,7 @@ benchmarks_instance_pallet! { } nudge_referendum_requeued_insertion { - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); // First create our referendum and place the deposit. It will be failing. let index = create_referendum::(origin.clone()); @@ -419,7 +419,7 @@ benchmarks_instance_pallet! { } nudge_referendum_requeued_slide { - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); // First create our referendum and place the deposit. It will be failing. let index = create_referendum::(origin.clone()); @@ -447,7 +447,7 @@ benchmarks_instance_pallet! { // free and this failing. It would result in `QUEUE_SIZE - 1` items being shifted for the // insertion at the beginning. - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); // First create our referendum and place the deposit. It will be failing. let index = create_referendum::(origin.clone()); @@ -466,7 +466,7 @@ benchmarks_instance_pallet! { } nudge_referendum_not_queued { - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); // First create our referendum and place the deposit. It will be failing. let index = create_referendum::(origin.clone()); @@ -485,7 +485,7 @@ benchmarks_instance_pallet! { } nudge_referendum_no_deposit { - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); let index = create_referendum::(origin); skip_prepare_period::(index); @@ -496,7 +496,7 @@ benchmarks_instance_pallet! { } nudge_referendum_preparing { - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); let index = create_referendum::(origin); place_deposit::(index); @@ -507,7 +507,7 @@ benchmarks_instance_pallet! { } nudge_referendum_timed_out { - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); let index = create_referendum::(origin); skip_timeout_period::(index); @@ -518,7 +518,7 @@ benchmarks_instance_pallet! { } nudge_referendum_begin_deciding_failing { - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); let index = create_referendum::(origin); place_deposit::(index); @@ -529,7 +529,7 @@ benchmarks_instance_pallet! { } nudge_referendum_begin_deciding_passing { - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); let index = create_referendum::(origin); place_deposit::(index); @@ -541,7 +541,7 @@ benchmarks_instance_pallet! { } nudge_referendum_begin_confirming { - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); let index = create_referendum::(origin); place_deposit::(index); @@ -555,7 +555,7 @@ benchmarks_instance_pallet! { } nudge_referendum_end_confirming { - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); let index = create_referendum::(origin); place_deposit::(index); @@ -570,7 +570,7 @@ benchmarks_instance_pallet! { } nudge_referendum_continue_not_confirming { - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); let index = create_referendum::(origin); place_deposit::(index); @@ -586,7 +586,7 @@ benchmarks_instance_pallet! { } nudge_referendum_continue_confirming { - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); let index = create_referendum::(origin); place_deposit::(index); @@ -601,7 +601,7 @@ benchmarks_instance_pallet! { } nudge_referendum_approved { - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); let index = create_referendum::(origin); place_deposit::(index); @@ -616,7 +616,7 @@ benchmarks_instance_pallet! { } nudge_referendum_rejected { - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); let index = create_referendum::(origin); place_deposit::(index); @@ -632,7 +632,7 @@ benchmarks_instance_pallet! { set_some_metadata { use sp_std::borrow::Cow; - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); let index = create_referendum::(origin.clone()); let hash = T::Preimages::note(Cow::from(vec![5, 6])).unwrap(); @@ -643,7 +643,7 @@ benchmarks_instance_pallet! { clear_metadata { use sp_std::borrow::Cow; - let origin = T::SubmitOrigin::try_successful_origin() + let origin = T::SubmitOrigin::try_successful_origin(&RawOrigin::Root.into()) .expect("SubmitOrigin has no successful origin required for the benchmark"); let index = create_referendum::(origin.clone()); let hash = T::Preimages::note(Cow::from(vec![6, 7, 8])).unwrap(); diff --git a/substrate/frame/referenda/src/lib.rs b/substrate/frame/referenda/src/lib.rs index dd352d0af4..3756257c33 100644 --- a/substrate/frame/referenda/src/lib.rs +++ b/substrate/frame/referenda/src/lib.rs @@ -138,7 +138,7 @@ const ASSEMBLY_ID: LockIdentifier = *b"assembly"; #[frame_support::pallet] pub mod pallet { use super::*; - use frame_support::pallet_prelude::*; + use frame_support::{pallet_prelude::*, traits::EnsureOriginWithArg}; use frame_system::pallet_prelude::*; /// The current storage version. @@ -167,7 +167,11 @@ pub mod pallet { type Currency: ReservableCurrency; // Origins and unbalances. /// Origin from which proposals may be submitted. - type SubmitOrigin: EnsureOrigin; + type SubmitOrigin: EnsureOriginWithArg< + Self::RuntimeOrigin, + PalletsOriginOf, + Success = Self::AccountId, + >; /// Origin from which any vote may be cancelled. type CancelOrigin: EnsureOrigin; /// Origin from which any vote may be killed. @@ -430,7 +434,8 @@ pub mod pallet { proposal: BoundedCallOf, enactment_moment: DispatchTime, ) -> DispatchResult { - let who = T::SubmitOrigin::ensure_origin(origin)?; + let proposal_origin = *proposal_origin; + let who = T::SubmitOrigin::ensure_origin(origin, &proposal_origin)?; let track = T::Tracks::track_for(&proposal_origin).map_err(|_| Error::::NoTrack)?; @@ -445,7 +450,7 @@ pub mod pallet { T::Preimages::bound(CallOf::::from(Call::nudge_referendum { index }))?; let status = ReferendumStatus { track, - origin: *proposal_origin, + origin: proposal_origin, proposal: proposal.clone(), enactment: enactment_moment, submitted: now, diff --git a/substrate/frame/society/src/lib.rs b/substrate/frame/society/src/lib.rs index d92bee48d6..4ac853ce8e 100644 --- a/substrate/frame/society/src/lib.rs +++ b/substrate/frame/society/src/lib.rs @@ -252,10 +252,11 @@ mod mock; mod tests; use frame_support::{ + impl_ensure_origin_with_arg_ignoring_arg, pallet_prelude::*, traits::{ - BalanceStatus, ChangeMembers, Currency, EnsureOrigin, ExistenceRequirement::AllowDeath, - Imbalance, OnUnbalanced, Randomness, ReservableCurrency, + BalanceStatus, ChangeMembers, Currency, EnsureOrigin, EnsureOriginWithArg, + ExistenceRequirement::AllowDeath, Imbalance, OnUnbalanced, Randomness, ReservableCurrency, }, PalletId, }; @@ -1181,6 +1182,12 @@ impl EnsureOrigin for EnsureFounder { } } +impl_ensure_origin_with_arg_ignoring_arg! { + impl<{ T: Config, A }> + EnsureOriginWithArg for EnsureFounder + {} +} + /// Pick an item at pseudo-random from the slice, given the `rng`. `None` iff the slice is empty. fn pick_item<'a, R: RngCore, T>(rng: &mut R, items: &'a [T]) -> Option<&'a T> { if items.is_empty() { diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs index e48a70a9df..0eaa73f603 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs @@ -418,6 +418,34 @@ fn expand_origin_pallet_conversions( } } } + + #attr + impl<'a> TryFrom<&'a OriginCaller> for &'a #pallet_origin { + type Error = (); + fn try_from( + x: &'a OriginCaller, + ) -> #scrate::sp_std::result::Result<&'a #pallet_origin, ()> { + if let OriginCaller::#variant_name(l) = x { + Ok(&l) + } else { + Err(()) + } + } + } + + #attr + impl<'a> TryFrom<&'a RuntimeOrigin> for &'a #pallet_origin { + type Error = (); + fn try_from( + x: &'a RuntimeOrigin, + ) -> #scrate::sp_std::result::Result<&'a #pallet_origin, ()> { + if let OriginCaller::#variant_name(l) = &x.caller { + Ok(&l) + } else { + Err(()) + } + } + } } } diff --git a/substrate/frame/support/src/dispatch.rs b/substrate/frame/support/src/dispatch.rs index 47c7b22f5b..8164b8361a 100644 --- a/substrate/frame/support/src/dispatch.rs +++ b/substrate/frame/support/src/dispatch.rs @@ -89,6 +89,26 @@ impl From> for RawOrigin { } } +impl RawOrigin { + /// Returns `Some` with a reference to the `AccountId` if `self` is `Signed`, `None` otherwise. + pub fn as_signed(&self) -> Option<&AccountId> { + match &self { + Self::Signed(x) => Some(x), + _ => None, + } + } + + /// Returns `true` if `self` is `Root`, `None` otherwise. + pub fn is_root(&self) -> bool { + matches!(&self, Self::Root) + } + + /// Returns `true` if `self` is `None`, `None` otherwise. + pub fn is_none(&self) -> bool { + matches!(&self, Self::None) + } +} + /// A type that can be used as a parameter in a dispatchable function. /// /// When using `decl_module` all arguments for call functions must implement this trait. diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs index 3db274c172..e4fd047504 100644 --- a/substrate/frame/support/src/traits.rs +++ b/substrate/frame/support/src/traits.rs @@ -98,7 +98,7 @@ pub use dispatch::EnsureOneOf; pub use dispatch::{ AsEnsureOriginWithArg, CallerTrait, EitherOf, EitherOfDiverse, EnsureOrigin, EnsureOriginEqualOrHigherPrivilege, EnsureOriginWithArg, MapSuccess, NeverEnsureOrigin, - OriginTrait, TryMapSuccess, UnfilteredDispatchable, + OriginTrait, TryMapSuccess, TryWithMorphedArg, UnfilteredDispatchable, }; mod voting; diff --git a/substrate/frame/support/src/traits/dispatch.rs b/substrate/frame/support/src/traits/dispatch.rs index 6961e69ba5..9ea58479a0 100644 --- a/substrate/frame/support/src/traits/dispatch.rs +++ b/substrate/frame/support/src/traits/dispatch.rs @@ -48,19 +48,6 @@ pub trait EnsureOrigin { fn try_successful_origin() -> Result; } -/// [`EnsureOrigin`] implementation that always fails. -pub struct NeverEnsureOrigin(sp_std::marker::PhantomData); -impl EnsureOrigin for NeverEnsureOrigin { - type Success = Success; - fn try_origin(o: OO) -> Result { - Err(o) - } - #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin() -> Result { - Err(()) - } -} - /// [`EnsureOrigin`] implementation that checks that an origin has equal or higher privilege /// compared to the expected `Origin`. /// @@ -166,6 +153,62 @@ pub trait EnsureOriginWithArg { fn try_successful_origin(a: &Argument) -> Result; } +/// Simple macro to explicitly implement [EnsureOriginWithArg] to be used on any type which +/// implements [EnsureOrigin]. This is quick and dirty, so you must use the type parameters `O` +/// (the origin type), `T` (the argument type) and `AccountId` (if you are using the `O: ..` form). +/// +/// The argument is ignored, much like in [AsEnsureOriginWithArg]. +#[macro_export] +macro_rules! impl_ensure_origin_with_arg_ignoring_arg { + ( impl < { O: .., I: 'static, $( $bound:tt )* }> EnsureOriginWithArg for $name:ty {} ) => { + impl_ensure_origin_with_arg_ignoring_arg! { + impl <{ + O: Into, O>> + From>, + I: 'static, + $( $bound )* + }> EnsureOriginWithArg for $name {} + } + }; + ( impl < { O: .. , $( $bound:tt )* }> EnsureOriginWithArg for $name:ty {} ) => { + impl_ensure_origin_with_arg_ignoring_arg! { + impl <{ + O: Into, O>> + From>, + $( $bound )* + }> EnsureOriginWithArg for $name {} + } + }; + ( impl < { $( $bound:tt )* } > EnsureOriginWithArg<$o_param:ty, $t_param:ty> for $name:ty {} ) => { + impl < $( $bound )* > EnsureOriginWithArg<$o_param, $t_param> for $name { + type Success = >::Success; + fn try_origin(o: $o_param, _: &$t_param) -> Result { + >::try_origin(o) + } + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(_: &$t_param) -> Result<$o_param, ()> { + >::try_successful_origin() + } + } + } +} + +/// [`EnsureOrigin`] implementation that always fails. +pub struct NeverEnsureOrigin(sp_std::marker::PhantomData); +impl EnsureOrigin for NeverEnsureOrigin { + type Success = Success; + fn try_origin(o: OO) -> Result { + Err(o) + } + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + Err(()) + } +} +impl_ensure_origin_with_arg_ignoring_arg! { + impl<{ OO, Success, A }> + EnsureOriginWithArg for NeverEnsureOrigin + {} +} + pub struct AsEnsureOriginWithArg(sp_std::marker::PhantomData); impl> EnsureOriginWithArg for AsEnsureOriginWithArg @@ -207,6 +250,18 @@ impl, Mutator: Morph> EnsureOrig Original::try_successful_origin() } } +impl, Mutator: Morph, A> + EnsureOriginWithArg for MapSuccess +{ + type Success = Mutator::Outcome; + fn try_origin(o: O, a: &A) -> Result { + Ok(Mutator::morph(Original::try_origin(o, a)?)) + } + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(a: &A) -> Result { + Original::try_successful_origin(a) + } +} /// A derivative `EnsureOrigin` implementation. It mutates the `Success` result of an `Original` /// implementation with a given `Mutator`, allowing the possibility of an error to be returned @@ -228,6 +283,43 @@ impl, Mutator: TryMorph> Original::try_successful_origin() } } +impl, Mutator: TryMorph, A> + EnsureOriginWithArg for TryMapSuccess +{ + type Success = Mutator::Outcome; + fn try_origin(o: O, a: &A) -> Result { + let orig = o.clone(); + Mutator::try_morph(Original::try_origin(o, a)?).map_err(|()| orig) + } + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(a: &A) -> Result { + Original::try_successful_origin(a) + } +} + +pub struct TryWithMorphedArg( + PhantomData<(O, A, Morph, Inner, Success)>, +); +impl< + O, + A, + Morph: for<'a> TryMorph<&'a A>, + Inner: for<'a> EnsureOriginWithArg>::Outcome, Success = Success>, + Success, + > EnsureOriginWithArg for TryWithMorphedArg +{ + type Success = Success; + fn try_origin(o: O, a: &A) -> Result { + match Morph::try_morph(a) { + Ok(x) => Inner::try_origin(o, &x), + _ => return Err(o), + } + } + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(a: &A) -> Result { + Inner::try_successful_origin(&Morph::try_morph(a).map_err(|_| ())?) + } +} /// "OR gate" implementation of `EnsureOrigin` allowing for different `Success` types for `L` /// and `R`, with them combined using an `Either` type. @@ -250,6 +342,24 @@ impl, R: EnsureOrigin> L::try_successful_origin().or_else(|()| R::try_successful_origin()) } } +impl< + OuterOrigin, + L: EnsureOriginWithArg, + R: EnsureOriginWithArg, + Argument, + > EnsureOriginWithArg for EitherOfDiverse +{ + type Success = Either; + fn try_origin(o: OuterOrigin, a: &Argument) -> Result { + L::try_origin(o, a) + .map_or_else(|o| R::try_origin(o, a).map(Either::Right), |o| Ok(Either::Left(o))) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(a: &Argument) -> Result { + L::try_successful_origin(a).or_else(|()| R::try_successful_origin(a)) + } +} /// "OR gate" implementation of `EnsureOrigin` allowing for different `Success` types for `L` /// and `R`, with them combined using an `Either` type. @@ -283,6 +393,23 @@ impl< L::try_successful_origin().or_else(|()| R::try_successful_origin()) } } +impl< + OuterOrigin, + L: EnsureOriginWithArg, + R: EnsureOriginWithArg, + Argument, + > EnsureOriginWithArg for EitherOf +{ + type Success = L::Success; + fn try_origin(o: OuterOrigin, a: &Argument) -> Result { + L::try_origin(o, a).or_else(|o| R::try_origin(o, a)) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(a: &Argument) -> Result { + L::try_successful_origin(a).or_else(|()| R::try_successful_origin(a)) + } +} /// Type that can be dispatched with an origin but without checking the origin filter. /// @@ -306,6 +433,21 @@ pub trait CallerTrait: Parameter + Member + From /// Extract a reference to the system-level `RawOrigin` if it is that. fn as_system_ref(&self) -> Option<&RawOrigin>; + + /// Extract the signer from it if a system `Signed` origin, `None` otherwise. + fn as_signed(&self) -> Option<&AccountId> { + self.as_system_ref().and_then(RawOrigin::as_signed) + } + + /// Returns `true` if `self` is a system `Root` origin, `None` otherwise. + fn is_root(&self) -> bool { + self.as_system_ref().map_or(false, RawOrigin::is_root) + } + + /// Returns `true` if `self` is a system `None` origin, `None` otherwise. + fn is_none(&self) -> bool { + self.as_system_ref().map_or(false, RawOrigin::is_none) + } } /// Methods available on `frame_system::Config::RuntimeOrigin`. @@ -356,7 +498,13 @@ pub trait OriginTrait: Sized { fn signed(by: Self::AccountId) -> Self; /// Extract the signer from the message if it is a `Signed` origin. + #[deprecated = "Use `into_signer` instead"] fn as_signed(self) -> Option { + self.into_signer() + } + + /// Extract the signer from the message if it is a `Signed` origin. + fn into_signer(self) -> Option { self.into_caller().into_system().and_then(|s| { if let RawOrigin::Signed(who) = s { Some(who) diff --git a/substrate/frame/system/src/lib.rs b/substrate/frame/system/src/lib.rs index a48a1e459f..cbda3d55cc 100644 --- a/substrate/frame/system/src/lib.rs +++ b/substrate/frame/system/src/lib.rs @@ -89,10 +89,11 @@ use frame_support::{ extract_actual_pays_fee, extract_actual_weight, DispatchClass, DispatchInfo, DispatchResult, DispatchResultWithPostInfo, PerDispatchClass, }, + impl_ensure_origin_with_arg_ignoring_arg, storage::{self, StorageStreamIter}, traits::{ - ConstU32, Contains, EnsureOrigin, Get, HandleLifetime, OnKilledAccount, OnNewAccount, - OriginTrait, PalletInfo, SortedMembers, StoredMap, TypedGet, + ConstU32, Contains, EnsureOrigin, EnsureOriginWithArg, Get, HandleLifetime, + OnKilledAccount, OnNewAccount, OriginTrait, PalletInfo, SortedMembers, StoredMap, TypedGet, }, Parameter, }; @@ -265,7 +266,7 @@ pub mod pallet { type RuntimeOrigin: Into, Self::RuntimeOrigin>> + From> + Clone - + OriginTrait; + + OriginTrait; /// The aggregated `RuntimeCall` type. #[pallet::no_default] @@ -823,6 +824,12 @@ impl, O>> + From>, Acco } } +impl_ensure_origin_with_arg_ignoring_arg! { + impl< { O: .., AccountId: Decode, T } > + EnsureOriginWithArg for EnsureRoot + {} +} + /// Ensure the origin is Root and return the provided `Success` value. pub struct EnsureRootWithSuccess( sp_std::marker::PhantomData<(AccountId, Success)>, @@ -847,6 +854,12 @@ impl< } } +impl_ensure_origin_with_arg_ignoring_arg! { + impl< { O: .., AccountId: Decode, Success: TypedGet, T } > + EnsureOriginWithArg for EnsureRootWithSuccess + {} +} + /// Ensure the origin is provided `Ensure` origin and return the provided `Success` value. pub struct EnsureWithSuccess( sp_std::marker::PhantomData<(Ensure, AccountId, Success)>, @@ -892,6 +905,12 @@ impl, O>> + From>, Acco } } +impl_ensure_origin_with_arg_ignoring_arg! { + impl< { O: .., AccountId: Decode, T } > + EnsureOriginWithArg for EnsureSigned + {} +} + /// Ensure the origin is `Signed` origin from the given `AccountId`. pub struct EnsureSignedBy(sp_std::marker::PhantomData<(Who, AccountId)>); impl< @@ -918,6 +937,12 @@ impl< } } +impl_ensure_origin_with_arg_ignoring_arg! { + impl< { O: .., Who: SortedMembers, AccountId: PartialEq + Clone + Ord + Decode, T } > + EnsureOriginWithArg for EnsureSignedBy + {} +} + /// Ensure the origin is `None`. i.e. unsigned transaction. pub struct EnsureNone(sp_std::marker::PhantomData); impl, O>> + From>, AccountId> @@ -937,10 +962,16 @@ impl, O>> + From>, Acco } } +impl_ensure_origin_with_arg_ignoring_arg! { + impl< { O: .., AccountId, T } > + EnsureOriginWithArg for EnsureNone + {} +} + /// Always fail. -pub struct EnsureNever(sp_std::marker::PhantomData); -impl EnsureOrigin for EnsureNever { - type Success = T; +pub struct EnsureNever(sp_std::marker::PhantomData); +impl EnsureOrigin for EnsureNever { + type Success = Success; fn try_origin(o: O) -> Result { Err(o) } @@ -951,6 +982,12 @@ impl EnsureOrigin for EnsureNever { } } +impl_ensure_origin_with_arg_ignoring_arg! { + impl< { O, Success, T } > + EnsureOriginWithArg for EnsureNever + {} +} + /// Ensure that the origin `o` represents a signed extrinsic (i.e. transaction). /// Returns `Ok` with the account that signed the extrinsic or an `Err` otherwise. pub fn ensure_signed(o: OuterOrigin) -> Result diff --git a/substrate/frame/system/src/tests.rs b/substrate/frame/system/src/tests.rs index 05a7e96fde..165df688b1 100644 --- a/substrate/frame/system/src/tests.rs +++ b/substrate/frame/system/src/tests.rs @@ -746,19 +746,20 @@ fn ensure_signed_stuff_works() { } let signed_origin = RuntimeOrigin::signed(0u64); - assert_ok!(EnsureSigned::try_origin(signed_origin.clone())); - assert_ok!(EnsureSignedBy::::try_origin(signed_origin)); + assert_ok!( as EnsureOrigin<_>>::try_origin(signed_origin.clone())); + assert_ok!( as EnsureOrigin<_>>::try_origin(signed_origin)); #[cfg(feature = "runtime-benchmarks")] { - let successful_origin: RuntimeOrigin = EnsureSigned::try_successful_origin() - .expect("EnsureSigned has no successful origin required for the test"); - assert_ok!(EnsureSigned::try_origin(successful_origin)); + let successful_origin: RuntimeOrigin = + as EnsureOrigin<_>>::try_successful_origin() + .expect("EnsureSigned has no successful origin required for the test"); + assert_ok!( as EnsureOrigin<_>>::try_origin(successful_origin)); let successful_origin: RuntimeOrigin = - EnsureSignedBy::::try_successful_origin() + as EnsureOrigin<_>>::try_successful_origin() .expect("EnsureSignedBy has no successful origin required for the test"); - assert_ok!(EnsureSignedBy::::try_origin(successful_origin)); + assert_ok!( as EnsureOrigin<_>>::try_origin(successful_origin)); } } diff --git a/substrate/primitives/runtime/src/traits.rs b/substrate/primitives/runtime/src/traits.rs index a6155e6a4f..9c09bad21d 100644 --- a/substrate/primitives/runtime/src/traits.rs +++ b/substrate/primitives/runtime/src/traits.rs @@ -339,6 +339,33 @@ impl> TryMorph for TryMorphInto { } } +/// Implementation of `Morph` to retrieve just the first element of a tuple. +pub struct TakeFirst; +impl Morph<(T1,)> for TakeFirst { + type Outcome = T1; + fn morph(a: (T1,)) -> T1 { + a.0 + } +} +impl Morph<(T1, T2)> for TakeFirst { + type Outcome = T1; + fn morph(a: (T1, T2)) -> T1 { + a.0 + } +} +impl Morph<(T1, T2, T3)> for TakeFirst { + type Outcome = T1; + fn morph(a: (T1, T2, T3)) -> T1 { + a.0 + } +} +impl Morph<(T1, T2, T3, T4)> for TakeFirst { + type Outcome = T1; + fn morph(a: (T1, T2, T3, T4)) -> T1 { + a.0 + } +} + /// Create a `Morph` and/or `TryMorph` impls with a simple closure-like expression. /// /// # Examples