diff --git a/flow/Cargo.toml b/flow/Cargo.toml index 217893af8..9b122f315 100644 --- a/flow/Cargo.toml +++ b/flow/Cargo.toml @@ -23,7 +23,11 @@ frame-system = { git = "https://github.com/paritytech/substrate", branch = "polk frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.13", default-features = false, optional = true } orml-traits = { path = "../../orml/traits", default-features = false } +orml-tokens = { path = "../../orml/tokens", optional = true } +orml-currencies = { path = "../../orml/currencies", optional = true } + gamedao-traits = { package = "gamedao-traits", path = "../traits", default-features = false } +gamedao-control = { package = "gamedao-control", path = "../control", optional = true } [dev-dependencies] sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.13" } @@ -33,14 +37,14 @@ sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0 pallet-timestamp = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.13" } pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.13" } -orml-tokens = { path = "../../orml/tokens", default-features = false } -orml-currencies = { path = "../../orml/currencies", default-features = false } - gamedao-control = { package = "gamedao-control", path = "../control", default-features = true } [features] default = ["std"] -runtime-benchmarks = ["frame-benchmarking"] +runtime-benchmarks = [ + "frame-benchmarking", + "gamedao-traits/frame-benchmarking", +] std = [ "codec/std", "serde/std", @@ -53,10 +57,12 @@ std = [ "sp-core/std", "sp-std/std", "sp-runtime/std", + "orml-traits/std", "orml-tokens/std", "orml-currencies/std", + "gamedao-traits/std", - "gamedao-control/std", + "gamedao-control/std", ] try-runtime = ["frame-support/try-runtime"] diff --git a/flow/src/benchmarking.rs b/flow/src/benchmarking.rs index 65ca2fe24..7fcdbd362 100644 --- a/flow/src/benchmarking.rs +++ b/flow/src/benchmarking.rs @@ -1,16 +1,194 @@ //! Benchmarking setup for gamedao-flow use super::*; -#[allow(unused)] use crate::Pallet as Flow; -use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite}; +use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite, whitelisted_caller}; use frame_system::RawOrigin; +use frame_support::traits::Hooks; +use sp_runtime::{DispatchError, traits::{Saturating, SaturatedConversion}}; use sp_std::vec; + +const SEED: u32 = 0; +const DEPOSIT_AMOUNT: u128 = 10000000000000000000; + + +/// 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(()) +} + +/// 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)) +} + + benchmarks! { - simple_benchmark {}: {} - // ::Currency::set_balance(GameCurrencyId, &creator, 1000); + create_campaign { + let b in 0 .. T::MaxCampaignsPerOrg::get()-1; // limit to total campaigns per organization + + let caller: T::AccountId = whitelisted_caller(); + fund_account::(&caller)?; + + // Prepare organization and treasury + let per_block_cnt = T::MaxCampaignsPerBlock::get(); + let org_id = T::Control::create_org(caller.clone().into())?; + let treasury_id = T::Control::org_treasury_account(&org_id); + fund_account::(&treasury_id)?; + + // Create some campaigns, respecting per block limitation + for i in 0 .. b { + create_campaign_call::(caller.clone(), org_id)?; + if i % per_block_cnt == 0 { + next_block::(); + } + } + + // Save number of existing campaigns to compare to new count after extrinsic called + let count_before = CampaignsCount::::get(); + }: _( + RawOrigin::Signed(caller.clone()), + org_id, + caller.clone(), + vec![0; T::MaxNameLength::get() as usize], + T::MinContribution::get(), + T::MinContribution::get(), + frame_system::Pallet::::block_number() + 200_u32.into(), + Default::default(), + Default::default(), + vec![0; T::MaxNameLength::get() as usize], + vec![0; 5], + vec![0; 32] + ) + verify { + assert!(CampaignsCount::::get() == count_before + 1); + } + + update_state { + let b in 1 .. T::MaxCampaignsPerOrg::get(); // limit to campaigns per organization + + let caller: T::AccountId = whitelisted_caller(); + fund_account::(&caller)?; + + // Prepare organization and treasury + let per_block_cnt = T::MaxCampaignsPerBlock::get(); + let org_id = T::Control::create_org(caller.clone().into())?; + let treasury_id = T::Control::org_treasury_account(&org_id); + fund_account::(&treasury_id)?; + + // Create some campaigns, respecting per block limitation + for i in 0 .. b { + create_campaign_call::(caller.clone(), org_id)?; + if i % per_block_cnt == 0 { + next_block::(); + } + } + + // Use last campaign to call extrinsic with + let campaign_id = T::Hashing::hash_of(&Nonce::::get().saturating_sub(1_u128)); + let new_state = FlowState::Paused; + }: _( + RawOrigin::Signed(caller.clone()), + campaign_id, + new_state.clone() + ) + verify { + assert!(CampaignState::::get(&campaign_id) == new_state); + } + + contribute { + + // Prepare owner and contributor accounts + let owner: T::AccountId = whitelisted_caller(); + let contributor: T::AccountId = account("contributor", 0, SEED); + fund_accounts::(&vec![owner.clone(), contributor.clone()])?; + + // Prepare organization and treasury + let org_id = T::Control::create_org(owner.clone().into())?; + let treasury_id = T::Control::org_treasury_account(&org_id); + fund_account::(&treasury_id)?; + + // Create campaign to use for contributions + let campaign_id = create_campaign_call::(owner, org_id)?; + }: _( + RawOrigin::Signed(contributor.clone()), + campaign_id.clone(), + T::MinContribution::get() + ) + verify { + assert!(CampaignContribution::::contains_key((&campaign_id, &contributor))); + } + + on_initialize { + let c in 1 .. T::MaxContributorsProcessing::get(); // number of contributions in current block + + let owner: T::AccountId = whitelisted_caller(); + fund_account::(&owner)?; + + // Prepare organization and treasury + let org_id = T::Control::create_org(owner.clone().into())?; + let treasury_id = T::Control::org_treasury_account(&org_id); + fund_account::(&treasury_id)?; + + // Create campaign and add some contributions + let campaign_id = create_campaign_call::(owner.clone(), org_id.clone())?; + 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())?; + } + + // 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()); + block_number = block_number.saturating_add(1_u32.into()); + frame_system::Pallet::::set_block_number(block_number); + }: { + Pallet::::on_initialize(block_number); + } } diff --git a/flow/src/lib.rs b/flow/src/lib.rs index 7c939d107..dc0f5ecf6 100644 --- a/flow/src/lib.rs +++ b/flow/src/lib.rs @@ -42,20 +42,17 @@ pub use types::*; mod mock; mod tests; - -// #[cfg(feature = "runtime-benchmarks")] -// mod benchmarking; - -// TODO: weights -// mod default_weights; +mod migration; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +pub mod weights; // TODO: externalise error messages - Enum: number and text description // mod errors; use frame_support::{ - codec::Encode, - dispatch::DispatchResult, - traits::{Get, UnixTime, BalanceStatus}, + dispatch::{DispatchResult, DispatchResultWithPostInfo}, + traits::{Get, BalanceStatus, StorageVersion, UnixTime}, transactional, weights::Weight }; @@ -67,10 +64,11 @@ use sp_std::vec::Vec; use sp_std::convert::TryFrom; use codec::HasCompact; -use gamedao_traits::{ControlTrait, FlowTrait}; +use gamedao_traits::{ControlTrait, ControlBenchmarkingTrait, FlowTrait}; use orml_traits::{MultiCurrency, MultiReservableCurrency}; pub use pallet::*; +pub use weights::WeightInfo; // TODO: use associated type instead pub type Moment = u64; @@ -81,8 +79,12 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; + /// The current storage version. + const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); #[pallet::config] @@ -112,7 +114,7 @@ pub mod pallet { + TypeInfo; /// Weight information for extrinsics in this module. - type WeightInfo: frame_system::weights::WeightInfo; + type WeightInfo: WeightInfo; /// Multi-currency support for asset management. type Currency: MultiCurrency @@ -120,7 +122,8 @@ pub mod pallet { type UnixTime: UnixTime; - type Control: ControlTrait; + type Control: ControlTrait + + ControlBenchmarkingTrait; /// The GameDAO Treasury AccountId. #[pallet::constant] @@ -136,6 +139,9 @@ pub mod pallet { #[pallet::constant] type MaxCampaignsPerAddress: Get; + + #[pallet::constant] + type MaxCampaignsPerOrg: Get; /// The max number of campaigns per one block. #[pallet::constant] @@ -214,12 +220,12 @@ pub mod pallet { pub(super) type CampaignState = StorageMap<_, Blake2_128Concat, T::Hash, FlowState, ValueQuery, GetDefault>; - /// List of campaign by certain campaign state. + /// List of campaign by certain campaign state and org. /// 0 init, 1 active, 2 paused, 3 complete success, 4 complete failed, 5 authority lock /// - /// CampaignsByState: map FlowState => Vec + /// CampaignsByState: double_map FlowState, Hash => Vec #[pallet::storage] - pub(super) type CampaignsByState = StorageMap<_, Blake2_128Concat, FlowState, Vec, ValueQuery>; + pub(super) type CampaignsByState = StorageDoubleMap<_, Blake2_128Concat, FlowState, Blake2_128Concat, T::Hash, Vec, ValueQuery>; /// Campaigns ending in block x. /// @@ -446,6 +452,8 @@ pub mod pallet { EndTooLate, /// Max campaigns per block exceeded. CampaignsPerBlockExceeded, + /// Max campaigns per org exceeded. + CampaignsPerOrgExceeded, /// Name too long. NameTooLong, /// Name too short. @@ -486,17 +494,20 @@ pub mod pallet { #[pallet::hooks] impl Hooks for Pallet { - fn on_initialize(block_number: T::BlockNumber) -> Weight { - let mut processed: u32 = 0; - Self::process_campaigns(&block_number, FlowState::Finalizing, &mut processed) - .saturating_add( - Self::process_campaigns(&block_number, FlowState::Reverting, &mut processed) - ) - } + fn on_initialize(block_number: T::BlockNumber) -> Weight { + let mut contributors: u32 = 0; + Self::process_campaigns(&block_number, FlowState::Finalizing, &mut contributors); + Self::process_campaigns(&block_number, FlowState::Reverting, &mut contributors); + T::WeightInfo::on_initialize(contributors) + } fn on_finalize(block_number: T::BlockNumber) { Self::schedule_campaign_settlements(block_number) } + + fn on_runtime_upgrade() -> Weight { + migration::migrate::() + } } #[pallet::call] @@ -518,8 +529,10 @@ pub mod pallet { /// /// Emits `CampaignCreated` event when successful. /// - /// Weight: `O(1)` - #[pallet::weight(5_000_000)] + /// Weight: `O(log n)` + #[pallet::weight(T::WeightInfo::create_campaign( + T::MaxCampaignsPerOrg::get() + ))] #[transactional] pub fn create_campaign( origin: OriginFor, @@ -534,9 +547,9 @@ pub mod pallet { cid: Vec, token_symbol: Vec, // up to 5 token_name: Vec, /* cleartext - * token_curve_a: u8, // preset - * token_curve_b: Vec, // custom */ - ) -> DispatchResult { + * token_curve_a: u8, // preset + * token_curve_b: Vec, // custom */ + ) -> DispatchResultWithPostInfo { let creator = ensure_signed(origin)?; let owner = T::Control::org_controller_account(&org_id); ensure!(creator == owner, Error::::AuthorizationError); @@ -572,12 +585,18 @@ pub mod pallet { // for collision // check contribution limit per block - let camapaigns = CampaignsByBlock::::get(expiry); + let block_campaigns_cnt = CampaignsByBlock::::get(expiry).len() as u32; ensure!( - (camapaigns.len() as u32) < T::MaxCampaignsPerBlock::get(), + block_campaigns_cnt < T::MaxCampaignsPerBlock::get(), Error::::CampaignsPerBlockExceeded ); + let org_campaigns_cnt = CampaignsByOrg::::get(&org_id).len() as u32; + ensure!( + org_campaigns_cnt < T::MaxCampaignsPerOrg::get(), + Error::::CampaignsPerOrgExceeded + ); + let campaign = Campaign { id: id.clone(), org: org_id.clone(), @@ -600,7 +619,7 @@ pub mod pallet { // 0 init, 1 active, 2 paused, 3 complete success, 4 complete failed, 5 // authority lock - Self::set_state(id.clone(), FlowState::Active); + Self::set_state(id.clone(), FlowState::Active, &org_id); // deposit the event Self::deposit_event(Event::CampaignCreated { @@ -612,7 +631,7 @@ pub mod pallet { expiry, name, }); - Ok(()) + Ok(Some(T::WeightInfo::create_campaign(org_campaigns_cnt)).into()) // No fees are paid here if we need to create this account; // that's why we don't just use the stock `transfer`. @@ -627,9 +646,11 @@ pub mod pallet { /// /// Emits `CampaignUpdated` event when successful. /// - /// Weight: - #[pallet::weight(1_000_000)] - pub fn update_state(origin: OriginFor, campaign_id: T::Hash, state: FlowState) -> DispatchResult { + /// Weight: O(log n) + #[pallet::weight(T::WeightInfo::update_state( + T::MaxCampaignsPerOrg::get() + ))] + pub fn update_state(origin: OriginFor, campaign_id: T::Hash, state: FlowState) -> DispatchResultWithPostInfo { // access control let sender = ensure_signed(origin)?; @@ -643,10 +664,12 @@ pub mod pallet { ensure!(current_block < campaign.expiry, Error::::CampaignExpired); // not finished or locked? - let current_state = CampaignState::::get(campaign_id); + let org_id = CampaignOrg::::get(&campaign_id); + let org_campaigns_cnt = CampaignsByOrg::::get(&org_id).len() as u32; + let current_state = CampaignState::::get(&campaign_id); ensure!(current_state < FlowState::Success, Error::::CampaignExpired); - Self::set_state(campaign_id.clone(), state.clone()); + Self::set_state(campaign_id.clone(), state.clone(), &org_id); // dispatch status update event Self::deposit_event(Event::CampaignUpdated { @@ -655,7 +678,7 @@ pub mod pallet { block_number: current_block, }); - Ok(()) + Ok(Some(T::WeightInfo::update_state(org_campaigns_cnt)).into()) } /// Contribute to project @@ -665,8 +688,8 @@ pub mod pallet { /// /// Emits `CampaignContributed` event when successful. /// - /// Weight: - #[pallet::weight(5_000_000)] + /// Weight: O(1) + #[pallet::weight(T::WeightInfo::contribute())] pub fn contribute(origin: OriginFor, campaign_id: T::Hash, contribution: T::Balance) -> DispatchResult { // check @@ -706,22 +729,22 @@ pub mod pallet { } impl Pallet { - fn set_state(campaign_id: T::Hash, state: FlowState) { + fn set_state(campaign_id: T::Hash, state: FlowState, org_id: &T::Hash) { let current_state = CampaignState::::get(&campaign_id); // remove - let mut current_state_members = CampaignsByState::::get(¤t_state); + let mut current_state_members = CampaignsByState::::get(¤t_state, org_id); match current_state_members.binary_search(&campaign_id) { Ok(index) => { current_state_members.remove(index); - CampaignsByState::::insert(¤t_state, current_state_members); + CampaignsByState::::insert(¤t_state, org_id, current_state_members); } Err(_) => (), //(Error::::IdUnknown) } // add - CampaignsByState::::mutate(&state, |campaigns| campaigns.push(campaign_id.clone())); + CampaignsByState::::mutate(&state, org_id, |campaigns| campaigns.push(campaign_id.clone())); CampaignState::::insert(campaign_id, state); } @@ -836,7 +859,7 @@ impl Pallet { // Campaign cap reached: Finalizing if campaign_balance >= campaign.cap { - Self::set_state(campaign.id, FlowState::Finalizing); + Self::set_state(campaign.id, FlowState::Finalizing, &campaign.org); Self::deposit_event(Event::CampaignFinalising { campaign_id: *campaign_id, @@ -846,7 +869,7 @@ impl Pallet { // Campaign cap not reached: Reverting } else { - Self::set_state(campaign.id, FlowState::Reverting); + Self::set_state(campaign.id, FlowState::Reverting, &campaign.org); Self::deposit_event(Event::CampaignReverting { campaign_id: *campaign_id, @@ -857,40 +880,37 @@ impl Pallet { } } - fn process_campaigns(block_number: &T::BlockNumber, state: FlowState, processed: &mut u32) -> Weight { - let campaign_ids = CampaignsByState::::get(&state); - let total_weight: Weight = 0; - for campaign_id in campaign_ids { - let campaign = Campaigns::::get(campaign_id); - let campaign_balance = CampaignBalance::::get(campaign_id); - let org = CampaignOrg::::get(&campaign_id); - let org_treasury = T::Control::org_treasury_account(&org); - let contributors = CampaignContributors::::get(campaign_id); - - if state == FlowState::Finalizing { - if let Some(owner) = CampaignOwner::::get(campaign.id) { - total_weight.saturating_add( - Self::finalize_campaign(&block_number, processed, &campaign, &campaign_balance, &org, &org_treasury, &contributors, &owner) - ); - } else { - // TODO: If no campaign owner: revert the campaign or leave it as is? + fn process_campaigns(block_number: &T::BlockNumber, state: FlowState, processed: &mut u32) -> u32 { + let mut campaigns_processed: u32 = 0; + let campaign_ids_by_org: Vec<(T::Hash, Vec)> = CampaignsByState::::iter_prefix(&state).collect(); + for (_org_id, campaign_ids) in campaign_ids_by_org { + for campaign_id in campaign_ids { + let campaign = Campaigns::::get(campaign_id); + let campaign_balance = CampaignBalance::::get(&campaign_id); + let org_treasury = T::Control::org_treasury_account(&campaign.org); + let contributors = CampaignContributors::::get(&campaign_id); + + if state == FlowState::Finalizing { + if let Some(owner) = CampaignOwner::::get(campaign.id) { + Self::finalize_campaign(&block_number, processed, &campaign, &campaign_balance, &org_treasury, &contributors, &owner); + } else { + // TODO: If no campaign owner: revert the campaign or leave it as is? + } + } else if state == FlowState::Reverting { + Self::revert_campaign(&block_number, processed, &campaign, &campaign_balance, &org_treasury, &contributors); } - } else if state == FlowState::Reverting { - total_weight.saturating_add( - Self::revert_campaign(&block_number, processed, &campaign, &campaign_balance, &org, &org_treasury, &contributors) - ); + campaigns_processed = campaigns_processed.saturating_add(1); } } - total_weight + campaigns_processed } fn finalize_campaign( block_number: &T::BlockNumber, processed: &mut u32, campaign: &Campaign, - campaign_balance: &T::Balance, org: &T::Hash, org_treasury: &T::AccountId, + campaign_balance: &T::Balance, org_treasury: &T::AccountId, contributors: &Vec, owner: &T::AccountId - ) -> Weight { - let contributors = CampaignContributors::::get(campaign.id); + ) { let processed_offset = ContributorsFinalized::::get(campaign.id); let offset: usize = usize::try_from(processed_offset).unwrap(); for contributor in &contributors[offset..] { @@ -918,16 +938,14 @@ impl Pallet { *processed += 1; if *processed >= T::MaxContributorsProcessing::get() { ContributorsFinalized::::insert(campaign.id, processed_offset + *processed); - // TODO: return T::WeightInfo::finalize_campaign(processed) - return 1 as Weight + return } } ContributorsFinalized::::insert(campaign.id, processed_offset + *processed); // TODO: This doesn't make sense without "transfer_amount" error handling if *campaign_balance < campaign.cap { - Self::set_state(campaign.id, FlowState::Reverting); - // TODO: return T::WeightInfo::finalize_campaign(processed) - return 1 as Weight + Self::set_state(campaign.id, FlowState::Reverting, &campaign.org); + return } let commission = T::CampaignFee::get().mul_floor(campaign_balance.clone()); let _transfer_commission = T::Currency::repatriate_reserved( @@ -942,7 +960,7 @@ impl Pallet { let updated_balance = *campaign_balance - commission; CampaignBalance::::insert(campaign.id, updated_balance); - Self::set_state(campaign.id, FlowState::Success); + Self::set_state(campaign.id, FlowState::Success, &campaign.org); Self::deposit_event(Event::CampaignFinalized { campaign_id: campaign.id, @@ -950,17 +968,14 @@ impl Pallet { block_number: *block_number, success: true, }); - - // TODO: return T::WeightInfo::finalize_campaign(processed) - 1 as Weight } fn revert_campaign( block_number: &T::BlockNumber, processed: &mut u32, campaign: &Campaign, - campaign_balance: &T::Balance, org: &T::Hash, org_treasury: &T::AccountId, + campaign_balance: &T::Balance, org_treasury: &T::AccountId, contributors: &Vec - ) -> Weight { + ) { let processed_offset = ContributorsReverted::::get(campaign.id); let offset: usize = usize::try_from(processed_offset).unwrap(); for account in &contributors[offset..] { @@ -970,15 +985,14 @@ impl Pallet { *processed += 1; if *processed >= T::MaxContributorsProcessing::get() { ContributorsReverted::::insert(campaign.id, processed_offset + *processed); - // TODO: return T::WeightInfo::revert_campaign(processed) - return 1 as Weight + return } } ContributorsReverted::::insert(campaign.id, processed_offset + *processed); // Unreserve Initial deposit T::Currency::unreserve(T::ProtocolTokenId::get(), &org_treasury, campaign.deposit); - Self::set_state(campaign.id, FlowState::Failed); + Self::set_state(campaign.id, FlowState::Failed, &campaign.org); Self::deposit_event(Event::CampaignFailed { campaign_id: campaign.id, campaign_balance: *campaign_balance, @@ -986,8 +1000,6 @@ impl Pallet { success: false, }); - // TODO: return T::WeightInfo::revert_campaign(processed) - 1 as Weight } } diff --git a/flow/src/migration.rs b/flow/src/migration.rs new file mode 100644 index 000000000..b97bae44c --- /dev/null +++ b/flow/src/migration.rs @@ -0,0 +1,53 @@ +use frame_support::{ + generate_storage_alias, + traits::Get, + Blake2_128Concat, +}; +use sp_std::prelude::*; +use crate::{ + CampaignsByState as CampaignsByStateNew, + CampaignOrg, + Config, + FlowState, + Pallet, + Weight +}; + + +pub fn migrate() -> Weight { + use frame_support::traits::StorageVersion; + + let version = StorageVersion::get::>(); + let mut weight: Weight = 0; + + if version < 1 { + weight = weight.saturating_add(v1::migrate::()); + StorageVersion::new(1).put::>(); + } + + weight +} + +mod v1 { + use super::*; + + generate_storage_alias!( + Flow, + CampaignsByState => Map<(Blake2_128Concat, FlowState), Vec> + ); + + pub fn migrate() -> Weight { + let mut weight: Weight = 0; + + let old_records: Vec<(FlowState, Vec)> = CampaignsByState::::drain().collect(); + for (state, campaign_ids) in old_records { + for campaign_id in campaign_ids { + let org_id = CampaignOrg::::get(&campaign_id); + CampaignsByStateNew::::mutate(&state, &org_id, |campaigns| campaigns.push(campaign_id)); + weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1)); + } + } + + weight + } +} diff --git a/flow/src/mock.rs b/flow/src/mock.rs index 197b364ed..6d8179835 100644 --- a/flow/src/mock.rs +++ b/flow/src/mock.rs @@ -5,7 +5,6 @@ use frame_support::{ construct_runtime, parameter_types, PalletId, traits::{Everything, GenesisBuild, Nothing}, }; -use frame_system::EnsureRoot; use sp_core::H256; use sp_runtime::{traits::IdentityLookup, Permill}; @@ -195,6 +194,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 = 4; pub const MinCampaignDuration: BlockNumber = 1 * DAYS; @@ -223,6 +223,7 @@ impl Config for Test { type MaxNameLength = MaxNameLength; type MaxCampaignsPerAddress = MaxCampaignsPerAddress; type MaxCampaignsPerBlock = MaxCampaignsPerBlock; + type MaxCampaignsPerOrg = MaxCampaignsPerOrg; type MaxContributionsPerBlock = MaxContributionsPerBlock; type MinCampaignDuration = MinCampaignDuration; type MaxCampaignDuration = MaxCampaignDuration; diff --git a/flow/src/tests.rs b/flow/src/tests.rs index 947fdc720..d6adc0873 100644 --- a/flow/src/tests.rs +++ b/flow/src/tests.rs @@ -1,11 +1,10 @@ #![cfg(test)] use super::*; -use codec::Encode; +use mock::{Event, *}; use frame_support::traits::Hooks; use frame_support::{assert_noop, assert_ok}; -use frame_system::{EventRecord, Phase, RawOrigin}; -use mock::{Event, Moment, *}; +use frame_system::RawOrigin; use sp_core::H256; use sp_runtime::traits::{Hash, AccountIdConversion}; @@ -125,7 +124,7 @@ fn flow_create_errors() { #[test] fn flow_create_success() { new_test_ext().execute_with(|| { - let (org, treasury, _) = create_org_treasury(); + let (org, _treasury, _) = create_org_treasury(); let current_block = 3; System::set_block_number(current_block); @@ -154,7 +153,7 @@ fn flow_create_success() { assert_eq!(CampaignsOwnedCount::::get(org), 1); assert_eq!(CampaignsOwnedIndex::::get((org, id)), 0); assert_eq!(Nonce::::get(), 1); - assert_eq!(CampaignsByState::::get(FlowState::Active), vec![id]); + assert_eq!(CampaignsByState::::get(FlowState::Active, &org), vec![id]); assert_eq!(CampaignState::::get(id), FlowState::Active); System::assert_has_event(Event::Flow(crate::Event::CampaignCreated { @@ -230,9 +229,11 @@ fn flow_update_state_success() { Campaigns::::insert(&campaign_id, &campaign); CampaignOwner::::insert(campaign_id, BOB); CampaignAdmin::::insert(campaign_id, BOB); + CampaignOrg::::insert(campaign_id, campaign.org); + CampaignsByOrg::::insert(campaign.org, vec![campaign_id]); assert_ok!(Flow::update_state(Origin::signed(BOB), campaign_id, FlowState::Paused)); - assert_eq!(CampaignsByState::::get(FlowState::Paused), vec![campaign_id]); + assert_eq!(CampaignsByState::::get(FlowState::Paused, &campaign.org), vec![campaign_id]); assert_eq!(CampaignState::::get(campaign_id), FlowState::Paused); }); } @@ -340,7 +341,7 @@ fn flow_on_finalize_campaign_succeess() { let nonce = Nonce::::get(); let campaign_id: H256 = ::Hashing::hash_of(&nonce); assert_ok!(Flow::create_campaign( - Origin::signed(BOB), org, BOB, vec![1, 2], target, deposit, expiry, + Origin::signed(BOB), org.clone(), BOB, vec![1, 2], target, deposit, expiry, FlowProtocol::Raise, FlowGovernance::No, vec![1, 2], vec![], vec![] )); @@ -369,7 +370,7 @@ fn flow_on_finalize_campaign_succeess() { })); // Ensure that campaign was scheduled to be finalized - assert_eq!(CampaignsByState::::get(&FlowState::Finalizing), vec![campaign_id]); + assert_eq!(CampaignsByState::::get(&FlowState::Finalizing, &org), vec![campaign_id]); // Ensure that campaign will be finalize in 3 blocks: 4 + 4 + 2 let batch_size: u128 = 4; assert_eq!(MaxContributorsProcessing::get(), batch_size as u32); @@ -427,7 +428,7 @@ fn flow_on_finalize_campaign_succeess() { tbalance - deposit ); // Ensure that campaign succeeded - assert_eq!(CampaignsByState::::get(&FlowState::Success), vec![campaign_id]); + assert_eq!(CampaignsByState::::get(&FlowState::Success, &org), vec![campaign_id]); System::assert_has_event(Event::Flow(crate::Event::CampaignFinalized { campaign_id, campaign_balance: CampaignBalance::::get(campaign_id), @@ -455,7 +456,7 @@ fn flow_on_finalize_campaign_failed() { let nonce = Nonce::::get(); let campaign_id: H256 = ::Hashing::hash_of(&nonce); assert_ok!(Flow::create_campaign( - Origin::signed(BOB), org, BOB, vec![1, 2], target, deposit, expiry, + Origin::signed(BOB), org.clone(), BOB, vec![1, 2], target, deposit, expiry, FlowProtocol::Raise, FlowGovernance::No, vec![1, 2], vec![], vec![] )); @@ -484,7 +485,7 @@ fn flow_on_finalize_campaign_failed() { })); // Ensure that campaign was scheduled to be reverted - assert_eq!(CampaignsByState::::get(&FlowState::Reverting), vec![campaign_id]); + assert_eq!(CampaignsByState::::get(&FlowState::Reverting, &org), vec![campaign_id]); // Ensure that campaign will be reverted in 3 blocks: 4 + 4 + 2 let batch_size: u128 = 4; assert_eq!(MaxContributorsProcessing::get(), batch_size as u32); @@ -543,7 +544,7 @@ fn flow_on_finalize_campaign_failed() { tbalance ); // Ensure that campaign failed - assert_eq!(CampaignsByState::::get(&FlowState::Failed), vec![campaign_id]); + assert_eq!(CampaignsByState::::get(&FlowState::Failed, &org), vec![campaign_id]); System::assert_has_event(Event::Flow(crate::Event::CampaignFailed { campaign_id, diff --git a/flow/src/weights.rs b/flow/src/weights.rs new file mode 100644 index 000000000..9dd841443 --- /dev/null +++ b/flow/src/weights.rs @@ -0,0 +1,210 @@ +// 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_flow +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-06-21, 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_flow +// --extrinsic=* +// --steps=20 +// --repeat=10 +// --output=gamedao-protocol/flow/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_flow. +pub trait WeightInfo { + fn create_campaign(b: u32, ) -> Weight; + fn update_state(b: u32, ) -> Weight; + fn contribute() -> Weight; + fn on_initialize(c: u32, ) -> Weight; +} + +/// Weights for gamedao_flow using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + // Storage: Control OrgController (r:1 w:0) + // Storage: Control OrgTreasury (r:1 w:0) + // Storage: Tokens Accounts (r:1 w:1) + // Storage: Flow Nonce (r:1 w:1) + // Storage: Flow CampaignsByBlock (r:1 w:1) + // Storage: Flow CampaignsByOrg (r:1 w:1) + // Storage: Timestamp Now (r:1 w:0) + // Storage: Flow CampaignsCount (r:1 w:1) + // Storage: Flow CampaignsOwnedCount (r:1 w:1) + // Storage: Flow CampaignState (r:1 w:1) + // Storage: Flow CampaignsByState (r:2 w:1) + // Storage: Flow CampaignAdmin (r:0 w:1) + // Storage: Flow CampaignsOwnedIndex (r:0 w:1) + // Storage: Flow CampaignOwner (r:0 w:1) + // Storage: Flow CampaignsOwnedArray (r:0 w:1) + // Storage: Flow CampaignsArray (r:0 w:1) + // Storage: Flow Campaigns (r:0 w:1) + // Storage: Flow CampaignOrg (r:0 w:1) + // Storage: Flow CampaignsIndex (r:0 w:1) + fn create_campaign(b: u32, ) -> Weight { + (94_410_000 as Weight) + // Standard Error: 19_000 + .saturating_add((791_000 as Weight).saturating_mul(b as Weight)) + .saturating_add(T::DbWeight::get().reads(12 as Weight)) + .saturating_add(T::DbWeight::get().writes(16 as Weight)) + } + // Storage: Flow CampaignOwner (r:1 w:0) + // Storage: Flow CampaignAdmin (r:1 w:0) + // Storage: Flow Campaigns (r:1 w:0) + // Storage: Flow CampaignOrg (r:1 w:0) + // Storage: Flow CampaignsByOrg (r:1 w:0) + // Storage: Flow CampaignState (r:1 w:1) + // Storage: Flow CampaignsByState (r:2 w:2) + fn update_state(b: u32, ) -> Weight { + (56_185_000 as Weight) + // Standard Error: 22_000 + .saturating_add((490_000 as Weight).saturating_mul(b as Weight)) + .saturating_add(T::DbWeight::get().reads(8 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + // Storage: Tokens Accounts (r:1 w:1) + // Storage: Flow CampaignOwner (r:1 w:0) + // Storage: Flow Campaigns (r:1 w:0) + // Storage: Flow CampaignState (r:1 w:0) + // Storage: Flow CampaignContribution (r:1 w:1) + // Storage: Flow CampaignsContributedCount (r:1 w:1) + // Storage: Flow CampaignContributorsCount (r:1 w:1) + // Storage: Flow CampaignContributors (r:1 w:1) + // Storage: Flow CampaignsContributed (r:1 w:1) + // Storage: Flow CampaignBalance (r:1 w:1) + // Storage: Flow CampaignsContributedIndex (r:0 w:1) + // Storage: Flow CampaignsContributedArray (r:0 w:1) + fn contribute() -> Weight { + (53_779_000 as Weight) + .saturating_add(T::DbWeight::get().reads(10 as Weight)) + .saturating_add(T::DbWeight::get().writes(9 as Weight)) + } + // Storage: Flow CampaignsByState (r:4 w:2) + // Storage: Flow Campaigns (r:1 w:0) + // Storage: Flow CampaignBalance (r:1 w:1) + // Storage: Control OrgTreasury (r:1 w:0) + // Storage: Flow CampaignContributors (r:1 w:0) + // Storage: Flow CampaignOwner (r:1 w:0) + // Storage: Flow ContributorsFinalized (r:1 w:1) + // Storage: Flow CampaignContribution (r:1 w:0) + // Storage: Tokens Accounts (r:3 w:3) + // Storage: System Account (r:1 w:1) + // Storage: Flow CampaignState (r:1 w:1) + fn on_initialize(c: u32, ) -> Weight { + (91_286_000 as Weight) + // Standard Error: 214_000 + .saturating_add((10_321_000 as Weight).saturating_mul(c as Weight)) + .saturating_add(T::DbWeight::get().reads(14 as Weight)) + .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(c as Weight))) + .saturating_add(T::DbWeight::get().writes(9 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(c as Weight))) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + // Storage: Control OrgController (r:1 w:0) + // Storage: Control OrgTreasury (r:1 w:0) + // Storage: Tokens Accounts (r:1 w:1) + // Storage: Flow Nonce (r:1 w:1) + // Storage: Flow CampaignsByBlock (r:1 w:1) + // Storage: Flow CampaignsByOrg (r:1 w:1) + // Storage: Timestamp Now (r:1 w:0) + // Storage: Flow CampaignsCount (r:1 w:1) + // Storage: Flow CampaignsOwnedCount (r:1 w:1) + // Storage: Flow CampaignState (r:1 w:1) + // Storage: Flow CampaignsByState (r:2 w:1) + // Storage: Flow CampaignAdmin (r:0 w:1) + // Storage: Flow CampaignsOwnedIndex (r:0 w:1) + // Storage: Flow CampaignOwner (r:0 w:1) + // Storage: Flow CampaignsOwnedArray (r:0 w:1) + // Storage: Flow CampaignsArray (r:0 w:1) + // Storage: Flow Campaigns (r:0 w:1) + // Storage: Flow CampaignOrg (r:0 w:1) + // Storage: Flow CampaignsIndex (r:0 w:1) + fn create_campaign(b: u32, ) -> Weight { + (94_410_000 as Weight) + // Standard Error: 19_000 + .saturating_add((791_000 as Weight).saturating_mul(b as Weight)) + .saturating_add(RocksDbWeight::get().reads(12 as Weight)) + .saturating_add(RocksDbWeight::get().writes(16 as Weight)) + } + // Storage: Flow CampaignOwner (r:1 w:0) + // Storage: Flow CampaignAdmin (r:1 w:0) + // Storage: Flow Campaigns (r:1 w:0) + // Storage: Flow CampaignOrg (r:1 w:0) + // Storage: Flow CampaignsByOrg (r:1 w:0) + // Storage: Flow CampaignState (r:1 w:1) + // Storage: Flow CampaignsByState (r:2 w:2) + fn update_state(b: u32, ) -> Weight { + (56_185_000 as Weight) + // Standard Error: 22_000 + .saturating_add((490_000 as Weight).saturating_mul(b as Weight)) + .saturating_add(RocksDbWeight::get().reads(8 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + } + // Storage: Tokens Accounts (r:1 w:1) + // Storage: Flow CampaignOwner (r:1 w:0) + // Storage: Flow Campaigns (r:1 w:0) + // Storage: Flow CampaignState (r:1 w:0) + // Storage: Flow CampaignContribution (r:1 w:1) + // Storage: Flow CampaignsContributedCount (r:1 w:1) + // Storage: Flow CampaignContributorsCount (r:1 w:1) + // Storage: Flow CampaignContributors (r:1 w:1) + // Storage: Flow CampaignsContributed (r:1 w:1) + // Storage: Flow CampaignBalance (r:1 w:1) + // Storage: Flow CampaignsContributedIndex (r:0 w:1) + // Storage: Flow CampaignsContributedArray (r:0 w:1) + fn contribute() -> Weight { + (53_779_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(10 as Weight)) + .saturating_add(RocksDbWeight::get().writes(9 as Weight)) + } + // Storage: Flow CampaignsByState (r:4 w:2) + // Storage: Flow Campaigns (r:1 w:0) + // Storage: Flow CampaignBalance (r:1 w:1) + // Storage: Control OrgTreasury (r:1 w:0) + // Storage: Flow CampaignContributors (r:1 w:0) + // Storage: Flow CampaignOwner (r:1 w:0) + // Storage: Flow ContributorsFinalized (r:1 w:1) + // Storage: Flow CampaignContribution (r:1 w:0) + // Storage: Tokens Accounts (r:3 w:3) + // Storage: System Account (r:1 w:1) + // Storage: Flow CampaignState (r:1 w:1) + fn on_initialize(c: u32, ) -> Weight { + (91_286_000 as Weight) + // Standard Error: 214_000 + .saturating_add((10_321_000 as Weight).saturating_mul(c as Weight)) + .saturating_add(RocksDbWeight::get().reads(14 as Weight)) + .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(c as Weight))) + .saturating_add(RocksDbWeight::get().writes(9 as Weight)) + .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(c as Weight))) + } +}