diff --git a/control/src/benchmarking.rs b/control/src/benchmarking.rs index 9c70fdea4..28a3a16a9 100644 --- a/control/src/benchmarking.rs +++ b/control/src/benchmarking.rs @@ -3,19 +3,25 @@ use crate::*; use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite, whitelisted_caller}; use frame_system::RawOrigin; -use sp_runtime::{DispatchError, traits::{Saturating}}; +use sp_runtime::{DispatchError, traits::SaturatedConversion}; use sp_std::vec; const SEED: u32 = 0; +const DEPOSIT_AMOUNT: u128 = 10_000_000_000_000_000_000; + + +/// Fund account with tokens, needed for org and campaign interactions +fn fund_account(account_id: &T::AccountId) -> Result<(), DispatchError> { + let balance_amount: T::Balance = DEPOSIT_AMOUNT.saturated_into(); + T::Currency::deposit(T::ProtocolTokenId::get(), account_id, balance_amount)?; + T::Currency::deposit(T::PaymentTokenId::get(), account_id, balance_amount)?; + Ok(()) +} -/// Fund accounts with tokens, needed for org interactions fn fund_accounts(account_ids: &Vec) -> Result<(), DispatchError> { - let multiplier: u32 = 2; - let amount = T::MinimumDeposit::get().saturating_mul(multiplier.into()); for account_id in account_ids { - T::Currency::deposit(T::ProtocolTokenId::get(), account_id, amount)?; - T::Currency::deposit(T::PaymentTokenId::get(), account_id, amount)?; + fund_account::(&account_id)?; } Ok(()) } @@ -25,7 +31,7 @@ benchmarks! { create_org { let caller: T::AccountId = whitelisted_caller(); - fund_accounts::(&vec![caller.clone()])?; + fund_account::(&caller)?; let org_nonce = Nonce::::get(); }: _( RawOrigin::Signed(caller.clone()), @@ -47,26 +53,33 @@ benchmarks! { disable_org { let caller: T::AccountId = whitelisted_caller(); - fund_accounts::(&vec![caller.clone()])?; + fund_account::(&caller)?; let org_id = as ControlBenchmarkingTrait>::create_org(caller.clone()).unwrap(); - }: _(RawOrigin::Root, org_id) + }: _( + RawOrigin::Root, + org_id + ) verify { assert!(OrgState::::get(org_id) == ControlState::Inactive); } enable_org { let caller: T::AccountId = whitelisted_caller(); - fund_accounts::(&vec![caller.clone()])?; + fund_account::(&caller)?; let org_id = as ControlBenchmarkingTrait>::create_org(caller.clone()).unwrap(); - Pallet::::disable_org(RawOrigin::Root.into(), org_id); - }: _(RawOrigin::Root, org_id) + Pallet::::disable_org(RawOrigin::Root.into(), org_id)?; + }: _( + RawOrigin::Root, + org_id + ) verify { assert!(OrgState::::get(org_id) == ControlState::Active); } add_member { - let r in 1 .. T::MaxMembersPerDAO::get()-1; + let r in 1 .. T::MaxMembersPerDAO::get()-1; // Limit members per org + // Prepare org creator and members let creator: T::AccountId = whitelisted_caller(); let member: T::AccountId = account("member", 0, SEED); let accounts: Vec = (1..r) @@ -76,38 +89,53 @@ benchmarks! { .collect(); fund_accounts::(&vec![creator.clone(), member.clone()])?; fund_accounts::(&accounts)?; + + // Create org and fill with members let org_id = as ControlBenchmarkingTrait>::create_org(creator.clone()).unwrap(); Pallet::::fill_org_with_members(&org_id, &accounts)?; - }: _(RawOrigin::Signed(creator), org_id, member.clone()) + }: _( + RawOrigin::Signed(creator), + org_id, + member.clone() + ) verify { assert!(OrgMembers::::get(&org_id).contains(&member)); } remove_member { - let r in 1 .. T::MaxMembersPerDAO::get(); + let r in 1 .. T::MaxMembersPerDAO::get(); // Limit members per org + // Prepare org creator and members let creator: T::AccountId = whitelisted_caller(); - fund_accounts::(&vec![creator.clone()])?; + fund_account::(&creator)?; let org_id = as ControlBenchmarkingTrait>::create_org(creator.clone()).unwrap(); - let origin = RawOrigin::Signed(creator.clone()); let accounts: Vec = (1..r+1) .collect::>() .iter() .map(|i| account("member", *i, SEED)) .collect(); fund_accounts::(&accounts)?; + + // Add members to org Pallet::::fill_org_with_members(&org_id, &accounts)?; - }: _(origin, org_id, accounts[0].clone()) + }: _( + RawOrigin::Signed(creator), + org_id, + accounts[0].clone() + ) verify { assert!(!OrgMembers::::get(&org_id).contains(&accounts[0])); } check_membership { let caller: T::AccountId = whitelisted_caller(); - fund_accounts::(&vec![caller.clone()])?; + fund_account::(&caller)?; let org_id = as ControlBenchmarkingTrait>::create_org(caller.clone()).unwrap(); - }: _(RawOrigin::Signed(caller.clone()), org_id, caller.clone()) - + }: _( + RawOrigin::Signed(caller.clone()), + org_id, + caller.clone() + ) } diff --git a/flow/src/benchmarking.rs b/flow/src/benchmarking.rs index 7fcdbd362..d654a2361 100644 --- a/flow/src/benchmarking.rs +++ b/flow/src/benchmarking.rs @@ -10,7 +10,7 @@ use sp_std::vec; const SEED: u32 = 0; -const DEPOSIT_AMOUNT: u128 = 10000000000000000000; +const DEPOSIT_AMOUNT: u128 = 10_000_000_000_000_000_000; /// Fund account with tokens, needed for org and campaign interactions @@ -31,36 +31,7 @@ fn fund_accounts(account_ids: &Vec) -> Result<(), Dispa /// Switch to next block number fn next_block() { let current_block = frame_system::Pallet::::block_number(); - frame_system::Pallet::::set_block_number(current_block.saturating_add(1_u32.into())); -} - -/// Execute `create_campaign` extrinsic and return id of created campaign object -fn create_campaign_call(caller: T::AccountId, org_id: T::Hash) -> Result { - let name: Vec = vec![0; T::MaxNameLength::get() as usize]; - let cid: Vec = vec![0; T::MaxNameLength::get() as usize]; - let token_symbol: Vec = vec![0; 5]; - let token_name: Vec = vec![0; 32]; - let target: T::Balance = T::MinContribution::get(); - let deposit: T::Balance = T::MinContribution::get(); - let expiry: T::BlockNumber = frame_system::Pallet::::block_number() + 200_u32.into(); - let protocol: FlowProtocol = FlowProtocol::default(); - let governance: FlowGovernance = FlowGovernance::default(); - let nonce = Nonce::::get(); - Flow::::create_campaign( - RawOrigin::Signed(caller.clone()).into(), - org_id, - caller.clone(), - name, - target, - deposit, - expiry, - protocol, - governance, - cid, - token_name, - token_symbol - )?; - Ok(T::Hashing::hash_of(&nonce)) + frame_system::Pallet::::set_block_number(current_block + 1_u32.into()); } @@ -80,7 +51,7 @@ benchmarks! { // Create some campaigns, respecting per block limitation for i in 0 .. b { - create_campaign_call::(caller.clone(), org_id)?; + as FlowBenchmarkingTrait>::create_campaign(&caller, &org_id)?; if i % per_block_cnt == 0 { next_block::(); } @@ -120,7 +91,7 @@ benchmarks! { // Create some campaigns, respecting per block limitation for i in 0 .. b { - create_campaign_call::(caller.clone(), org_id)?; + as FlowBenchmarkingTrait>::create_campaign(&caller, &org_id)?; if i % per_block_cnt == 0 { next_block::(); } @@ -151,7 +122,7 @@ benchmarks! { fund_account::(&treasury_id)?; // Create campaign to use for contributions - let campaign_id = create_campaign_call::(owner, org_id)?; + let campaign_id = as FlowBenchmarkingTrait>::create_campaign(&owner, &org_id)?; }: _( RawOrigin::Signed(contributor.clone()), campaign_id.clone(), @@ -173,21 +144,21 @@ benchmarks! { fund_account::(&treasury_id)?; // Create campaign and add some contributions - let campaign_id = create_campaign_call::(owner.clone(), org_id.clone())?; + let campaign_id = as FlowBenchmarkingTrait>::create_campaign(&owner, &org_id)?; for i in 0 .. c { let account: T::AccountId = account("contributor", i, SEED); fund_account::(&account)?; - Pallet::::contribute(RawOrigin::Signed(account).into(), campaign_id.clone(), T::MinContribution::get())?; + Flow::::contribute(RawOrigin::Signed(account).into(), campaign_id.clone(), T::MinContribution::get())?; } // Trigger on_finalize to prepare object to be used in initialize hook let mut block_number = Campaigns::::get(&campaign_id).expiry; frame_system::Pallet::::set_block_number(block_number.clone()); - Pallet::::on_finalize(block_number.clone()); + Flow::::on_finalize(block_number.clone()); block_number = block_number.saturating_add(1_u32.into()); frame_system::Pallet::::set_block_number(block_number); }: { - Pallet::::on_initialize(block_number); + Flow::::on_initialize(block_number); } } diff --git a/flow/src/lib.rs b/flow/src/lib.rs index dc0f5ecf6..6a6d0732a 100644 --- a/flow/src/lib.rs +++ b/flow/src/lib.rs @@ -51,20 +51,20 @@ pub mod weights; // mod errors; use frame_support::{ - dispatch::{DispatchResult, DispatchResultWithPostInfo}, - traits::{Get, BalanceStatus, StorageVersion, UnixTime}, + dispatch::{DispatchError, DispatchResult, DispatchResultWithPostInfo}, + traits::{Get, BalanceStatus, Hooks, StorageVersion, UnixTime}, transactional, weights::Weight }; use scale_info::TypeInfo; -use sp_runtime::{traits::{AtLeast32BitUnsigned, Hash}, Permill}; -use sp_std::vec::Vec; +use sp_runtime::{traits::{AtLeast32BitUnsigned, Hash, Saturating}, Permill}; +use sp_std::{vec, vec::Vec}; use sp_std::convert::TryFrom; use codec::HasCompact; -use gamedao_traits::{ControlTrait, ControlBenchmarkingTrait, FlowTrait}; +use gamedao_traits::{ControlTrait, ControlBenchmarkingTrait, FlowTrait, FlowBenchmarkingTrait}; use orml_traits::{MultiCurrency, MultiReservableCurrency}; pub use pallet::*; @@ -1021,3 +1021,58 @@ impl FlowTrait for Pallet { CampaignState::::get(campaign_id) == FlowState::Success } } + +impl FlowBenchmarkingTrait for Pallet { + /// ** Should be used for benchmarking only!!! ** + #[cfg(feature = "runtime-benchmarks")] + fn create_campaign(caller: &T::AccountId, org_id: &T::Hash) -> Result { + let name: Vec = vec![0; T::MaxNameLength::get() as usize]; + let cid: Vec = vec![0; T::MaxNameLength::get() as usize]; + let token_symbol: Vec = vec![0; 5]; + let token_name: Vec = vec![0; 32]; + let target: T::Balance = T::MinContribution::get(); + let deposit: T::Balance = T::MinContribution::get(); + let expiry: T::BlockNumber = frame_system::Pallet::::block_number() + 200_u32.into(); + let protocol: FlowProtocol = FlowProtocol::default(); + let governance: FlowGovernance = FlowGovernance::default(); + let nonce = Nonce::::get(); + Pallet::::create_campaign( + frame_system::RawOrigin::Signed(caller.clone()).into(), + *org_id, + caller.clone(), + name, + target, + deposit, + expiry, + protocol, + governance, + cid, + token_name, + token_symbol + )?; + Ok(T::Hashing::hash_of(&nonce)) + } + + /// ** Should be used for benchmarking only!!! ** + #[cfg(feature = "runtime-benchmarks")] + fn create_contributions(campaign_id: &T::Hash, contributors: &Vec) -> Result<(), DispatchError> { + for account_id in contributors { + Pallet::::contribute( + frame_system::RawOrigin::Signed(account_id.clone()).into(), + campaign_id.clone(), + T::MinContribution::get() + )?; + } + Ok(()) + } + + /// ** Should be used for benchmarking only!!! ** + #[cfg(feature = "runtime-benchmarks")] + fn finalize_campaigns_by_block(block_number: T::BlockNumber) { + frame_system::Pallet::::set_block_number(block_number); + Pallet::::on_finalize(block_number); + let next_block = block_number.saturating_add(1_u32.into()); + frame_system::Pallet::::set_block_number(next_block); + Pallet::::on_initialize(next_block); + } +} diff --git a/signal/src/benchmarking.rs b/signal/src/benchmarking.rs index 9b86b3dd2..fb3febf9d 100644 --- a/signal/src/benchmarking.rs +++ b/signal/src/benchmarking.rs @@ -1,49 +1,223 @@ #![cfg(feature = "runtime-benchmarks")] -use frame_benchmarking::{account, benchmarks, whitelisted_caller}; +use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite, whitelisted_caller}; use frame_system::RawOrigin; +use frame_support::{dispatch::DispatchError, traits::Get}; +use sp_runtime::traits::{SaturatedConversion}; +// use sp_std::vec; + +use crate::*; + + +const SEED: u32 = 0; +const DEPOSIT_AMOUNT: u128 = 10_000_000_000_000_000_000; + + +/// Fund account with tokens, needed for org and campaign interactions +fn fund_account(account_id: &T::AccountId) -> Result<(), DispatchError> { + let balance_amount: T::Balance = DEPOSIT_AMOUNT.saturated_into(); + T::Currency::deposit(T::ProtocolTokenId::get(), account_id, balance_amount)?; + T::Currency::deposit(T::PaymentTokenId::get(), account_id, balance_amount)?; + Ok(()) +} + +fn fund_accounts(account_ids: &Vec) -> Result<(), DispatchError> { + for account_id in account_ids { + fund_account::(&account_id)?; + } + Ok(()) +} + + +fn create_and_finalize_campaign(caller: T::AccountId, contributors_count: u32) -> Result { + let org_id = T::Control::create_org(caller.clone())?; + let treasury_account_id = T::Control::org_treasury_account(&org_id); + fund_account::(&treasury_account_id)?; + let campaign_id = T::Flow::create_campaign(&caller, &org_id)?; + let contributors: Vec = (0..contributors_count).collect::>().iter() + .map(|i| account("contributor", *i, SEED)).collect(); + fund_accounts::(&contributors)?; + T::Flow::create_contributions(&campaign_id, &contributors)?; + let current_block = frame_system::Pallet::::block_number(); + let mut expiry = current_block + 200_u32.into(); + for _ in 0 .. 10 { + T::Flow::finalize_campaigns_by_block(expiry); + if T::Flow::is_campaign_succeeded(&campaign_id) { + break; + } + expiry = expiry + 1_u32.into(); + } + Ok(campaign_id) +} -use crate::{ Pallet as Signal, Event, Config }; -use pallet_control::{ Pallet as Control, Config as ControlConfig, Event as ControlEvent, OrgType, AccessModel, FeeModel }; -use pallet_flow::Pallet as Flow; -use frame_support::{ assert_ok }; -use zero_primitives::{ AccountId }; benchmarks! { general_proposal { - let caller = whitelisted_caller(); - let origin = RawOrigin::Signed(caller); - let controller: T::AccountId = account("controller", 1, 0); - let treasury: T::AccountId = account("treasury", 1, 0); - Control::create( - origin, - controller, - treasury, - vec![1,2,3], - vec![1,2,3], - OrgType::Individual, - AccessModel::Open, - FeeModel::NoFees, - 0, 1, 2, 100 + let caller: T::AccountId = whitelisted_caller(); + fund_account::(&caller)?; + let org_id = T::Control::create_org(caller.clone())?; + let start = frame_system::Pallet::::block_number() + 1_u32.into(); + let expiry = start + 100_u32.into(); + let proposal_id = T::Hashing::hash_of(&Nonce::::get()); + }: _( + RawOrigin::Signed(caller.clone()), + org_id.clone(), + (0..255).collect(), + (0..255).collect(), + start, + expiry + ) + verify { + assert!(Proposals::::contains_key(&proposal_id)); + } + + membership_proposal { + let caller: T::AccountId = whitelisted_caller(); + fund_account::(&caller)?; + let org_id = T::Control::create_org(caller.clone())?; + let start = frame_system::Pallet::::block_number() + 1_u32.into(); + let expiry = start + 100_u32.into(); + let member: T::AccountId = account("member", 0, SEED); + let proposal_id = T::Hashing::hash_of(&Nonce::::get()); + }: _(RawOrigin::Signed(caller), org_id, member, 0, start, expiry) + + withdraw_proposal { + let caller: T::AccountId = whitelisted_caller(); + fund_account::(&caller)?; + let campaign_id = create_and_finalize_campaign::(caller.clone(), 10)?; + let proposal_id = T::Hashing::hash_of(&Nonce::::get()); + }: _( + RawOrigin::Signed(caller), + campaign_id.clone(), + (0..255).collect(), + (0..255).collect(), + T::Flow::campaign_balance(&campaign_id), + frame_system::Pallet::::block_number() + 1_u32.into(), + frame_system::Pallet::::block_number() + 200_u32.into() + ) + verify { + assert!(Proposals::::contains_key(&proposal_id)); + } + + simple_vote_general { + let b in 0 .. T::MaxVotesPerProposal::get()-1; // limit number of votes + + let caller: T::AccountId = whitelisted_caller(); + fund_account::(&caller)?; + let org_id = T::Control::create_org(caller.clone())?; + let start = frame_system::Pallet::::block_number() + 1_u32.into(); + let expiry = start + 500_u32.into(); + let proposal_id = T::Hashing::hash_of(&Nonce::::get()); + Pallet::::general_proposal( + RawOrigin::Signed(caller.clone()).into(), + org_id.clone(), + (0..255).collect(), + (0..255).collect(), + start, + expiry )?; - let bn = frame_system::Pallet::block_number(); - let org_event: ControlEvent = >::events().pop() - .expect("No event generated").event.into(); - // let Event::Control(ControlEvent::OrgCreated {sender_id, org_id, ..}) = org_event; - if let Event::Control(ControlEvent::OrgCreated {org_id, ..}) = org_event { - println!("Okay!"); - } else { - println!("Fail"); + let voters: Vec = (0..b).collect::>().iter() + .map(|i| account("voter", *i, SEED)).collect(); + fund_accounts::(&voters)?; + for voter in voters.iter().step_by(2) { + Pallet::::simple_vote(RawOrigin::Signed(voter.clone()).into(), proposal_id, true)?; } - // } + for voter in voters.iter().skip(1).step_by(2) { + Pallet::::simple_vote(RawOrigin::Signed(voter.clone()).into(), proposal_id, false)?; + } + let votes_count_before = ProposalVotes::::get(&proposal_id); + }: { + Pallet::::simple_vote( + RawOrigin::Signed(caller.clone()).into(), + proposal_id.clone(), + true + )?; + } + verify { + assert!(ProposalVotes::::get(&proposal_id) == votes_count_before + 1); + } + + simple_vote_withdraw { + let b in 0 .. T::MaxVotesPerProposal::get()-1; + + let caller: T::AccountId = whitelisted_caller(); + fund_account::(&caller)?; - }: _(caller, org_id.unwrap(), vec![1,2,3], vec![1,2,3], bn, bn + 10) - // verify { - // let proposal_event = System::events().pop() - // .expect("No event generated").event; - // // assert!(proposal_event as SignalEvent).proposal_id.len(); - // } + // Create campaign and withdraw proposal + let campaign_id = create_and_finalize_campaign::(caller.clone(), 10)?; + let withdraw_amount = T::Flow::campaign_balance(&campaign_id); + let start = frame_system::Pallet::::block_number() + 1_u32.into(); + let expiry = start + 200_u32.into(); + let proposal_id = T::Hashing::hash_of(&Nonce::::get()); + Pallet::::withdraw_proposal( + RawOrigin::Signed(caller.clone()).into(), + campaign_id, + (0..255).collect(), + (0..255).collect(), + withdraw_amount, + start, + expiry + )?; + + let voters: Vec = (0 .. b).collect::>().iter() + .map(|i| account("voter", *i, SEED)).collect(); + fund_accounts::(&voters)?; + for voter in voters { + Pallet::::simple_vote(RawOrigin::Signed(voter.clone()).into(), proposal_id.clone(), false)?; + } + let votes_count_before = ProposalVotes::::get(&proposal_id); - impl_benchmark_test_suite!(Signal, crate::tests::new_test_ext(), crate::tests::Test) + }: { + Pallet::::simple_vote( + RawOrigin::Signed(caller.clone()).into(), + proposal_id.clone(), + true + )?; + } + verify { + assert!(ProposalVotes::::get(&proposal_id) == votes_count_before + 1); + } + + unlock_balance { + let b in 2 .. 100; + + let caller: T::AccountId = whitelisted_caller(); + fund_account::(&caller)?; + + // Create campaign and withdraw proposal + let campaign_id = create_and_finalize_campaign::(caller.clone(), b*2)?; + let withdraw_amount = T::Flow::campaign_balance(&campaign_id); + let start = frame_system::Pallet::::block_number() + 1_u32.into(); + let expiry = start + 200_u32.into(); + let proposal_id = T::Hashing::hash_of(&Nonce::::get()); + Pallet::::withdraw_proposal( + RawOrigin::Signed(caller.clone()).into(), + campaign_id, + (0..255).collect(), + (0..255).collect(), + withdraw_amount, + start, + expiry + )?; + + let voters: Vec = (0 .. b).collect::>().iter() + .map(|i| account("voter", *i, SEED)).collect(); + fund_accounts::(&voters)?; + for voter in voters { + Pallet::::simple_vote(RawOrigin::Signed(voter.clone()).into(), proposal_id.clone(), true)?; + } + ProposalSimpleVotes::::insert(proposal_id, (b as u64 + 1, 0)); + let proposal = Proposals::::get(&proposal_id).unwrap(); + }: { + Pallet::::unlock_balance( + &proposal, + 0 + ); + } + verify { + assert!(ProposalStates::::get(&proposal_id) == ProposalState::Finalized); + } } + +impl_benchmark_test_suite!(Signal, crate::tests::new_test_ext(), crate::tests::Test); diff --git a/signal/src/lib.rs b/signal/src/lib.rs index a8c61e424..9d478464e 100644 --- a/signal/src/lib.rs +++ b/signal/src/lib.rs @@ -20,25 +20,28 @@ pub mod migration; pub mod mock; #[cfg(test)] mod tests; -// #[cfg(feature = "runtime-benchmarks")] -// mod benchmarking; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; -use frame_support::{{traits::StorageVersion}, transactional, dispatch::DispatchResult, weights::{GetDispatchInfo, Weight}}; -use frame_system::{ensure_signed, WeightInfo}; +use frame_support::{traits::StorageVersion, transactional, dispatch::DispatchResult, weights::Weight}; +use frame_system::{ensure_signed}; use orml_traits::{MultiCurrency, MultiReservableCurrency}; use sp_runtime::traits::{AtLeast32BitUnsigned, CheckedAdd, CheckedSub, Zero, Hash}; use sp_std::vec::Vec; use codec::HasCompact; -use gamedao_traits::{ControlTrait, FlowTrait}; +use gamedao_traits::{ControlTrait, ControlBenchmarkingTrait, FlowTrait, FlowBenchmarkingTrait}; use types::{Proposal, ProposalMetadata, ProposalState, ProposalType, VotingType}; pub use pallet::*; +pub use weights::WeightInfo; #[frame_support::pallet] pub mod pallet { use super::*; + use core::convert::TryInto; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; @@ -55,7 +58,7 @@ pub mod pallet { type Event: From> + IsType<::Event> + Into<::Event>; - + /// The units in which we record balances. type Balance: Member + Parameter @@ -68,7 +71,7 @@ pub mod pallet { + MaybeSerializeDeserialize + MaxEncodedLen + TypeInfo; - + /// The currency ID type type CurrencyId: Member + Parameter @@ -78,20 +81,26 @@ pub mod pallet { + MaybeSerializeDeserialize + MaxEncodedLen + TypeInfo; - + /// Multi-currency support for asset management. type Currency: MultiCurrency + MultiReservableCurrency; - + /// Control pallet's public interface. - type Control: ControlTrait; + type Control: ControlTrait + + ControlBenchmarkingTrait; /// Flow pallet's public interface. - type Flow: FlowTrait; + type Flow: FlowTrait + + FlowBenchmarkingTrait; /// Weight information for extrinsics in this module. type WeightInfo: WeightInfo; + /// Max number of votes per proposal + #[pallet::constant] + type MaxVotesPerProposal: Get; + /// The max number of proposals per one block. #[pallet::constant] type MaxProposalsPerBlock: Get; // 3 @@ -292,7 +301,7 @@ pub mod pallet { // TODO: ProposalTotalEligibleWeight /// Nonce. Increase per each proposal creation. - /// + /// /// Nonce: u128 #[pallet::storage] #[pallet::getter(fn nonce)] @@ -372,6 +381,8 @@ pub mod pallet { BalanceInsufficient, /// Too many proposals for block. TooManyProposals, + /// Too many votes for proposal + TooManyVotes, /// Proposal has no owner. NoProposalOwner, /// Overflow error. @@ -398,7 +409,7 @@ pub mod pallet { /// Emits `Proposal` event when successful. /// /// Weight: O(1) - #[pallet::weight(5_000_000)] + #[pallet::weight(T::WeightInfo::general_proposal())] #[transactional] pub fn general_proposal( origin: OriginFor, @@ -457,7 +468,7 @@ pub mod pallet { /// Emits `Proposal` event when successful. /// /// Weight: - #[pallet::weight(5_000_000)] + #[pallet::weight(T::WeightInfo::membership_proposal())] pub fn membership_proposal( origin: OriginFor, org_id: T::Hash, @@ -480,7 +491,7 @@ pub mod pallet { } /// Create a withdrawal proposal - /// + /// /// Origin must be controller of the campaign == controller of the dao /// beneficiary must be the treasury of the dao. /// @@ -494,7 +505,7 @@ pub mod pallet { /// Emits `ProposalCreated` event when successful. /// /// Weight: O(1) - #[pallet::weight(5_000_000)] + #[pallet::weight(T::WeightInfo::withdraw_proposal())] #[transactional] pub fn withdraw_proposal( origin: OriginFor, @@ -513,8 +524,8 @@ pub mod pallet { ensure!(sender == owner, Error::::AuthorizationError); let current_block = >::block_number(); - ensure!(start > current_block, Error::::OutOfBounds ); - ensure!(expiry > start, Error::::OutOfBounds ); + ensure!(start > current_block, Error::::OutOfBounds); + ensure!(expiry > start, Error::::OutOfBounds); ensure!( expiry <= current_block + ProposalTimeLimit::::get(), Error::::OutOfBounds @@ -532,7 +543,7 @@ pub mod pallet { (proposals.len() as u32) < T::MaxProposalsPerBlock::get(), Error::::TooManyProposals ); - + let nonce = Self::get_and_increment_nonce(); let proposal_id = T::Hashing::hash_of(&nonce); ensure!(!Proposals::::contains_key(&proposal_id), Error::::ProposalExists); @@ -541,7 +552,7 @@ pub mod pallet { Self::create_proposal( &sender, proposal_id, org_id, Some(campaign_id), title, cid, amount, ProposalType::Withdrawal, VotingType::Simple, start, expiry - ); + )?; Self::deposit_event(Event::::ProposalCreated { sender_id: sender, @@ -567,15 +578,36 @@ pub mod pallet { /// Emits `ProposalVoted` event when successful. /// /// Weight: - #[pallet::weight(5_000_000)] - pub fn simple_vote(origin: OriginFor, proposal_id: T::Hash, vote: bool) -> DispatchResult { + #[pallet::weight( + T::WeightInfo::simple_vote_general( + T::MaxVotesPerProposal::get() + ).max( + T::WeightInfo::simple_vote_withdraw( + T::MaxVotesPerProposal::get() + ).saturating_add( + T::WeightInfo::unlock_balance( + T::MaxVotesPerProposal::get()/2 + ) + ) + ) + )] + pub fn simple_vote(origin: OriginFor, proposal_id: T::Hash, vote: bool) -> DispatchResultWithPostInfo { let sender = ensure_signed(origin)?; let proposal = Proposals::::get(&proposal_id).ok_or(Error::::ProposalUnknown)?; // Ensure the proposal has not ended let proposal_state = ProposalStates::::get(&proposal_id); - ensure!(proposal_state == ProposalState::Active, Error::::ProposalEnded); + ensure!( + proposal_state == ProposalState::Active, + Error::::ProposalEnded + ); + + // Ensure proposal votes limit is not reached + ensure!( + ProposalVotes::::get(proposal_id) < T::MaxVotesPerProposal::get().into(), + Error::::TooManyVotes + ); // Ensure the contributor did not vote before ensure!( @@ -597,7 +629,7 @@ pub mod pallet { // sender.clone()); ensure!( sender_balance > T::Balance::from(0), "You are not // a contributor of this Campaign"); - Self::perform_vote(&sender, &proposal, vote)?; + let weight = Self::perform_vote(&sender, &proposal, vote)?; // dispatch vote event Self::deposit_event(Event::::ProposalVoted { @@ -605,7 +637,7 @@ pub mod pallet { proposal_id: proposal_id.clone(), vote, }); - Ok(()) + Ok(Some(weight).into()) } } @@ -715,7 +747,7 @@ pub mod pallet { // when the number of approvals is higher // than the number of rejections // accepted / denied >= 1 - fn unlock_balance(proposal: &Proposal, _supported_count: u64) -> Result<(), Error> { + pub(super) fn unlock_balance(proposal: &Proposal, _supported_count: u64) -> Result<(), Error> { let metadata = Metadata::::get(&proposal.proposal_id); // Ensure sufficient balance @@ -826,8 +858,9 @@ pub mod pallet { sender: &T::AccountId, proposal: &Proposal, vote: bool, - ) -> Result<(), Error> { + ) -> Result> { let proposal_id = &proposal.proposal_id; + let mut weight: Weight = 0; match &proposal.proposal_type { // DAO Democratic Proposal @@ -854,6 +887,7 @@ pub mod pallet { } ProposalSimpleVotes::::insert(proposal_id.clone(), (yes, no)); + weight = weight.saturating_add(yes).saturating_add(no).saturating_add(1); } // 50% majority over total number of campaign contributors ProposalType::Withdrawal => { @@ -876,6 +910,7 @@ pub mod pallet { // todo: if proposal finished ahead of expiry, probably remove it from expiry // proposals mapping? Self::unlock_balance(&proposal, updated_approvers)?; + weight = weight.saturating_add(T::WeightInfo::unlock_balance(yes.try_into().unwrap())); } } false => { @@ -889,6 +924,7 @@ pub mod pallet { } ProposalSimpleVotes::::insert(proposal_id.clone(), (yes, no)); + weight = weight.saturating_add(yes).saturating_add(no); } // Campaign Token Weighted Proposal @@ -914,6 +950,7 @@ pub mod pallet { ProposalsByVoterCount::::mutate(&sender, |v| *v += 1); ProposalVotesByVoters::::mutate(&proposal_id, |votings| votings.push((sender.clone(), vote.clone()))); ProposalsByVoter::::mutate(&sender, |votings| votings.push((proposal_id.clone(), vote))); + ProposalVotes::::insert(&proposal_id, ProposalVotes::::get(&proposal_id).saturating_add(1_u64)); let mut voters = ProposalVoters::::get(&proposal_id); match voters.binary_search(&sender) { @@ -923,7 +960,7 @@ pub mod pallet { ProposalVoters::::insert(&proposal_id, voters); } } - Ok(()) + Ok(weight) } fn get_and_increment_nonce() -> u128 { diff --git a/signal/src/migration.rs b/signal/src/migration.rs index 68b454930..1c06cd2bd 100644 --- a/signal/src/migration.rs +++ b/signal/src/migration.rs @@ -10,10 +10,8 @@ use crate::{Config, Pallet, Weight}; use frame_support::{ - storage::migration, traits::{Get, PalletInfoAccess}, }; -use sp_std::prelude::*; pub fn migrate() -> Weight { use frame_support::traits::StorageVersion; diff --git a/signal/src/mock.rs b/signal/src/mock.rs index 3a77befd6..a7d9e149a 100644 --- a/signal/src/mock.rs +++ b/signal/src/mock.rs @@ -173,6 +173,7 @@ parameter_types! { pub const MaxNameLength: u32 = 4; pub const MaxCampaignsPerAddress: u32 = 3; pub const MaxCampaignsPerBlock: u32 = 1; + pub const MaxCampaignsPerOrg: u32 = 64; pub const MaxContributionsPerBlock: u32 = 3; pub const MaxContributorsProcessing: u32 = 5; pub const MinCampaignDuration: BlockNumber = 1 * DAYS; @@ -198,6 +199,7 @@ impl gamedao_flow::Config for Test { type MaxContributorsProcessing = MaxContributorsProcessing; type MaxCampaignsPerAddress = MaxCampaignsPerAddress; type MaxCampaignsPerBlock = MaxCampaignsPerBlock; + type MaxCampaignsPerOrg = MaxCampaignsPerOrg; type MaxContributionsPerBlock = MaxContributionsPerBlock; type MinCampaignDuration = MinCampaignDuration; type MaxCampaignDuration = MaxCampaignDuration; @@ -209,12 +211,14 @@ impl gamedao_flow::Config for Test { parameter_types! { pub const MaxProposalsPerBlock: u32 = 2; pub const MaxProposalDuration: u32 = 20; + pub const MaxVotesPerProposal: u32 = 1000; } impl gamedao_signal::Config for Test { type Event = Event; type WeightInfo = (); type Control = Control; type Flow = Flow; + type MaxVotesPerProposal = MaxVotesPerProposal; type MaxProposalsPerBlock = MaxProposalsPerBlock; type MaxProposalDuration = MaxProposalDuration; type ProtocolTokenId = ProtocolTokenId; diff --git a/signal/src/tests.rs b/signal/src/tests.rs index d65911c69..add31dfea 100644 --- a/signal/src/tests.rs +++ b/signal/src/tests.rs @@ -700,6 +700,12 @@ fn signal_simple_vote_error() { Error::::AlreadyVoted ); + >::insert(proposal_id, ::MaxVotesPerProposal::get() as u64); + assert_noop!( + Signal::simple_vote(Origin::signed(ACC1), proposal_id, true), + Error::::TooManyVotes + ); + >::insert(proposal_id, ProposalState::Expired); assert_noop!( Signal::simple_vote(Origin::signed(ACC1), proposal_id, true), diff --git a/signal/src/weights.rs b/signal/src/weights.rs new file mode 100644 index 000000000..fd064fd76 --- /dev/null +++ b/signal/src/weights.rs @@ -0,0 +1,266 @@ +// This file is part of Substrate. + +// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for gamedao_signal +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-06-22, STEPS: `20`, REPEAT: 10, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 128 + +// Executed Command: +// ./target/release/subzero +// benchmark +// --pallet=gamedao_signal +// --extrinsic=* +// --steps=20 +// --repeat=10 +// --output=gamedao-protocol/signal/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for gamedao_signal. +pub trait WeightInfo { + fn general_proposal() -> Weight; + fn membership_proposal() -> Weight; + fn withdraw_proposal() -> Weight; + fn simple_vote_general(b: u32, ) -> Weight; + fn simple_vote_withdraw(b: u32, ) -> Weight; + fn unlock_balance(b: u32, ) -> Weight; +} + +/// Weights for gamedao_signal using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + // Storage: Control OrgState (r:1 w:0) + // Storage: Control OrgMemberState (r:1 w:0) + // Storage: Signal ProposalTimeLimit (r:1 w:0) + // Storage: Signal ProposalsByBlock (r:1 w:1) + // Storage: Signal Nonce (r:1 w:1) + // Storage: Signal Proposals (r:1 w:1) + // Storage: Signal ProposalsCount (r:1 w:1) + // Storage: Signal ProposalsByOrgCount (r:1 w:1) + // Storage: Signal ProposalsByOwnerCount (r:1 w:1) + // Storage: Signal ProposalsByOrg (r:1 w:1) + // Storage: Signal ProposalsArray (r:0 w:1) + // Storage: Signal ProposalsByOrgArray (r:0 w:1) + // Storage: Signal ProposalsIndex (r:0 w:1) + // Storage: Signal Owners (r:0 w:1) + // Storage: Signal ProposalsByOwnerIndex (r:0 w:1) + // Storage: Signal Metadata (r:0 w:1) + // Storage: Signal ProposalsByOwnerArray (r:0 w:1) + // Storage: Signal ProposalStates (r:0 w:1) + // Storage: Signal ProposalsByOrgIndex (r:0 w:1) + fn general_proposal() -> Weight { + (61_370_000 as Weight) + .saturating_add(T::DbWeight::get().reads(10 as Weight)) + .saturating_add(T::DbWeight::get().writes(16 as Weight)) + } + fn membership_proposal() -> Weight { + (1_123_000 as Weight) + } + // Storage: Flow CampaignState (r:1 w:0) + // Storage: Flow CampaignOwner (r:1 w:0) + // Storage: Signal ProposalTimeLimit (r:1 w:0) + // Storage: Signal CampaignBalanceUsed (r:1 w:0) + // Storage: Flow CampaignBalance (r:1 w:0) + // Storage: Signal ProposalsByBlock (r:1 w:1) + // Storage: Signal Nonce (r:1 w:1) + // Storage: Signal Proposals (r:1 w:1) + // Storage: Flow CampaignOrg (r:1 w:0) + // Storage: Signal ProposalsCount (r:1 w:1) + // Storage: Signal ProposalsByOrgCount (r:1 w:1) + // Storage: Signal ProposalsByOwnerCount (r:1 w:1) + // Storage: Signal ProposalsByOrg (r:1 w:1) + // Storage: Signal ProposalsArray (r:0 w:1) + // Storage: Signal ProposalsByOrgArray (r:0 w:1) + // Storage: Signal ProposalsIndex (r:0 w:1) + // Storage: Signal Owners (r:0 w:1) + // Storage: Signal ProposalsByOwnerIndex (r:0 w:1) + // Storage: Signal Metadata (r:0 w:1) + // Storage: Signal ProposalsByOwnerArray (r:0 w:1) + // Storage: Signal ProposalStates (r:0 w:1) + // Storage: Signal ProposalsByOrgIndex (r:0 w:1) + fn withdraw_proposal() -> Weight { + (79_144_000 as Weight) + .saturating_add(T::DbWeight::get().reads(13 as Weight)) + .saturating_add(T::DbWeight::get().writes(16 as Weight)) + } + // Storage: Signal Proposals (r:1 w:0) + // Storage: Signal ProposalStates (r:1 w:0) + // Storage: Signal ProposalVotes (r:1 w:1) + // Storage: Signal VotedBefore (r:1 w:1) + // Storage: Signal ProposalSimpleVotes (r:1 w:1) + // Storage: Signal ProposalApprovers (r:1 w:1) + // Storage: Signal ProposalsByVoterCount (r:1 w:1) + // Storage: Signal ProposalVotesByVoters (r:1 w:1) + // Storage: Signal ProposalsByVoter (r:1 w:1) + // Storage: Signal ProposalVoters (r:1 w:1) + fn simple_vote_general(b: u32, ) -> Weight { + (73_951_000 as Weight) + // Standard Error: 5_000 + .saturating_add((393_000 as Weight).saturating_mul(b as Weight)) + .saturating_add(T::DbWeight::get().reads(10 as Weight)) + .saturating_add(T::DbWeight::get().writes(8 as Weight)) + } + // Storage: Signal Proposals (r:1 w:0) + // Storage: Signal ProposalStates (r:1 w:0) + // Storage: Signal ProposalVotes (r:1 w:1) + // Storage: Signal VotedBefore (r:1 w:1) + // Storage: Signal ProposalSimpleVotes (r:1 w:1) + // Storage: Signal ProposalApprovers (r:1 w:1) + // Storage: Flow CampaignContributorsCount (r:1 w:0) + // Storage: Signal ProposalsByVoterCount (r:1 w:1) + // Storage: Signal ProposalVotesByVoters (r:1 w:1) + // Storage: Signal ProposalsByVoter (r:1 w:1) + // Storage: Signal ProposalVoters (r:1 w:1) + fn simple_vote_withdraw(b: u32, ) -> Weight { + (103_869_000 as Weight) + // Standard Error: 6_000 + .saturating_add((457_000 as Weight).saturating_mul(b as Weight)) + .saturating_add(T::DbWeight::get().reads(11 as Weight)) + .saturating_add(T::DbWeight::get().writes(8 as Weight)) + } + // Storage: Signal Metadata (r:1 w:0) + // Storage: Flow CampaignBalance (r:1 w:0) + // Storage: Signal CampaignBalanceUsed (r:1 w:1) + // Storage: Signal Owners (r:1 w:0) + // Storage: Flow CampaignOrg (r:1 w:0) + // Storage: Control OrgTreasury (r:1 w:0) + // Storage: Tokens Accounts (r:1 w:1) + // Storage: Signal ProposalStates (r:0 w:1) + fn unlock_balance(b: u32, ) -> Weight { + (90_153_000 as Weight) + // Standard Error: 68_000 + .saturating_add((151_000 as Weight).saturating_mul(b as Weight)) + .saturating_add(T::DbWeight::get().reads(7 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + // Storage: Control OrgState (r:1 w:0) + // Storage: Control OrgMemberState (r:1 w:0) + // Storage: Signal ProposalTimeLimit (r:1 w:0) + // Storage: Signal ProposalsByBlock (r:1 w:1) + // Storage: Signal Nonce (r:1 w:1) + // Storage: Signal Proposals (r:1 w:1) + // Storage: Signal ProposalsCount (r:1 w:1) + // Storage: Signal ProposalsByOrgCount (r:1 w:1) + // Storage: Signal ProposalsByOwnerCount (r:1 w:1) + // Storage: Signal ProposalsByOrg (r:1 w:1) + // Storage: Signal ProposalsArray (r:0 w:1) + // Storage: Signal ProposalsByOrgArray (r:0 w:1) + // Storage: Signal ProposalsIndex (r:0 w:1) + // Storage: Signal Owners (r:0 w:1) + // Storage: Signal ProposalsByOwnerIndex (r:0 w:1) + // Storage: Signal Metadata (r:0 w:1) + // Storage: Signal ProposalsByOwnerArray (r:0 w:1) + // Storage: Signal ProposalStates (r:0 w:1) + // Storage: Signal ProposalsByOrgIndex (r:0 w:1) + fn general_proposal() -> Weight { + (61_370_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(10 as Weight)) + .saturating_add(RocksDbWeight::get().writes(16 as Weight)) + } + fn membership_proposal() -> Weight { + (1_123_000 as Weight) + } + // Storage: Flow CampaignState (r:1 w:0) + // Storage: Flow CampaignOwner (r:1 w:0) + // Storage: Signal ProposalTimeLimit (r:1 w:0) + // Storage: Signal CampaignBalanceUsed (r:1 w:0) + // Storage: Flow CampaignBalance (r:1 w:0) + // Storage: Signal ProposalsByBlock (r:1 w:1) + // Storage: Signal Nonce (r:1 w:1) + // Storage: Signal Proposals (r:1 w:1) + // Storage: Flow CampaignOrg (r:1 w:0) + // Storage: Signal ProposalsCount (r:1 w:1) + // Storage: Signal ProposalsByOrgCount (r:1 w:1) + // Storage: Signal ProposalsByOwnerCount (r:1 w:1) + // Storage: Signal ProposalsByOrg (r:1 w:1) + // Storage: Signal ProposalsArray (r:0 w:1) + // Storage: Signal ProposalsByOrgArray (r:0 w:1) + // Storage: Signal ProposalsIndex (r:0 w:1) + // Storage: Signal Owners (r:0 w:1) + // Storage: Signal ProposalsByOwnerIndex (r:0 w:1) + // Storage: Signal Metadata (r:0 w:1) + // Storage: Signal ProposalsByOwnerArray (r:0 w:1) + // Storage: Signal ProposalStates (r:0 w:1) + // Storage: Signal ProposalsByOrgIndex (r:0 w:1) + fn withdraw_proposal() -> Weight { + (79_144_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(13 as Weight)) + .saturating_add(RocksDbWeight::get().writes(16 as Weight)) + } + // Storage: Signal Proposals (r:1 w:0) + // Storage: Signal ProposalStates (r:1 w:0) + // Storage: Signal ProposalVotes (r:1 w:1) + // Storage: Signal VotedBefore (r:1 w:1) + // Storage: Signal ProposalSimpleVotes (r:1 w:1) + // Storage: Signal ProposalApprovers (r:1 w:1) + // Storage: Signal ProposalsByVoterCount (r:1 w:1) + // Storage: Signal ProposalVotesByVoters (r:1 w:1) + // Storage: Signal ProposalsByVoter (r:1 w:1) + // Storage: Signal ProposalVoters (r:1 w:1) + fn simple_vote_general(b: u32, ) -> Weight { + (73_951_000 as Weight) + // Standard Error: 5_000 + .saturating_add((393_000 as Weight).saturating_mul(b as Weight)) + .saturating_add(RocksDbWeight::get().reads(10 as Weight)) + .saturating_add(RocksDbWeight::get().writes(8 as Weight)) + } + // Storage: Signal Proposals (r:1 w:0) + // Storage: Signal ProposalStates (r:1 w:0) + // Storage: Signal ProposalVotes (r:1 w:1) + // Storage: Signal VotedBefore (r:1 w:1) + // Storage: Signal ProposalSimpleVotes (r:1 w:1) + // Storage: Signal ProposalApprovers (r:1 w:1) + // Storage: Flow CampaignContributorsCount (r:1 w:0) + // Storage: Signal ProposalsByVoterCount (r:1 w:1) + // Storage: Signal ProposalVotesByVoters (r:1 w:1) + // Storage: Signal ProposalsByVoter (r:1 w:1) + // Storage: Signal ProposalVoters (r:1 w:1) + fn simple_vote_withdraw(b: u32, ) -> Weight { + (103_869_000 as Weight) + // Standard Error: 6_000 + .saturating_add((457_000 as Weight).saturating_mul(b as Weight)) + .saturating_add(RocksDbWeight::get().reads(11 as Weight)) + .saturating_add(RocksDbWeight::get().writes(8 as Weight)) + } + // Storage: Signal Metadata (r:1 w:0) + // Storage: Flow CampaignBalance (r:1 w:0) + // Storage: Signal CampaignBalanceUsed (r:1 w:1) + // Storage: Signal Owners (r:1 w:0) + // Storage: Flow CampaignOrg (r:1 w:0) + // Storage: Control OrgTreasury (r:1 w:0) + // Storage: Tokens Accounts (r:1 w:1) + // Storage: Signal ProposalStates (r:0 w:1) + fn unlock_balance(b: u32, ) -> Weight { + (90_153_000 as Weight) + // Standard Error: 68_000 + .saturating_add((151_000 as Weight).saturating_mul(b as Weight)) + .saturating_add(RocksDbWeight::get().reads(7 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } +} diff --git a/traits/src/lib.rs b/traits/src/lib.rs index 4b45f8a5e..4f69019e3 100644 --- a/traits/src/lib.rs +++ b/traits/src/lib.rs @@ -47,3 +47,23 @@ pub trait FlowTrait { fn campaign_org(campaign_id: &Hash) -> Hash; fn campaign_owner(campaign_id: &Hash) -> Option; } + +/// ** Should be used for benchmarking only!!! ** +pub trait FlowBenchmarkingTrait { + + /// Helper method to create campaign + /// ** Should be used for benchmarking only!!! ** + #[cfg(feature = "runtime-benchmarks")] + fn create_campaign(caller: &AccountId, org_id: &Hash) -> Result; + + /// Helper method to fill campaign with contributions + /// It is assumed those accounts have enought currency to contribute + /// ** Should be used for benchmarking only!!! ** + #[cfg(feature = "runtime-benchmarks")] + fn create_contributions(campaign_id: &Hash, contributors: &Vec) -> Result<(), DispatchError>; + + /// Trigger campaigns finalization by setting block number to specified value and calling appropriate hooks + /// ** Should be used for benchmarking only!!! ** + #[cfg(feature = "runtime-benchmarks")] + fn finalize_campaigns_by_block(block_number: BlockNumber); +}