From 7d2ecc89954a0b4c96a428ee981814ea401f4507 Mon Sep 17 00:00:00 2001 From: Doordashcon <jesse.chejieh@gmail.com> Date: Thu, 23 Jun 2022 05:18:40 +0100 Subject: [PATCH] make pallet-tips & pallet-bounties instantiable (#11473) * make pallet-tips & pallet-bounties instantiable * update test * add default instance * update * cargo fmt * update * update * update * update * fix merge * fix tests * bounties benchmarking instantiable * fix benchmarks * make tips benchmarks instantible Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> --- substrate/frame/bounties/src/benchmarking.rs | 156 ++++++++------- substrate/frame/bounties/src/lib.rs | 193 ++++++++++--------- substrate/frame/bounties/src/tests.rs | 89 +++++++-- substrate/frame/tips/src/benchmarking.rs | 58 +++--- substrate/frame/tips/src/lib.rs | 105 +++++----- substrate/frame/tips/src/tests.rs | 87 ++++++++- 6 files changed, 417 insertions(+), 271 deletions(-) diff --git a/substrate/frame/bounties/src/benchmarking.rs b/substrate/frame/bounties/src/benchmarking.rs index 912e461501b..7566c32f6e9 100644 --- a/substrate/frame/bounties/src/benchmarking.rs +++ b/substrate/frame/bounties/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::{account, benchmarks_instance_pallet, whitelisted_caller}; use frame_system::RawOrigin; use sp_runtime::traits::Bounded; @@ -31,25 +31,25 @@ use pallet_treasury::Pallet as Treasury; const SEED: u32 = 0; // Create bounties that are approved for use in `on_initialize`. -fn create_approved_bounties<T: Config>(n: u32) -> Result<(), &'static str> { +fn create_approved_bounties<T: Config<I>, I: 'static>(n: u32) -> Result<(), &'static str> { for i in 0..n { let (caller, _curator, _fee, value, reason) = - setup_bounty::<T>(i, T::MaximumReasonLength::get()); - Bounties::<T>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?; - let bounty_id = BountyCount::<T>::get() - 1; - Bounties::<T>::approve_bounty(RawOrigin::Root.into(), bounty_id)?; + setup_bounty::<T, I>(i, T::MaximumReasonLength::get()); + Bounties::<T, I>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?; + let bounty_id = BountyCount::<T, I>::get() - 1; + Bounties::<T, I>::approve_bounty(RawOrigin::Root.into(), bounty_id)?; } - ensure!(BountyApprovals::<T>::get().len() == n as usize, "Not all bounty approved"); + ensure!(BountyApprovals::<T, I>::get().len() == n as usize, "Not all bounty approved"); Ok(()) } // Create the pre-requisite information needed to create a treasury `propose_bounty`. -fn setup_bounty<T: Config>( +fn setup_bounty<T: Config<I>, I: 'static>( u: u32, d: u32, -) -> (T::AccountId, T::AccountId, BalanceOf<T>, BalanceOf<T>, Vec<u8>) { +) -> (T::AccountId, T::AccountId, BalanceOf<T, I>, BalanceOf<T, I>, Vec<u8>) { let caller = account("caller", u, SEED); - let value: BalanceOf<T> = T::BountyValueMinimum::get().saturating_mul(100u32.into()); + let value: BalanceOf<T, I> = T::BountyValueMinimum::get().saturating_mul(100u32.into()); let fee = value / 2u32.into(); let deposit = T::BountyDepositBase::get() + T::DataDepositPerByte::get() * T::MaximumReasonLength::get().into(); @@ -60,97 +60,103 @@ fn setup_bounty<T: Config>( (caller, curator, fee, value, reason) } -fn create_bounty<T: Config>( +fn create_bounty<T: Config<I>, I: 'static>( ) -> Result<(<T::Lookup as StaticLookup>::Source, BountyIndex), &'static str> { - let (caller, curator, fee, value, reason) = setup_bounty::<T>(0, T::MaximumReasonLength::get()); + let (caller, curator, fee, value, reason) = + setup_bounty::<T, I>(0, T::MaximumReasonLength::get()); let curator_lookup = T::Lookup::unlookup(curator.clone()); - Bounties::<T>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?; - let bounty_id = BountyCount::<T>::get() - 1; - Bounties::<T>::approve_bounty(RawOrigin::Root.into(), bounty_id)?; - Treasury::<T>::on_initialize(T::BlockNumber::zero()); - Bounties::<T>::propose_curator(RawOrigin::Root.into(), bounty_id, curator_lookup.clone(), fee)?; - Bounties::<T>::accept_curator(RawOrigin::Signed(curator).into(), bounty_id)?; + Bounties::<T, I>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?; + let bounty_id = BountyCount::<T, I>::get() - 1; + Bounties::<T, I>::approve_bounty(RawOrigin::Root.into(), bounty_id)?; + Treasury::<T, I>::on_initialize(T::BlockNumber::zero()); + Bounties::<T, I>::propose_curator( + RawOrigin::Root.into(), + bounty_id, + curator_lookup.clone(), + fee, + )?; + Bounties::<T, I>::accept_curator(RawOrigin::Signed(curator).into(), bounty_id)?; Ok((curator_lookup, bounty_id)) } -fn setup_pot_account<T: Config>() { - let pot_account = Bounties::<T>::account_id(); +fn setup_pot_account<T: Config<I>, I: 'static>() { + let pot_account = Bounties::<T, I>::account_id(); let value = T::Currency::minimum_balance().saturating_mul(1_000_000_000u32.into()); let _ = T::Currency::make_free_balance_be(&pot_account, value); } -fn assert_last_event<T: Config>(generic_event: <T as Config>::Event) { +fn assert_last_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::Event) { frame_system::Pallet::<T>::assert_last_event(generic_event.into()); } -benchmarks! { +benchmarks_instance_pallet! { propose_bounty { let d in 0 .. T::MaximumReasonLength::get(); - let (caller, curator, fee, value, description) = setup_bounty::<T>(0, d); + let (caller, curator, fee, value, description) = setup_bounty::<T, I>(0, d); }: _(RawOrigin::Signed(caller), value, description) approve_bounty { - let (caller, curator, fee, value, reason) = setup_bounty::<T>(0, T::MaximumReasonLength::get()); - Bounties::<T>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?; - let bounty_id = BountyCount::<T>::get() - 1; + let (caller, curator, fee, value, reason) = setup_bounty::<T, I>(0, T::MaximumReasonLength::get()); + Bounties::<T, I>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?; + let bounty_id = BountyCount::<T, I>::get() - 1; }: _(RawOrigin::Root, bounty_id) propose_curator { - setup_pot_account::<T>(); - let (caller, curator, fee, value, reason) = setup_bounty::<T>(0, T::MaximumReasonLength::get()); + setup_pot_account::<T, I>(); + let (caller, curator, fee, value, reason) = setup_bounty::<T, I>(0, T::MaximumReasonLength::get()); let curator_lookup = T::Lookup::unlookup(curator); - Bounties::<T>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?; - let bounty_id = BountyCount::<T>::get() - 1; - Bounties::<T>::approve_bounty(RawOrigin::Root.into(), bounty_id)?; - Bounties::<T>::on_initialize(T::BlockNumber::zero()); + Bounties::<T, I>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?; + let bounty_id = BountyCount::<T, I>::get() - 1; + Bounties::<T, I>::approve_bounty(RawOrigin::Root.into(), bounty_id)?; + Treasury::<T, I>::on_initialize(T::BlockNumber::zero()); }: _(RawOrigin::Root, bounty_id, curator_lookup, fee) // Worst case when curator is inactive and any sender unassigns the curator. unassign_curator { - setup_pot_account::<T>(); - let (curator_lookup, bounty_id) = create_bounty::<T>()?; - Bounties::<T>::on_initialize(T::BlockNumber::zero()); - let bounty_id = BountyCount::<T>::get() - 1; - frame_system::Pallet::<T>::set_block_number(T::BountyUpdatePeriod::get() + 1u32.into()); + setup_pot_account::<T, I>(); + let (curator_lookup, bounty_id) = create_bounty::<T, I>()?; + Treasury::<T, I>::on_initialize(T::BlockNumber::zero()); + let bounty_id = BountyCount::<T, I>::get() - 1; + frame_system::Pallet::<T>::set_block_number(T::BountyUpdatePeriod::get() + 2u32.into()); let caller = whitelisted_caller(); }: _(RawOrigin::Signed(caller), bounty_id) accept_curator { - setup_pot_account::<T>(); - let (caller, curator, fee, value, reason) = setup_bounty::<T>(0, T::MaximumReasonLength::get()); + setup_pot_account::<T, I>(); + let (caller, curator, fee, value, reason) = setup_bounty::<T, I>(0, T::MaximumReasonLength::get()); let curator_lookup = T::Lookup::unlookup(curator.clone()); - Bounties::<T>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?; - let bounty_id = BountyCount::<T>::get() - 1; - Bounties::<T>::approve_bounty(RawOrigin::Root.into(), bounty_id)?; - Bounties::<T>::on_initialize(T::BlockNumber::zero()); - Bounties::<T>::propose_curator(RawOrigin::Root.into(), bounty_id, curator_lookup, fee)?; + Bounties::<T, I>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?; + let bounty_id = BountyCount::<T, I>::get() - 1; + Bounties::<T, I>::approve_bounty(RawOrigin::Root.into(), bounty_id)?; + Treasury::<T, I>::on_initialize(T::BlockNumber::zero()); + Bounties::<T, I>::propose_curator(RawOrigin::Root.into(), bounty_id, curator_lookup, fee)?; }: _(RawOrigin::Signed(curator), bounty_id) award_bounty { - setup_pot_account::<T>(); - let (curator_lookup, bounty_id) = create_bounty::<T>()?; - Bounties::<T>::on_initialize(T::BlockNumber::zero()); + setup_pot_account::<T, I>(); + let (curator_lookup, bounty_id) = create_bounty::<T, I>()?; + Treasury::<T, I>::on_initialize(T::BlockNumber::zero()); - let bounty_id = BountyCount::<T>::get() - 1; + let bounty_id = BountyCount::<T, I>::get() - 1; let curator = T::Lookup::lookup(curator_lookup).map_err(<&str>::from)?; let beneficiary = T::Lookup::unlookup(account("beneficiary", 0, SEED)); }: _(RawOrigin::Signed(curator), bounty_id, beneficiary) claim_bounty { - setup_pot_account::<T>(); - let (curator_lookup, bounty_id) = create_bounty::<T>()?; - Bounties::<T>::on_initialize(T::BlockNumber::zero()); + setup_pot_account::<T, I>(); + let (curator_lookup, bounty_id) = create_bounty::<T, I>()?; + Treasury::<T, I>::on_initialize(T::BlockNumber::zero()); - let bounty_id = BountyCount::<T>::get() - 1; + let bounty_id = BountyCount::<T, I>::get() - 1; let curator = T::Lookup::lookup(curator_lookup).map_err(<&str>::from)?; let beneficiary_account: T::AccountId = account("beneficiary", 0, SEED); let beneficiary = T::Lookup::unlookup(beneficiary_account.clone()); - Bounties::<T>::award_bounty(RawOrigin::Signed(curator.clone()).into(), bounty_id, beneficiary)?; + Bounties::<T, I>::award_bounty(RawOrigin::Signed(curator.clone()).into(), bounty_id, beneficiary)?; - frame_system::Pallet::<T>::set_block_number(T::BountyDepositPayoutDelay::get()); + frame_system::Pallet::<T>::set_block_number(T::BountyDepositPayoutDelay::get() + 1u32.into()); ensure!(T::Currency::free_balance(&beneficiary_account).is_zero(), "Beneficiary already has balance"); }: _(RawOrigin::Signed(curator), bounty_id) @@ -159,45 +165,45 @@ benchmarks! { } close_bounty_proposed { - setup_pot_account::<T>(); - let (caller, curator, fee, value, reason) = setup_bounty::<T>(0, 0); - Bounties::<T>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?; - let bounty_id = BountyCount::<T>::get() - 1; + setup_pot_account::<T, I>(); + let (caller, curator, fee, value, reason) = setup_bounty::<T, I>(0, 0); + Bounties::<T, I>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?; + let bounty_id = BountyCount::<T, I>::get() - 1; }: close_bounty(RawOrigin::Root, bounty_id) close_bounty_active { - setup_pot_account::<T>(); - let (curator_lookup, bounty_id) = create_bounty::<T>()?; - Bounties::<T>::on_initialize(T::BlockNumber::zero()); - let bounty_id = BountyCount::<T>::get() - 1; + setup_pot_account::<T, I>(); + let (curator_lookup, bounty_id) = create_bounty::<T, I>()?; + Treasury::<T, I>::on_initialize(T::BlockNumber::zero()); + let bounty_id = BountyCount::<T, I>::get() - 1; }: close_bounty(RawOrigin::Root, bounty_id) verify { - assert_last_event::<T>(Event::BountyCanceled { index: bounty_id }.into()) + assert_last_event::<T, I>(Event::BountyCanceled { index: bounty_id }.into()) } extend_bounty_expiry { - setup_pot_account::<T>(); - let (curator_lookup, bounty_id) = create_bounty::<T>()?; - Bounties::<T>::on_initialize(T::BlockNumber::zero()); + setup_pot_account::<T, I>(); + let (curator_lookup, bounty_id) = create_bounty::<T, I>()?; + Treasury::<T, I>::on_initialize(T::BlockNumber::zero()); - let bounty_id = BountyCount::<T>::get() - 1; + let bounty_id = BountyCount::<T, I>::get() - 1; let curator = T::Lookup::lookup(curator_lookup).map_err(<&str>::from)?; }: _(RawOrigin::Signed(curator), bounty_id, Vec::new()) verify { - assert_last_event::<T>(Event::BountyExtended { index: bounty_id }.into()) + assert_last_event::<T, I>(Event::BountyExtended { index: bounty_id }.into()) } spend_funds { let b in 1 .. 100; - setup_pot_account::<T>(); - create_approved_bounties::<T>(b)?; + setup_pot_account::<T, I>(); + create_approved_bounties::<T, I>(b)?; - let mut budget_remaining = BalanceOf::<T>::max_value(); - let mut imbalance = PositiveImbalanceOf::<T>::zero(); + let mut budget_remaining = BalanceOf::<T, I>::max_value(); + let mut imbalance = PositiveImbalanceOf::<T, I>::zero(); let mut total_weight = Weight::zero(); let mut missed_any = false; }: { - <Bounties<T> as pallet_treasury::SpendFunds<T>>::spend_funds( + <Bounties<T, I> as pallet_treasury::SpendFunds<T, I>>::spend_funds( &mut budget_remaining, &mut imbalance, &mut total_weight, @@ -205,9 +211,9 @@ benchmarks! { ); } verify { - ensure!(budget_remaining < BalanceOf::<T>::max_value(), "Budget not used"); + ensure!(budget_remaining < BalanceOf::<T, I>::max_value(), "Budget not used"); ensure!(missed_any == false, "Missed some"); - assert_last_event::<T>(Event::BountyBecameActive { index: b - 1 }.into()) + assert_last_event::<T, I>(Event::BountyBecameActive { index: b - 1 }.into()) } impl_benchmark_test_suite!(Bounties, crate::tests::new_test_ext(), crate::tests::Test) diff --git a/substrate/frame/bounties/src/lib.rs b/substrate/frame/bounties/src/lib.rs index b01a36b430a..8e28cacb9d9 100644 --- a/substrate/frame/bounties/src/lib.rs +++ b/substrate/frame/bounties/src/lib.rs @@ -107,9 +107,9 @@ pub use weights::WeightInfo; pub use pallet::*; -type BalanceOf<T> = pallet_treasury::BalanceOf<T>; +type BalanceOf<T, I = ()> = pallet_treasury::BalanceOf<T, I>; -type PositiveImbalanceOf<T> = pallet_treasury::PositiveImbalanceOf<T>; +type PositiveImbalanceOf<T, I = ()> = pallet_treasury::PositiveImbalanceOf<T, I>; /// An index of a bounty. Just a `u32`. pub type BountyIndex = u32; @@ -188,13 +188,13 @@ pub mod pallet { #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] - pub struct Pallet<T>(_); + pub struct Pallet<T, I = ()>(_); #[pallet::config] - pub trait Config: frame_system::Config + pallet_treasury::Config { + pub trait Config<I: 'static = ()>: frame_system::Config + pallet_treasury::Config<I> { /// The amount held on deposit for placing a bounty proposal. #[pallet::constant] - type BountyDepositBase: Get<BalanceOf<Self>>; + type BountyDepositBase: Get<BalanceOf<Self, I>>; /// The delay period for which a bounty beneficiary need to wait before claim the payout. #[pallet::constant] @@ -213,22 +213,22 @@ pub mod pallet { /// Maximum amount of funds that should be placed in a deposit for making a proposal. #[pallet::constant] - type CuratorDepositMax: Get<Option<BalanceOf<Self>>>; + type CuratorDepositMax: Get<Option<BalanceOf<Self, I>>>; /// Minimum amount of funds that should be placed in a deposit for making a proposal. #[pallet::constant] - type CuratorDepositMin: Get<Option<BalanceOf<Self>>>; + type CuratorDepositMin: Get<Option<BalanceOf<Self, I>>>; /// Minimum value for a bounty. #[pallet::constant] - type BountyValueMinimum: Get<BalanceOf<Self>>; + type BountyValueMinimum: Get<BalanceOf<Self, I>>; /// The amount held on deposit per byte within the tip report reason or bounty description. #[pallet::constant] - type DataDepositPerByte: Get<BalanceOf<Self>>; + type DataDepositPerByte: Get<BalanceOf<Self, I>>; /// The overarching event type. - type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>; + type Event: From<Event<Self, I>> + IsType<<Self as frame_system::Config>::Event>; /// Maximum acceptable reason length. /// @@ -240,11 +240,11 @@ pub mod pallet { type WeightInfo: WeightInfo; /// The child bounty manager. - type ChildBountyManager: ChildBountyManager<BalanceOf<Self>>; + type ChildBountyManager: ChildBountyManager<BalanceOf<Self, I>>; } #[pallet::error] - pub enum Error<T> { + pub enum Error<T, I = ()> { /// Proposer's balance is too low. InsufficientProposersBalance, /// No proposal or bounty at that index. @@ -272,17 +272,17 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event<T: Config> { + pub enum Event<T: Config<I>, I: 'static = ()> { /// New bounty proposal. BountyProposed { index: BountyIndex }, /// A bounty proposal was rejected; funds were slashed. - BountyRejected { index: BountyIndex, bond: BalanceOf<T> }, + BountyRejected { index: BountyIndex, bond: BalanceOf<T, I> }, /// A bounty proposal is funded and became active. BountyBecameActive { index: BountyIndex }, /// A bounty is awarded to a beneficiary. BountyAwarded { index: BountyIndex, beneficiary: T::AccountId }, /// A bounty is claimed by beneficiary. - BountyClaimed { index: BountyIndex, payout: BalanceOf<T>, beneficiary: T::AccountId }, + BountyClaimed { index: BountyIndex, payout: BalanceOf<T, I>, beneficiary: T::AccountId }, /// A bounty is cancelled. BountyCanceled { index: BountyIndex }, /// A bounty expiry is extended. @@ -292,32 +292,32 @@ pub mod pallet { /// Number of bounty proposals that have been made. #[pallet::storage] #[pallet::getter(fn bounty_count)] - pub type BountyCount<T: Config> = StorageValue<_, BountyIndex, ValueQuery>; + pub type BountyCount<T: Config<I>, I: 'static = ()> = StorageValue<_, BountyIndex, ValueQuery>; /// Bounties that have been made. #[pallet::storage] #[pallet::getter(fn bounties)] - pub type Bounties<T: Config> = StorageMap< + pub type Bounties<T: Config<I>, I: 'static = ()> = StorageMap< _, Twox64Concat, BountyIndex, - Bounty<T::AccountId, BalanceOf<T>, T::BlockNumber>, + Bounty<T::AccountId, BalanceOf<T, I>, T::BlockNumber>, >; /// The description of each bounty. #[pallet::storage] #[pallet::getter(fn bounty_descriptions)] - pub type BountyDescriptions<T: Config> = + pub type BountyDescriptions<T: Config<I>, I: 'static = ()> = StorageMap<_, Twox64Concat, BountyIndex, BoundedVec<u8, T::MaximumReasonLength>>; /// Bounty indices that have been approved but not yet funded. #[pallet::storage] #[pallet::getter(fn bounty_approvals)] - pub type BountyApprovals<T: Config> = + pub type BountyApprovals<T: Config<I>, I: 'static = ()> = StorageValue<_, BoundedVec<BountyIndex, T::MaxApprovals>, ValueQuery>; #[pallet::call] - impl<T: Config> Pallet<T> { + impl<T: Config<I>, I: 'static> Pallet<T, I> { /// Propose a new bounty. /// /// The dispatch origin for this call must be _Signed_. @@ -330,10 +330,10 @@ pub mod pallet { /// - `fee`: The curator fee. /// - `value`: The total payment amount of this bounty, curator fee included. /// - `description`: The description of this bounty. - #[pallet::weight(<T as Config>::WeightInfo::propose_bounty(description.len() as u32))] + #[pallet::weight(<T as Config<I>>::WeightInfo::propose_bounty(description.len() as u32))] pub fn propose_bounty( origin: OriginFor<T>, - #[pallet::compact] value: BalanceOf<T>, + #[pallet::compact] value: BalanceOf<T, I>, description: Vec<u8>, ) -> DispatchResult { let proposer = ensure_signed(origin)?; @@ -349,21 +349,21 @@ pub mod pallet { /// # <weight> /// - O(1). /// # </weight> - #[pallet::weight(<T as Config>::WeightInfo::approve_bounty())] + #[pallet::weight(<T as Config<I>>::WeightInfo::approve_bounty())] pub fn approve_bounty( origin: OriginFor<T>, #[pallet::compact] bounty_id: BountyIndex, ) -> DispatchResult { T::ApproveOrigin::ensure_origin(origin)?; - Bounties::<T>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { - let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T>::InvalidIndex)?; - ensure!(bounty.status == BountyStatus::Proposed, Error::<T>::UnexpectedStatus); + Bounties::<T, I>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { + let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T, I>::InvalidIndex)?; + ensure!(bounty.status == BountyStatus::Proposed, Error::<T, I>::UnexpectedStatus); bounty.status = BountyStatus::Approved; - BountyApprovals::<T>::try_append(bounty_id) - .map_err(|()| Error::<T>::TooManyQueued)?; + BountyApprovals::<T, I>::try_append(bounty_id) + .map_err(|()| Error::<T, I>::TooManyQueued)?; Ok(()) })?; @@ -377,24 +377,24 @@ pub mod pallet { /// # <weight> /// - O(1). /// # </weight> - #[pallet::weight(<T as Config>::WeightInfo::propose_curator())] + #[pallet::weight(<T as Config<I>>::WeightInfo::propose_curator())] pub fn propose_curator( origin: OriginFor<T>, #[pallet::compact] bounty_id: BountyIndex, curator: <T::Lookup as StaticLookup>::Source, - #[pallet::compact] fee: BalanceOf<T>, + #[pallet::compact] fee: BalanceOf<T, I>, ) -> DispatchResult { T::ApproveOrigin::ensure_origin(origin)?; let curator = T::Lookup::lookup(curator)?; - Bounties::<T>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { - let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T>::InvalidIndex)?; + Bounties::<T, I>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { + let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T, I>::InvalidIndex)?; match bounty.status { BountyStatus::Proposed | BountyStatus::Approved | BountyStatus::Funded => {}, - _ => return Err(Error::<T>::UnexpectedStatus.into()), + _ => return Err(Error::<T, I>::UnexpectedStatus.into()), }; - ensure!(fee < bounty.value, Error::<T>::InvalidFee); + ensure!(fee < bounty.value, Error::<T, I>::InvalidFee); bounty.status = BountyStatus::CuratorProposed { curator }; bounty.fee = fee; @@ -422,7 +422,7 @@ pub mod pallet { /// # <weight> /// - O(1). /// # </weight> - #[pallet::weight(<T as Config>::WeightInfo::unassign_curator())] + #[pallet::weight(<T as Config<I>>::WeightInfo::unassign_curator())] pub fn unassign_curator( origin: OriginFor<T>, #[pallet::compact] bounty_id: BountyIndex, @@ -431,10 +431,11 @@ pub mod pallet { .map(Some) .or_else(|_| T::RejectOrigin::ensure_origin(origin).map(|_| None))?; - Bounties::<T>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { - let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T>::InvalidIndex)?; + Bounties::<T, I>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { + let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T, I>::InvalidIndex)?; - let slash_curator = |curator: &T::AccountId, curator_deposit: &mut BalanceOf<T>| { + let slash_curator = |curator: &T::AccountId, + curator_deposit: &mut BalanceOf<T, I>| { let imbalance = T::Currency::slash_reserved(curator, *curator_deposit).0; T::OnSlash::on_unbalanced(imbalance); *curator_deposit = Zero::zero(); @@ -443,7 +444,7 @@ pub mod pallet { match bounty.status { BountyStatus::Proposed | BountyStatus::Approved | BountyStatus::Funded => { // No curator to unassign at this point. - return Err(Error::<T>::UnexpectedStatus.into()) + return Err(Error::<T, I>::UnexpectedStatus.into()) }, BountyStatus::CuratorProposed { ref curator } => { // A curator has been proposed, but not accepted yet. @@ -468,7 +469,7 @@ pub mod pallet { // Continue to change bounty status below... } else { // Curator has more time to give an update. - return Err(Error::<T>::Premature.into()) + return Err(Error::<T, I>::Premature.into()) } } else { // Else this is the curator, willingly giving up their role. @@ -506,19 +507,19 @@ pub mod pallet { /// # <weight> /// - O(1). /// # </weight> - #[pallet::weight(<T as Config>::WeightInfo::accept_curator())] + #[pallet::weight(<T as Config<I>>::WeightInfo::accept_curator())] pub fn accept_curator( origin: OriginFor<T>, #[pallet::compact] bounty_id: BountyIndex, ) -> DispatchResult { let signer = ensure_signed(origin)?; - Bounties::<T>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { - let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T>::InvalidIndex)?; + Bounties::<T, I>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { + let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T, I>::InvalidIndex)?; match bounty.status { BountyStatus::CuratorProposed { ref curator } => { - ensure!(signer == *curator, Error::<T>::RequireCurator); + ensure!(signer == *curator, Error::<T, I>::RequireCurator); let deposit = Self::calculate_curator_deposit(&bounty.fee); T::Currency::reserve(curator, deposit)?; @@ -531,7 +532,7 @@ pub mod pallet { Ok(()) }, - _ => Err(Error::<T>::UnexpectedStatus.into()), + _ => Err(Error::<T, I>::UnexpectedStatus.into()), } })?; Ok(()) @@ -548,7 +549,7 @@ pub mod pallet { /// # <weight> /// - O(1). /// # </weight> - #[pallet::weight(<T as Config>::WeightInfo::award_bounty())] + #[pallet::weight(<T as Config<I>>::WeightInfo::award_bounty())] pub fn award_bounty( origin: OriginFor<T>, #[pallet::compact] bounty_id: BountyIndex, @@ -557,20 +558,20 @@ pub mod pallet { let signer = ensure_signed(origin)?; let beneficiary = T::Lookup::lookup(beneficiary)?; - Bounties::<T>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { - let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T>::InvalidIndex)?; + Bounties::<T, I>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { + let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T, I>::InvalidIndex)?; // Ensure no active child bounties before processing the call. ensure!( T::ChildBountyManager::child_bounties_count(bounty_id) == 0, - Error::<T>::HasActiveChildBounty + Error::<T, I>::HasActiveChildBounty ); match &bounty.status { BountyStatus::Active { curator, .. } => { - ensure!(signer == *curator, Error::<T>::RequireCurator); + ensure!(signer == *curator, Error::<T, I>::RequireCurator); }, - _ => return Err(Error::<T>::UnexpectedStatus.into()), + _ => return Err(Error::<T, I>::UnexpectedStatus.into()), } bounty.status = BountyStatus::PendingPayout { curator: signer, @@ -582,7 +583,7 @@ pub mod pallet { Ok(()) })?; - Self::deposit_event(Event::<T>::BountyAwarded { index: bounty_id, beneficiary }); + Self::deposit_event(Event::<T, I>::BountyAwarded { index: bounty_id, beneficiary }); Ok(()) } @@ -595,21 +596,21 @@ pub mod pallet { /// # <weight> /// - O(1). /// # </weight> - #[pallet::weight(<T as Config>::WeightInfo::claim_bounty())] + #[pallet::weight(<T as Config<I>>::WeightInfo::claim_bounty())] pub fn claim_bounty( origin: OriginFor<T>, #[pallet::compact] bounty_id: BountyIndex, ) -> DispatchResult { let _ = ensure_signed(origin)?; // anyone can trigger claim - Bounties::<T>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { - let bounty = maybe_bounty.take().ok_or(Error::<T>::InvalidIndex)?; + Bounties::<T, I>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { + let bounty = maybe_bounty.take().ok_or(Error::<T, I>::InvalidIndex)?; if let BountyStatus::PendingPayout { curator, beneficiary, unlock_at } = bounty.status { ensure!( frame_system::Pallet::<T>::block_number() >= unlock_at, - Error::<T>::Premature + Error::<T, I>::Premature ); let bounty_account = Self::bounty_account_id(bounty_id); let balance = T::Currency::free_balance(&bounty_account); @@ -633,16 +634,16 @@ pub mod pallet { *maybe_bounty = None; - BountyDescriptions::<T>::remove(bounty_id); + BountyDescriptions::<T, I>::remove(bounty_id); - Self::deposit_event(Event::<T>::BountyClaimed { + Self::deposit_event(Event::<T, I>::BountyClaimed { index: bounty_id, payout, beneficiary, }); Ok(()) } else { - Err(Error::<T>::UnexpectedStatus.into()) + Err(Error::<T, I>::UnexpectedStatus.into()) } })?; Ok(()) @@ -658,47 +659,47 @@ pub mod pallet { /// # <weight> /// - O(1). /// # </weight> - #[pallet::weight(<T as Config>::WeightInfo::close_bounty_proposed() - .max(<T as Config>::WeightInfo::close_bounty_active()))] + #[pallet::weight(<T as Config<I>>::WeightInfo::close_bounty_proposed() + .max(<T as Config<I>>::WeightInfo::close_bounty_active()))] pub fn close_bounty( origin: OriginFor<T>, #[pallet::compact] bounty_id: BountyIndex, ) -> DispatchResultWithPostInfo { T::RejectOrigin::ensure_origin(origin)?; - Bounties::<T>::try_mutate_exists( + Bounties::<T, I>::try_mutate_exists( bounty_id, |maybe_bounty| -> DispatchResultWithPostInfo { - let bounty = maybe_bounty.as_ref().ok_or(Error::<T>::InvalidIndex)?; + let bounty = maybe_bounty.as_ref().ok_or(Error::<T, I>::InvalidIndex)?; // Ensure no active child bounties before processing the call. ensure!( T::ChildBountyManager::child_bounties_count(bounty_id) == 0, - Error::<T>::HasActiveChildBounty + Error::<T, I>::HasActiveChildBounty ); match &bounty.status { BountyStatus::Proposed => { // The reject origin would like to cancel a proposed bounty. - BountyDescriptions::<T>::remove(bounty_id); + BountyDescriptions::<T, I>::remove(bounty_id); let value = bounty.bond; let imbalance = T::Currency::slash_reserved(&bounty.proposer, value).0; T::OnSlash::on_unbalanced(imbalance); *maybe_bounty = None; - Self::deposit_event(Event::<T>::BountyRejected { + Self::deposit_event(Event::<T, I>::BountyRejected { index: bounty_id, bond: value, }); // Return early, nothing else to do. return Ok( - Some(<T as Config>::WeightInfo::close_bounty_proposed()).into() + Some(<T as Config<I>>::WeightInfo::close_bounty_proposed()).into() ) }, BountyStatus::Approved => { // For weight reasons, we don't allow a council to cancel in this phase. // We ask for them to wait until it is funded before they can cancel. - return Err(Error::<T>::UnexpectedStatus.into()) + return Err(Error::<T, I>::UnexpectedStatus.into()) }, BountyStatus::Funded | BountyStatus::CuratorProposed { .. } => { // Nothing extra to do besides the removal of the bounty below. @@ -715,13 +716,13 @@ pub mod pallet { // this bounty, it should mean the curator was acting maliciously. // So the council should first unassign the curator, slashing their // deposit. - return Err(Error::<T>::PendingPayout.into()) + return Err(Error::<T, I>::PendingPayout.into()) }, } let bounty_account = Self::bounty_account_id(bounty_id); - BountyDescriptions::<T>::remove(bounty_id); + BountyDescriptions::<T, I>::remove(bounty_id); let balance = T::Currency::free_balance(&bounty_account); let res = T::Currency::transfer( @@ -733,8 +734,8 @@ pub mod pallet { debug_assert!(res.is_ok()); *maybe_bounty = None; - Self::deposit_event(Event::<T>::BountyCanceled { index: bounty_id }); - Ok(Some(<T as Config>::WeightInfo::close_bounty_active()).into()) + Self::deposit_event(Event::<T, I>::BountyCanceled { index: bounty_id }); + Ok(Some(<T as Config<I>>::WeightInfo::close_bounty_active()).into()) }, ) } @@ -749,7 +750,7 @@ pub mod pallet { /// # <weight> /// - O(1). /// # </weight> - #[pallet::weight(<T as Config>::WeightInfo::extend_bounty_expiry())] + #[pallet::weight(<T as Config<I>>::WeightInfo::extend_bounty_expiry())] pub fn extend_bounty_expiry( origin: OriginFor<T>, #[pallet::compact] bounty_id: BountyIndex, @@ -757,30 +758,30 @@ pub mod pallet { ) -> DispatchResult { let signer = ensure_signed(origin)?; - Bounties::<T>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { - let bounty = maybe_bounty.as_mut().ok_or(Error::<T>::InvalidIndex)?; + Bounties::<T, I>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { + let bounty = maybe_bounty.as_mut().ok_or(Error::<T, I>::InvalidIndex)?; match bounty.status { BountyStatus::Active { ref curator, ref mut update_due } => { - ensure!(*curator == signer, Error::<T>::RequireCurator); + ensure!(*curator == signer, Error::<T, I>::RequireCurator); *update_due = (frame_system::Pallet::<T>::block_number() + T::BountyUpdatePeriod::get()) .max(*update_due); }, - _ => return Err(Error::<T>::UnexpectedStatus.into()), + _ => return Err(Error::<T, I>::UnexpectedStatus.into()), } Ok(()) })?; - Self::deposit_event(Event::<T>::BountyExtended { index: bounty_id }); + Self::deposit_event(Event::<T, I>::BountyExtended { index: bounty_id }); Ok(()) } } } -impl<T: Config> Pallet<T> { - pub fn calculate_curator_deposit(fee: &BalanceOf<T>) -> BalanceOf<T> { +impl<T: Config<I>, I: 'static> Pallet<T, I> { + pub fn calculate_curator_deposit(fee: &BalanceOf<T, I>) -> BalanceOf<T, I> { let mut deposit = T::CuratorDepositMultiplier::get() * *fee; if let Some(max_deposit) = T::CuratorDepositMax::get() { @@ -812,11 +813,11 @@ impl<T: Config> Pallet<T> { fn create_bounty( proposer: T::AccountId, description: Vec<u8>, - value: BalanceOf<T>, + value: BalanceOf<T, I>, ) -> DispatchResult { let bounded_description: BoundedVec<_, _> = - description.try_into().map_err(|()| Error::<T>::ReasonTooBig)?; - ensure!(value >= T::BountyValueMinimum::get(), Error::<T>::InvalidValue); + description.try_into().map_err(|()| Error::<T, I>::ReasonTooBig)?; + ensure!(value >= T::BountyValueMinimum::get(), Error::<T, I>::InvalidValue); let index = Self::bounty_count(); @@ -824,9 +825,9 @@ impl<T: Config> Pallet<T> { let bond = T::BountyDepositBase::get() + T::DataDepositPerByte::get() * (bounded_description.len() as u32).into(); T::Currency::reserve(&proposer, bond) - .map_err(|_| Error::<T>::InsufficientProposersBalance)?; + .map_err(|_| Error::<T, I>::InsufficientProposersBalance)?; - BountyCount::<T>::put(index + 1); + BountyCount::<T, I>::put(index + 1); let bounty = Bounty { proposer, @@ -837,26 +838,26 @@ impl<T: Config> Pallet<T> { status: BountyStatus::Proposed, }; - Bounties::<T>::insert(index, &bounty); - BountyDescriptions::<T>::insert(index, bounded_description); + Bounties::<T, I>::insert(index, &bounty); + BountyDescriptions::<T, I>::insert(index, bounded_description); - Self::deposit_event(Event::<T>::BountyProposed { index }); + Self::deposit_event(Event::<T, I>::BountyProposed { index }); Ok(()) } } -impl<T: Config> pallet_treasury::SpendFunds<T> for Pallet<T> { +impl<T: Config<I>, I: 'static> pallet_treasury::SpendFunds<T, I> for Pallet<T, I> { fn spend_funds( - budget_remaining: &mut BalanceOf<T>, - imbalance: &mut PositiveImbalanceOf<T>, + budget_remaining: &mut BalanceOf<T, I>, + imbalance: &mut PositiveImbalanceOf<T, I>, total_weight: &mut Weight, missed_any: &mut bool, ) { - let bounties_len = BountyApprovals::<T>::mutate(|v| { + let bounties_len = BountyApprovals::<T, I>::mutate(|v| { let bounties_approval_len = v.len() as u32; v.retain(|&index| { - Bounties::<T>::mutate(index, |bounty| { + Bounties::<T, I>::mutate(index, |bounty| { // Should always be true, but shouldn't panic if false or we're screwed. if let Some(bounty) = bounty { if bounty.value <= *budget_remaining { @@ -874,7 +875,7 @@ impl<T: Config> pallet_treasury::SpendFunds<T> for Pallet<T> { bounty.value, )); - Self::deposit_event(Event::<T>::BountyBecameActive { index }); + Self::deposit_event(Event::<T, I>::BountyBecameActive { index }); false } else { *missed_any = true; @@ -888,7 +889,7 @@ impl<T: Config> pallet_treasury::SpendFunds<T> for Pallet<T> { bounties_approval_len }); - *total_weight += <T as Config>::WeightInfo::spend_funds(bounties_len); + *total_weight += <T as pallet::Config<I>>::WeightInfo::spend_funds(bounties_len); } } diff --git a/substrate/frame/bounties/src/tests.rs b/substrate/frame/bounties/src/tests.rs index 0904e3a2901..ff220600794 100644 --- a/substrate/frame/bounties/src/tests.rs +++ b/substrate/frame/bounties/src/tests.rs @@ -35,7 +35,7 @@ use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BadOrigin, BlakeTwo256, IdentityLookup}, - Perbill, Storage, + BuildStorage, Perbill, Storage, }; use super::Event as BountiesEvent; @@ -52,7 +52,9 @@ frame_support::construct_runtime!( System: frame_system::{Pallet, Call, Config, Storage, Event<T>}, Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>}, Bounties: pallet_bounties::{Pallet, Call, Storage, Event<T>}, + Bounties1: pallet_bounties::<Instance1>::{Pallet, Call, Storage, Event<T>}, Treasury: pallet_treasury::{Pallet, Call, Storage, Config, Event<T>}, + Treasury1: pallet_treasury::<Instance1>::{Pallet, Call, Storage, Config, Event<T>}, } ); @@ -105,8 +107,9 @@ thread_local! { } parameter_types! { pub const ProposalBond: Permill = Permill::from_percent(5); - pub const Burn: Permill = Permill::from_percent(50); + pub static Burn: Permill = Permill::from_percent(50); pub const TreasuryPalletId: PalletId = PalletId(*b"py/trsry"); + pub const TreasuryPalletId2: PalletId = PalletId(*b"py/trsr2"); } impl pallet_treasury::Config for Test { @@ -128,6 +131,25 @@ impl pallet_treasury::Config for Test { type SpendOrigin = frame_support::traits::NeverEnsureOrigin<u64>; } +impl pallet_treasury::Config<Instance1> for Test { + type PalletId = TreasuryPalletId2; + type Currency = pallet_balances::Pallet<Test>; + type ApproveOrigin = frame_system::EnsureRoot<u128>; + type RejectOrigin = frame_system::EnsureRoot<u128>; + type Event = Event; + type OnSlash = (); + type ProposalBond = ProposalBond; + type ProposalBondMinimum = ConstU64<1>; + type ProposalBondMaximum = (); + type SpendPeriod = ConstU64<2>; + type Burn = Burn; + type BurnDestination = (); // Just gets burned. + type WeightInfo = (); + type SpendFunds = Bounties1; + type MaxApprovals = ConstU32<100>; + type SpendOrigin = frame_support::traits::NeverEnsureOrigin<u64>; +} + parameter_types! { // This will be 50% of the bounty fee. pub const CuratorDepositMultiplier: Permill = Permill::from_percent(50); @@ -151,18 +173,35 @@ impl Config for Test { type ChildBountyManager = (); } +impl Config<Instance1> for Test { + type Event = Event; + type BountyDepositBase = ConstU64<80>; + type BountyDepositPayoutDelay = ConstU64<3>; + type BountyUpdatePeriod = ConstU64<20>; + type CuratorDepositMultiplier = CuratorDepositMultiplier; + type CuratorDepositMax = CuratorDepositMax; + type CuratorDepositMin = CuratorDepositMin; + type BountyValueMinimum = ConstU64<1>; + type DataDepositPerByte = ConstU64<1>; + type MaximumReasonLength = ConstU32<16384>; + type WeightInfo = (); + type ChildBountyManager = (); +} + type TreasuryError = pallet_treasury::Error<Test>; pub fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap(); - pallet_balances::GenesisConfig::<Test> { - // Total issuance will be 200 with treasury account initialized at ED. - balances: vec![(0, 100), (1, 98), (2, 1)], + let mut ext: sp_io::TestExternalities = GenesisConfig { + system: frame_system::GenesisConfig::default(), + balances: pallet_balances::GenesisConfig { balances: vec![(0, 100), (1, 98), (2, 1)] }, + treasury: Default::default(), + treasury_1: Default::default(), } - .assimilate_storage(&mut t) - .unwrap(); - GenesisBuild::<Test>::assimilate_storage(&pallet_treasury::GenesisConfig, &mut t).unwrap(); - t.into() + .build_storage() + .unwrap() + .into(); + ext.execute_with(|| System::set_block_number(1)); + ext } fn last_event() -> BountiesEvent<Test> { @@ -276,7 +315,7 @@ fn reject_non_existent_spend_proposal_fails() { new_test_ext().execute_with(|| { assert_noop!( Treasury::reject_proposal(Origin::root(), 0), - pallet_treasury::Error::<Test, _>::InvalidIndex + pallet_treasury::Error::<Test>::InvalidIndex ); }); } @@ -469,7 +508,7 @@ fn close_bounty_works() { assert_eq!(Balances::free_balance(0), 100 - deposit); assert_eq!(Bounties::bounties(0), None); - assert!(!pallet_treasury::Proposals::<Test, _>::contains_key(0)); + assert!(!pallet_treasury::Proposals::<Test>::contains_key(0)); assert_eq!(Bounties::bounty_descriptions(0), None); }); @@ -1122,3 +1161,29 @@ fn accept_curator_handles_different_deposit_calculations() { assert_eq!(Balances::reserved_balance(&user), expected_deposit); }); } + +#[test] +fn approve_bounty_works_second_instance() { + new_test_ext().execute_with(|| { + // Set burn to 0 to make tracking funds easier. + Burn::set(Permill::from_percent(0)); + + System::set_block_number(1); + Balances::make_free_balance_be(&Treasury::account_id(), 101); + Balances::make_free_balance_be(&Treasury1::account_id(), 201); + assert_eq!(Balances::free_balance(&Treasury::account_id()), 101); + assert_eq!(Balances::free_balance(&Treasury1::account_id()), 201); + + assert_ok!(Bounties1::propose_bounty(Origin::signed(0), 50, b"12345".to_vec())); + assert_ok!(Bounties1::approve_bounty(Origin::root(), 0)); + <Treasury as OnInitialize<u64>>::on_initialize(2); + <Treasury1 as OnInitialize<u64>>::on_initialize(2); + + // Bounties 1 is funded... but from where? + assert_eq!(Balances::free_balance(Bounties1::bounty_account_id(0)), 50); + // Treasury 1 unchanged + assert_eq!(Balances::free_balance(&Treasury::account_id()), 101); + // Treasury 2 has funds removed + assert_eq!(Balances::free_balance(&Treasury1::account_id()), 201 - 50); + }); +} diff --git a/substrate/frame/tips/src/benchmarking.rs b/substrate/frame/tips/src/benchmarking.rs index 190ef60f3b8..33e455bd3b9 100644 --- a/substrate/frame/tips/src/benchmarking.rs +++ b/substrate/frame/tips/src/benchmarking.rs @@ -19,7 +19,7 @@ #![cfg(feature = "runtime-benchmarks")] -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::{account, benchmarks_instance_pallet, whitelisted_caller}; use frame_support::ensure; use frame_system::RawOrigin; use sp_runtime::traits::Saturating; @@ -30,7 +30,7 @@ use crate::Pallet as TipsMod; const SEED: u32 = 0; // Create the pre-requisite information needed to create a `report_awesome`. -fn setup_awesome<T: Config>(length: u32) -> (T::AccountId, Vec<u8>, T::AccountId) { +fn setup_awesome<T: Config<I>, I: 'static>(length: u32) -> (T::AccountId, Vec<u8>, T::AccountId) { let caller = whitelisted_caller(); let value = T::TipReportDepositBase::get() + T::DataDepositPerByte::get() * length.into() + @@ -42,10 +42,10 @@ fn setup_awesome<T: Config>(length: u32) -> (T::AccountId, Vec<u8>, T::AccountId } // Create the pre-requisite information needed to call `tip_new`. -fn setup_tip<T: Config>( +fn setup_tip<T: Config<I>, I: 'static>( r: u32, t: u32, -) -> Result<(T::AccountId, Vec<u8>, T::AccountId, BalanceOf<T>), &'static str> { +) -> Result<(T::AccountId, Vec<u8>, T::AccountId, BalanceOf<T, I>), &'static str> { let tippers_count = T::Tippers::count(); for i in 0..t { @@ -64,13 +64,17 @@ fn setup_tip<T: Config>( // Create `t` new tips for the tip proposal with `hash`. // This function automatically makes the tip able to close. -fn create_tips<T: Config>(t: u32, hash: T::Hash, value: BalanceOf<T>) -> Result<(), &'static str> { +fn create_tips<T: Config<I>, I: 'static>( + t: u32, + hash: T::Hash, + value: BalanceOf<T, I>, +) -> Result<(), &'static str> { for i in 0..t { let caller = account("member", i, SEED); ensure!(T::Tippers::contains(&caller), "caller is not a tipper"); - TipsMod::<T>::tip(RawOrigin::Signed(caller).into(), hash, value)?; + TipsMod::<T, I>::tip(RawOrigin::Signed(caller).into(), hash, value)?; } - Tips::<T>::mutate(hash, |maybe_tip| { + Tips::<T, I>::mutate(hash, |maybe_tip| { if let Some(open_tip) = maybe_tip { open_tip.closes = Some(T::BlockNumber::zero()); } @@ -78,16 +82,16 @@ fn create_tips<T: Config>(t: u32, hash: T::Hash, value: BalanceOf<T>) -> Result< Ok(()) } -fn setup_pot_account<T: Config>() { - let pot_account = TipsMod::<T>::account_id(); +fn setup_pot_account<T: Config<I>, I: 'static>() { + let pot_account = TipsMod::<T, I>::account_id(); let value = T::Currency::minimum_balance().saturating_mul(1_000_000_000u32.into()); let _ = T::Currency::make_free_balance_be(&pot_account, value); } -benchmarks! { +benchmarks_instance_pallet! { report_awesome { let r in 0 .. T::MaximumReasonLength::get(); - let (caller, reason, awesome_person) = setup_awesome::<T>(r); + let (caller, reason, awesome_person) = setup_awesome::<T, I>(r); // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::<T>::hashed_key_for(&caller); frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); @@ -95,8 +99,8 @@ benchmarks! { retract_tip { let r = T::MaximumReasonLength::get(); - let (caller, reason, awesome_person) = setup_awesome::<T>(r); - TipsMod::<T>::report_awesome( + let (caller, reason, awesome_person) = setup_awesome::<T, I>(r); + TipsMod::<T, I>::report_awesome( RawOrigin::Signed(caller.clone()).into(), reason.clone(), awesome_person.clone() @@ -112,7 +116,7 @@ benchmarks! { let r in 0 .. T::MaximumReasonLength::get(); let t in 1 .. T::Tippers::max_len() as u32; - let (caller, reason, beneficiary, value) = setup_tip::<T>(r, t)?; + let (caller, reason, beneficiary, value) = setup_tip::<T, I>(r, t)?; // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::<T>::hashed_key_for(&caller); frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); @@ -120,9 +124,9 @@ benchmarks! { tip { let t in 1 .. T::Tippers::max_len() as u32; - let (member, reason, beneficiary, value) = setup_tip::<T>(0, t)?; + let (member, reason, beneficiary, value) = setup_tip::<T, I>(0, t)?; let value = T::Currency::minimum_balance().saturating_mul(100u32.into()); - TipsMod::<T>::tip_new( + TipsMod::<T, I>::tip_new( RawOrigin::Signed(member).into(), reason.clone(), beneficiary.clone(), @@ -130,8 +134,8 @@ benchmarks! { )?; let reason_hash = T::Hashing::hash(&reason[..]); let hash = T::Hashing::hash_of(&(&reason_hash, &beneficiary)); - ensure!(Tips::<T>::contains_key(hash), "tip does not exist"); - create_tips::<T>(t - 1, hash, value)?; + ensure!(Tips::<T, I>::contains_key(hash), "tip does not exist"); + create_tips::<T, I>(t - 1, hash, value)?; let caller = account("member", t - 1, SEED); // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::<T>::hashed_key_for(&caller); @@ -142,12 +146,12 @@ benchmarks! { let t in 1 .. T::Tippers::max_len() as u32; // Make sure pot is funded - setup_pot_account::<T>(); + setup_pot_account::<T, I>(); // Set up a new tip proposal - let (member, reason, beneficiary, value) = setup_tip::<T>(0, t)?; + let (member, reason, beneficiary, value) = setup_tip::<T, I>(0, t)?; let value = T::Currency::minimum_balance().saturating_mul(100u32.into()); - TipsMod::<T>::tip_new( + TipsMod::<T, I>::tip_new( RawOrigin::Signed(member).into(), reason.clone(), beneficiary.clone(), @@ -157,9 +161,9 @@ benchmarks! { // Create a bunch of tips let reason_hash = T::Hashing::hash(&reason[..]); let hash = T::Hashing::hash_of(&(&reason_hash, &beneficiary)); - ensure!(Tips::<T>::contains_key(hash), "tip does not exist"); + ensure!(Tips::<T, I>::contains_key(hash), "tip does not exist"); - create_tips::<T>(t, hash, value)?; + create_tips::<T, I>(t, hash, value)?; let caller = account("caller", t, SEED); // Whitelist caller account from further DB operations. @@ -171,12 +175,12 @@ benchmarks! { let t in 1 .. T::Tippers::max_len() as u32; // Make sure pot is funded - setup_pot_account::<T>(); + setup_pot_account::<T, I>(); // Set up a new tip proposal - let (member, reason, beneficiary, value) = setup_tip::<T>(0, t)?; + let (member, reason, beneficiary, value) = setup_tip::<T, I>(0, t)?; let value = T::Currency::minimum_balance().saturating_mul(100u32.into()); - TipsMod::<T>::tip_new( + TipsMod::<T, I>::tip_new( RawOrigin::Signed(member).into(), reason.clone(), beneficiary.clone(), @@ -185,7 +189,7 @@ benchmarks! { let reason_hash = T::Hashing::hash(&reason[..]); let hash = T::Hashing::hash_of(&(&reason_hash, &beneficiary)); - ensure!(Tips::<T>::contains_key(hash), "tip does not exist"); + ensure!(Tips::<T, I>::contains_key(hash), "tip does not exist"); }: _(RawOrigin::Root, hash) impl_benchmark_test_suite!(TipsMod, crate::tests::new_test_ext(), crate::tests::Test); diff --git a/substrate/frame/tips/src/lib.rs b/substrate/frame/tips/src/lib.rs index e1c7b5e77c0..71af87b42b5 100644 --- a/substrate/frame/tips/src/lib.rs +++ b/substrate/frame/tips/src/lib.rs @@ -78,8 +78,8 @@ use frame_support::{ pub use pallet::*; pub use weights::WeightInfo; -pub type BalanceOf<T> = pallet_treasury::BalanceOf<T>; -pub type NegativeImbalanceOf<T> = pallet_treasury::NegativeImbalanceOf<T>; +pub type BalanceOf<T, I = ()> = pallet_treasury::BalanceOf<T, I>; +pub type NegativeImbalanceOf<T, I = ()> = pallet_treasury::NegativeImbalanceOf<T, I>; /// An open tipping "motion". Retains all details of a tip including information on the finder /// and the members who have voted. @@ -121,12 +121,12 @@ pub mod pallet { #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] #[pallet::without_storage_info] - pub struct Pallet<T>(_); + pub struct Pallet<T, I = ()>(_); #[pallet::config] - pub trait Config: frame_system::Config + pallet_treasury::Config { + pub trait Config<I: 'static = ()>: frame_system::Config + pallet_treasury::Config<I> { /// The overarching event type. - type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>; + type Event: From<Event<Self, I>> + IsType<<Self as frame_system::Config>::Event>; /// Maximum acceptable reason length. /// @@ -136,7 +136,7 @@ pub mod pallet { /// The amount held on deposit per byte within the tip report reason or bounty description. #[pallet::constant] - type DataDepositPerByte: Get<BalanceOf<Self>>; + type DataDepositPerByte: Get<BalanceOf<Self, I>>; /// The period for which a tip remains open after is has achieved threshold tippers. #[pallet::constant] @@ -148,7 +148,7 @@ pub mod pallet { /// The amount held on deposit for placing a tip report. #[pallet::constant] - type TipReportDepositBase: Get<BalanceOf<Self>>; + type TipReportDepositBase: Get<BalanceOf<Self, I>>; /// Origin from which tippers must come. /// @@ -166,11 +166,11 @@ pub mod pallet { /// guaranteed to be a secure hash. #[pallet::storage] #[pallet::getter(fn tips)] - pub type Tips<T: Config> = StorageMap< + pub type Tips<T: Config<I>, I: 'static = ()> = StorageMap< _, Twox64Concat, T::Hash, - OpenTip<T::AccountId, BalanceOf<T>, T::BlockNumber, T::Hash>, + OpenTip<T::AccountId, BalanceOf<T, I>, T::BlockNumber, T::Hash>, OptionQuery, >; @@ -178,25 +178,26 @@ pub mod pallet { /// insecure enumerable hash since the key is guaranteed to be the result of a secure hash. #[pallet::storage] #[pallet::getter(fn reasons)] - pub type Reasons<T: Config> = StorageMap<_, Identity, T::Hash, Vec<u8>, OptionQuery>; + pub type Reasons<T: Config<I>, I: 'static = ()> = + StorageMap<_, Identity, T::Hash, Vec<u8>, OptionQuery>; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event<T: Config> { + pub enum Event<T: Config<I>, I: 'static = ()> { /// A new tip suggestion has been opened. NewTip { tip_hash: T::Hash }, /// A tip suggestion has reached threshold and is closing. TipClosing { tip_hash: T::Hash }, /// A tip suggestion has been closed. - TipClosed { tip_hash: T::Hash, who: T::AccountId, payout: BalanceOf<T> }, + TipClosed { tip_hash: T::Hash, who: T::AccountId, payout: BalanceOf<T, I> }, /// A tip suggestion has been retracted. TipRetracted { tip_hash: T::Hash }, /// A tip suggestion has been slashed. - TipSlashed { tip_hash: T::Hash, finder: T::AccountId, deposit: BalanceOf<T> }, + TipSlashed { tip_hash: T::Hash, finder: T::AccountId, deposit: BalanceOf<T, I> }, } #[pallet::error] - pub enum Error<T> { + pub enum Error<T, I = ()> { /// The reason given is just too big. ReasonTooBig, /// The tip was already found/started. @@ -212,7 +213,7 @@ pub mod pallet { } #[pallet::call] - impl<T: Config> Pallet<T> { + impl<T: Config<I>, I: 'static> Pallet<T, I> { /// Report something `reason` that deserves a tip and claim any eventual the finder's fee. /// /// The dispatch origin for this call must be _Signed_. @@ -232,7 +233,7 @@ pub mod pallet { /// - DbReads: `Reasons`, `Tips` /// - DbWrites: `Reasons`, `Tips` /// # </weight> - #[pallet::weight(<T as Config>::WeightInfo::report_awesome(reason.len() as u32))] + #[pallet::weight(<T as Config<I>>::WeightInfo::report_awesome(reason.len() as u32))] pub fn report_awesome( origin: OriginFor<T>, reason: Vec<u8>, @@ -242,19 +243,19 @@ pub mod pallet { ensure!( reason.len() <= T::MaximumReasonLength::get() as usize, - Error::<T>::ReasonTooBig + Error::<T, I>::ReasonTooBig ); let reason_hash = T::Hashing::hash(&reason[..]); - ensure!(!Reasons::<T>::contains_key(&reason_hash), Error::<T>::AlreadyKnown); + ensure!(!Reasons::<T, I>::contains_key(&reason_hash), Error::<T, I>::AlreadyKnown); let hash = T::Hashing::hash_of(&(&reason_hash, &who)); - ensure!(!Tips::<T>::contains_key(&hash), Error::<T>::AlreadyKnown); + ensure!(!Tips::<T, I>::contains_key(&hash), Error::<T, I>::AlreadyKnown); let deposit = T::TipReportDepositBase::get() + T::DataDepositPerByte::get() * (reason.len() as u32).into(); T::Currency::reserve(&finder, deposit)?; - Reasons::<T>::insert(&reason_hash, &reason); + Reasons::<T, I>::insert(&reason_hash, &reason); let tip = OpenTip { reason: reason_hash, who, @@ -264,7 +265,7 @@ pub mod pallet { tips: vec![], finders_fee: true, }; - Tips::<T>::insert(&hash, tip); + Tips::<T, I>::insert(&hash, tip); Self::deposit_event(Event::NewTip { tip_hash: hash }); Ok(()) } @@ -288,14 +289,14 @@ pub mod pallet { /// - DbReads: `Tips`, `origin account` /// - DbWrites: `Reasons`, `Tips`, `origin account` /// # </weight> - #[pallet::weight(<T as Config>::WeightInfo::retract_tip())] + #[pallet::weight(<T as Config<I>>::WeightInfo::retract_tip())] pub fn retract_tip(origin: OriginFor<T>, hash: T::Hash) -> DispatchResult { let who = ensure_signed(origin)?; - let tip = Tips::<T>::get(&hash).ok_or(Error::<T>::UnknownTip)?; - ensure!(tip.finder == who, Error::<T>::NotFinder); + let tip = Tips::<T, I>::get(&hash).ok_or(Error::<T, I>::UnknownTip)?; + ensure!(tip.finder == who, Error::<T, I>::NotFinder); - Reasons::<T>::remove(&tip.reason); - Tips::<T>::remove(&hash); + Reasons::<T, I>::remove(&tip.reason); + Tips::<T, I>::remove(&hash); if !tip.deposit.is_zero() { let err_amount = T::Currency::unreserve(&who, tip.deposit); debug_assert!(err_amount.is_zero()); @@ -326,20 +327,20 @@ pub mod pallet { /// - DbReads: `Tippers`, `Reasons` /// - DbWrites: `Reasons`, `Tips` /// # </weight> - #[pallet::weight(<T as Config>::WeightInfo::tip_new(reason.len() as u32, T::Tippers::max_len() as u32))] + #[pallet::weight(<T as Config<I>>::WeightInfo::tip_new(reason.len() as u32, T::Tippers::max_len() as u32))] pub fn tip_new( origin: OriginFor<T>, reason: Vec<u8>, who: T::AccountId, - #[pallet::compact] tip_value: BalanceOf<T>, + #[pallet::compact] tip_value: BalanceOf<T, I>, ) -> DispatchResult { let tipper = ensure_signed(origin)?; ensure!(T::Tippers::contains(&tipper), BadOrigin); let reason_hash = T::Hashing::hash(&reason[..]); - ensure!(!Reasons::<T>::contains_key(&reason_hash), Error::<T>::AlreadyKnown); + ensure!(!Reasons::<T, I>::contains_key(&reason_hash), Error::<T, I>::AlreadyKnown); let hash = T::Hashing::hash_of(&(&reason_hash, &who)); - Reasons::<T>::insert(&reason_hash, &reason); + Reasons::<T, I>::insert(&reason_hash, &reason); Self::deposit_event(Event::NewTip { tip_hash: hash }); let tips = vec![(tipper.clone(), tip_value)]; let tip = OpenTip { @@ -351,7 +352,7 @@ pub mod pallet { tips, finders_fee: false, }; - Tips::<T>::insert(&hash, tip); + Tips::<T, I>::insert(&hash, tip); Ok(()) } @@ -379,20 +380,20 @@ pub mod pallet { /// - DbReads: `Tippers`, `Tips` /// - DbWrites: `Tips` /// # </weight> - #[pallet::weight(<T as Config>::WeightInfo::tip(T::Tippers::max_len() as u32))] + #[pallet::weight(<T as Config<I>>::WeightInfo::tip(T::Tippers::max_len() as u32))] pub fn tip( origin: OriginFor<T>, hash: T::Hash, - #[pallet::compact] tip_value: BalanceOf<T>, + #[pallet::compact] tip_value: BalanceOf<T, I>, ) -> DispatchResult { let tipper = ensure_signed(origin)?; ensure!(T::Tippers::contains(&tipper), BadOrigin); - let mut tip = Tips::<T>::get(hash).ok_or(Error::<T>::UnknownTip)?; + let mut tip = Tips::<T, I>::get(hash).ok_or(Error::<T, I>::UnknownTip)?; if Self::insert_tip_and_check_closing(&mut tip, tipper, tip_value) { Self::deposit_event(Event::TipClosing { tip_hash: hash }); } - Tips::<T>::insert(&hash, tip); + Tips::<T, I>::insert(&hash, tip); Ok(()) } @@ -412,16 +413,16 @@ pub mod pallet { /// - DbReads: `Tips`, `Tippers`, `tip finder` /// - DbWrites: `Reasons`, `Tips`, `Tippers`, `tip finder` /// # </weight> - #[pallet::weight(<T as Config>::WeightInfo::close_tip(T::Tippers::max_len() as u32))] + #[pallet::weight(<T as Config<I>>::WeightInfo::close_tip(T::Tippers::max_len() as u32))] pub fn close_tip(origin: OriginFor<T>, hash: T::Hash) -> DispatchResult { ensure_signed(origin)?; - let tip = Tips::<T>::get(hash).ok_or(Error::<T>::UnknownTip)?; - let n = tip.closes.as_ref().ok_or(Error::<T>::StillOpen)?; - ensure!(frame_system::Pallet::<T>::block_number() >= *n, Error::<T>::Premature); + let tip = Tips::<T, I>::get(hash).ok_or(Error::<T, I>::UnknownTip)?; + let n = tip.closes.as_ref().ok_or(Error::<T, I>::StillOpen)?; + ensure!(frame_system::Pallet::<T>::block_number() >= *n, Error::<T, I>::Premature); // closed. - Reasons::<T>::remove(&tip.reason); - Tips::<T>::remove(hash); + Reasons::<T, I>::remove(&tip.reason); + Tips::<T, I>::remove(hash); Self::payout_tip(hash, tip); Ok(()) } @@ -438,17 +439,17 @@ pub mod pallet { /// `T` is charged as upper bound given by `ContainsLengthBound`. /// The actual cost depends on the implementation of `T::Tippers`. /// # </weight> - #[pallet::weight(<T as Config>::WeightInfo::slash_tip(T::Tippers::max_len() as u32))] + #[pallet::weight(<T as Config<I>>::WeightInfo::slash_tip(T::Tippers::max_len() as u32))] pub fn slash_tip(origin: OriginFor<T>, hash: T::Hash) -> DispatchResult { T::RejectOrigin::ensure_origin(origin)?; - let tip = Tips::<T>::take(hash).ok_or(Error::<T>::UnknownTip)?; + let tip = Tips::<T, I>::take(hash).ok_or(Error::<T, I>::UnknownTip)?; if !tip.deposit.is_zero() { let imbalance = T::Currency::slash_reserved(&tip.finder, tip.deposit).0; T::OnSlash::on_unbalanced(imbalance); } - Reasons::<T>::remove(&tip.reason); + Reasons::<T, I>::remove(&tip.reason); Self::deposit_event(Event::TipSlashed { tip_hash: hash, finder: tip.finder, @@ -459,7 +460,7 @@ pub mod pallet { } } -impl<T: Config> Pallet<T> { +impl<T: Config<I>, I: 'static> Pallet<T, I> { // Add public immutables and private mutables. /// The account ID of the treasury pot. @@ -475,9 +476,9 @@ impl<T: Config> Pallet<T> { /// /// `O(T)` and one storage access. fn insert_tip_and_check_closing( - tip: &mut OpenTip<T::AccountId, BalanceOf<T>, T::BlockNumber, T::Hash>, + tip: &mut OpenTip<T::AccountId, BalanceOf<T, I>, T::BlockNumber, T::Hash>, tipper: T::AccountId, - tip_value: BalanceOf<T>, + tip_value: BalanceOf<T, I>, ) -> bool { match tip.tips.binary_search_by_key(&&tipper, |x| &x.0) { Ok(pos) => tip.tips[pos] = (tipper, tip_value), @@ -494,7 +495,7 @@ impl<T: Config> Pallet<T> { } /// Remove any non-members of `Tippers` from a `tips` vector. `O(T)`. - fn retain_active_tips(tips: &mut Vec<(T::AccountId, BalanceOf<T>)>) { + fn retain_active_tips(tips: &mut Vec<(T::AccountId, BalanceOf<T, I>)>) { let members = T::Tippers::sorted_members(); let mut members_iter = members.iter(); let mut member = members_iter.next(); @@ -520,14 +521,14 @@ impl<T: Config> Pallet<T> { /// Plus `O(T)` (`T` is Tippers length). fn payout_tip( hash: T::Hash, - tip: OpenTip<T::AccountId, BalanceOf<T>, T::BlockNumber, T::Hash>, + tip: OpenTip<T::AccountId, BalanceOf<T, I>, T::BlockNumber, T::Hash>, ) { let mut tips = tip.tips; Self::retain_active_tips(&mut tips); tips.sort_by_key(|i| i.1); let treasury = Self::account_id(); - let max_payout = pallet_treasury::Pallet::<T>::pot(); + let max_payout = pallet_treasury::Pallet::<T, I>::pot(); let mut payout = tips[tips.len() / 2].1.min(max_payout); if !tip.deposit.is_zero() { @@ -582,7 +583,7 @@ impl<T: Config> Pallet<T> { for (hash, old_tip) in storage_key_iter::< T::Hash, - OldOpenTip<T::AccountId, BalanceOf<T>, T::BlockNumber, T::Hash>, + OldOpenTip<T::AccountId, BalanceOf<T, I>, T::BlockNumber, T::Hash>, Twox64Concat, >(module, item) .drain() @@ -600,7 +601,7 @@ impl<T: Config> Pallet<T> { tips: old_tip.tips, finders_fee, }; - Tips::<T>::insert(hash, new_tip) + Tips::<T, I>::insert(hash, new_tip) } } } diff --git a/substrate/frame/tips/src/tests.rs b/substrate/frame/tips/src/tests.rs index 235952fd109..194ecc60d98 100644 --- a/substrate/frame/tips/src/tests.rs +++ b/substrate/frame/tips/src/tests.rs @@ -25,7 +25,7 @@ use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BadOrigin, BlakeTwo256, IdentityLookup}, - Perbill, Permill, + BuildStorage, Perbill, Permill, }; use sp_storage::Storage; @@ -53,7 +53,9 @@ frame_support::construct_runtime!( System: frame_system::{Pallet, Call, Config, Storage, Event<T>}, Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>}, Treasury: pallet_treasury::{Pallet, Call, Storage, Config, Event<T>}, + Treasury1: pallet_treasury::<Instance1>::{Pallet, Call, Storage, Config, Event<T>}, Tips: pallet_tips::{Pallet, Call, Storage, Event<T>}, + Tips1: pallet_tips::<Instance1>::{Pallet, Call, Storage, Event<T>}, } ); @@ -127,6 +129,7 @@ parameter_types! { pub const ProposalBond: Permill = Permill::from_percent(5); pub const Burn: Permill = Permill::from_percent(50); pub const TreasuryPalletId: PalletId = PalletId(*b"py/trsry"); + pub const TreasuryPalletId2: PalletId = PalletId(*b"py/trsr2"); } impl pallet_treasury::Config for Test { type PalletId = TreasuryPalletId; @@ -146,6 +149,26 @@ impl pallet_treasury::Config for Test { type MaxApprovals = ConstU32<100>; type SpendOrigin = frame_support::traits::NeverEnsureOrigin<u64>; } + +impl pallet_treasury::Config<Instance1> for Test { + type PalletId = TreasuryPalletId2; + type Currency = pallet_balances::Pallet<Test>; + type ApproveOrigin = frame_system::EnsureRoot<u128>; + type RejectOrigin = frame_system::EnsureRoot<u128>; + type Event = Event; + type OnSlash = (); + type ProposalBond = ProposalBond; + type ProposalBondMinimum = ConstU64<1>; + type ProposalBondMaximum = (); + type SpendPeriod = ConstU64<2>; + type Burn = Burn; + type BurnDestination = (); // Just gets burned. + type WeightInfo = (); + type SpendFunds = (); + type MaxApprovals = ConstU32<100>; + type SpendOrigin = frame_support::traits::NeverEnsureOrigin<u64>; +} + parameter_types! { pub const TipFindersFee: Percent = Percent::from_percent(20); } @@ -160,16 +183,29 @@ impl Config for Test { type WeightInfo = (); } +impl Config<Instance1> for Test { + type MaximumReasonLength = ConstU32<16384>; + type Tippers = TenToFourteen; + type TipCountdown = ConstU64<1>; + type TipFindersFee = TipFindersFee; + type TipReportDepositBase = ConstU64<1>; + type DataDepositPerByte = ConstU64<1>; + type Event = Event; + type WeightInfo = (); +} + pub fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap(); - pallet_balances::GenesisConfig::<Test> { - // Total issuance will be 200 with treasury account initialized at ED. - balances: vec![(0, 100), (1, 98), (2, 1)], + let mut ext: sp_io::TestExternalities = GenesisConfig { + system: frame_system::GenesisConfig::default(), + balances: pallet_balances::GenesisConfig { balances: vec![(0, 100), (1, 98), (2, 1)] }, + treasury: Default::default(), + treasury_1: Default::default(), } - .assimilate_storage(&mut t) - .unwrap(); - GenesisBuild::<Test>::assimilate_storage(&pallet_treasury::GenesisConfig, &mut t).unwrap(); - t.into() + .build_storage() + .unwrap() + .into(); + ext.execute_with(|| System::set_block_number(1)); + ext } fn last_event() -> TipEvent<Test> { @@ -540,3 +576,36 @@ fn genesis_funding_works() { assert_eq!(Treasury::pot(), initial_funding - Balances::minimum_balance()); }); } + +#[test] +fn report_awesome_and_tip_works_second_instance() { + new_test_ext().execute_with(|| { + Balances::make_free_balance_be(&Treasury::account_id(), 101); + Balances::make_free_balance_be(&Treasury1::account_id(), 201); + assert_eq!(Balances::free_balance(&Treasury::account_id()), 101); + assert_eq!(Balances::free_balance(&Treasury1::account_id()), 201); + + assert_ok!(Tips1::report_awesome(Origin::signed(0), b"awesome.dot".to_vec(), 3)); + // duplicate report in tips1 reports don't count. + assert_noop!( + Tips1::report_awesome(Origin::signed(1), b"awesome.dot".to_vec(), 3), + Error::<Test, Instance1>::AlreadyKnown + ); + // but tips is separate + assert_ok!(Tips::report_awesome(Origin::signed(0), b"awesome.dot".to_vec(), 3)); + + let h = tip_hash(); + assert_ok!(Tips1::tip(Origin::signed(10), h.clone(), 10)); + assert_ok!(Tips1::tip(Origin::signed(11), h.clone(), 10)); + assert_ok!(Tips1::tip(Origin::signed(12), h.clone(), 10)); + assert_noop!(Tips1::tip(Origin::signed(9), h.clone(), 10), BadOrigin); + + System::set_block_number(2); + + assert_ok!(Tips1::close_tip(Origin::signed(100), h.into())); + // Treasury 1 unchanged + assert_eq!(Balances::free_balance(&Treasury::account_id()), 101); + // Treasury 2 gave the funds + assert_eq!(Balances::free_balance(&Treasury1::account_id()), 191); + }); +} -- GitLab