Add child-bounties pallet. (#10309)

* Extract child-bounties as a separate pallet.

* Initial tests added.

* More tests.

* Tests complete. Fixed curator fee issue.

* Fixed comments.

* Added benchmarks for child-bounties pallet.

* Added weights.

* Fixed formatting.

* Fixed comments.

* Re-run benchmarks for bounties pallet.

* Make cargo fmt happy again

* add max encoded len

* use event structs

* fmt

* fix compile

* Addressed review comments.

* Use config type instead of const in benchmarking.

* Addressed more review comments.

* Use ensure_can_withdraw instead of just checking min balance.

* fmt.

* Introduce ChildBountyCuratorDepositBase to avoid zero curator deposits for child bounties.

* Fix unassign curator logic for child-bounties.

* Added more tests for unassign curator.

* Reduce bounty description max length in node runtime.

* Updated weights for child bounties pallet.

* reduce indentation of unassign_curator

* more indentation reduction

* deduplicate slashing arms

* reintroduce ensure check

* add assertion to check that bad unassign origin fails

* formatting

* Updated comments.

Co-authored-by: Ricardo Rius <ricardo@parity.io>
Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
Co-authored-by: Alexander Popiak <alexander.popiak@parity.io>
This commit is contained in:
Gautam Dhameja
2021-12-07 23:40:47 +01:00
committed by GitHub
parent fe6189d370
commit 752e050cf4
13 changed files with 2896 additions and 78 deletions
+54
View File
@@ -0,0 +1,54 @@
[package]
name = "pallet-child-bounties"
version = "4.0.0-dev"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2021"
license = "Apache-2.0"
homepage = "https://substrate.io"
repository = "https://github.com/paritytech/substrate/"
description = "FRAME pallet to manage child bounties"
readme = "README.md"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = [
"derive",
] }
scale-info = { version = "1.0", default-features = false, features = ["derive"] }
sp-std = { version = "4.0.0-dev", default-features = false, path = "../../primitives/std" }
sp-runtime = { version = "4.0.0-dev", default-features = false, path = "../../primitives/runtime" }
frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" }
frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" }
pallet-treasury = { version = "4.0.0-dev", default-features = false, path = "../treasury" }
pallet-bounties = { version = "4.0.0-dev", default-features = false, path = "../bounties" }
sp-io = { version = "4.0.0-dev", path = "../../primitives/io", default-features = false }
sp-core = { version = "4.0.0-dev", path = "../../primitives/core", default-features = false }
frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../benchmarking", optional = true }
log = { version = "0.4.14", default-features = false }
[dev-dependencies]
pallet-balances = { version = "4.0.0-dev", path = "../balances" }
[features]
default = ["std"]
std = [
"codec/std",
"sp-core/std",
"sp-io/std",
"scale-info/std",
"sp-std/std",
"sp-runtime/std",
"frame-support/std",
"frame-system/std",
"pallet-treasury/std",
"pallet-bounties/std",
"log/std",
]
runtime-benchmarks = [
"frame-benchmarking",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
]
try-runtime = ["frame-support/try-runtime"]
+21
View File
@@ -0,0 +1,21 @@
# Child Bounties Pallet (pallet-child-bounties)
## Child Bounty
> NOTE: This pallet is tightly coupled with pallet-treasury and pallet-bounties.
With child bounties, a large bounty proposal can be divided into smaller chunks, for parallel execution, and for efficient governance and tracking of spent funds.
A child-bounty is a smaller piece of work, extracted from a parent bounty. A curator is assigned after the child-bounty is created by the parent bounty curator, to be delegated with the responsibility of assigning a payout address once the specified set of tasks is completed.
## Interface
### Dispatchable Functions
- `add_child_bounty` - Add a child-bounty for a parent-bounty to for dividing the work in smaller tasks.
- `propose_curator` - Assign an account to a child-bounty as candidate curator.
- `accept_curator` - Accept a child-bounty assignment from the parent-bounty curator, setting a curator deposit.
- `award_child_bounty` - Close and pay out the specified amount for the completed work.
- `claim_child_bounty` - Claim a specific child-bounty amount from the payout address.
- `unassign_curator` - Unassign an accepted curator from a specific child-bounty.
- `close_child_bounty` - Cancel the child-bounty for a specific treasury amount and close the bounty.
@@ -0,0 +1,308 @@
// 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.
//! Child-bounties pallet benchmarking.
#![cfg(feature = "runtime-benchmarks")]
use super::*;
use frame_benchmarking::{account, benchmarks, whitelisted_caller};
use frame_system::RawOrigin;
use crate::Pallet as ChildBounties;
use pallet_bounties::Pallet as Bounties;
use pallet_treasury::Pallet as Treasury;
const SEED: u32 = 0;
#[derive(Clone)]
struct BenchmarkChildBounty<T: Config> {
/// Bounty ID.
bounty_id: BountyIndex,
/// ChildBounty ID.
child_bounty_id: BountyIndex,
/// The account proposing it.
caller: T::AccountId,
/// The master curator account.
curator: T::AccountId,
/// The child-bounty curator account.
child_curator: T::AccountId,
/// The (total) amount that should be paid if the bounty is rewarded.
value: BalanceOf<T>,
/// The curator fee. included in value.
fee: BalanceOf<T>,
/// The (total) amount that should be paid if the child-bounty is rewarded.
child_bounty_value: BalanceOf<T>,
/// The child-bounty curator fee. included in value.
child_bounty_fee: BalanceOf<T>,
/// Bounty description.
reason: Vec<u8>,
}
fn setup_bounty<T: Config>(
user: u32,
description: u32,
) -> (T::AccountId, T::AccountId, BalanceOf<T>, BalanceOf<T>, Vec<u8>) {
let caller = account("caller", user, SEED);
let value: BalanceOf<T> = T::BountyValueMinimum::get().saturating_mul(100u32.into());
let fee = value / 2u32.into();
let deposit = T::BountyDepositBase::get() +
T::DataDepositPerByte::get() * T::MaximumReasonLength::get().into();
let _ = T::Currency::make_free_balance_be(&caller, deposit);
let curator = account("curator", user, SEED);
let _ = T::Currency::make_free_balance_be(&curator, fee / 2u32.into());
let reason = vec![0; description as usize];
(caller, curator, fee, value, reason)
}
fn setup_child_bounty<T: Config>(user: u32, description: u32) -> BenchmarkChildBounty<T> {
let (caller, curator, fee, value, reason) = setup_bounty::<T>(user, description);
let child_curator = account("child-curator", user, SEED);
let _ = T::Currency::make_free_balance_be(&child_curator, fee / 2u32.into());
let child_bounty_value = (value - fee) / 4u32.into();
let child_bounty_fee = child_bounty_value / 2u32.into();
BenchmarkChildBounty::<T> {
bounty_id: 0,
child_bounty_id: 0,
caller,
curator,
child_curator,
value,
fee,
child_bounty_value,
child_bounty_fee,
reason,
}
}
fn activate_bounty<T: Config>(
user: u32,
description: u32,
) -> Result<BenchmarkChildBounty<T>, &'static str> {
let mut child_bounty_setup = setup_child_bounty::<T>(user, description);
let curator_lookup = T::Lookup::unlookup(child_bounty_setup.curator.clone());
Bounties::<T>::propose_bounty(
RawOrigin::Signed(child_bounty_setup.caller.clone()).into(),
child_bounty_setup.value,
child_bounty_setup.reason.clone(),
)?;
child_bounty_setup.bounty_id = Bounties::<T>::bounty_count() - 1;
Bounties::<T>::approve_bounty(RawOrigin::Root.into(), child_bounty_setup.bounty_id)?;
Treasury::<T>::on_initialize(T::BlockNumber::zero());
Bounties::<T>::propose_curator(
RawOrigin::Root.into(),
child_bounty_setup.bounty_id,
curator_lookup.clone(),
child_bounty_setup.fee,
)?;
Bounties::<T>::accept_curator(
RawOrigin::Signed(child_bounty_setup.curator.clone()).into(),
child_bounty_setup.bounty_id,
)?;
Ok(child_bounty_setup)
}
fn activate_child_bounty<T: Config>(
user: u32,
description: u32,
) -> Result<BenchmarkChildBounty<T>, &'static str> {
let mut bounty_setup = activate_bounty::<T>(user, description)?;
let child_curator_lookup = T::Lookup::unlookup(bounty_setup.child_curator.clone());
ChildBounties::<T>::add_child_bounty(
RawOrigin::Signed(bounty_setup.curator.clone()).into(),
bounty_setup.bounty_id,
bounty_setup.child_bounty_value,
bounty_setup.reason.clone(),
)?;
bounty_setup.child_bounty_id = ChildBountyCount::<T>::get() - 1;
ChildBounties::<T>::propose_curator(
RawOrigin::Signed(bounty_setup.curator.clone()).into(),
bounty_setup.bounty_id,
bounty_setup.child_bounty_id,
child_curator_lookup.clone(),
bounty_setup.child_bounty_fee,
)?;
ChildBounties::<T>::accept_curator(
RawOrigin::Signed(bounty_setup.child_curator.clone()).into(),
bounty_setup.bounty_id,
bounty_setup.child_bounty_id,
)?;
Ok(bounty_setup)
}
fn setup_pot_account<T: Config>() {
let pot_account = Bounties::<T>::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) {
frame_system::Pallet::<T>::assert_last_event(generic_event.into());
}
benchmarks! {
add_child_bounty {
let d in 0 .. T::MaximumReasonLength::get();
setup_pot_account::<T>();
let bounty_setup = activate_bounty::<T>(0, d)?;
}: _(RawOrigin::Signed(bounty_setup.curator), bounty_setup.bounty_id,
bounty_setup.child_bounty_value, bounty_setup.reason.clone())
verify {
assert_last_event::<T>(Event::Added {
index: bounty_setup.bounty_id,
child_index: bounty_setup.child_bounty_id,
}.into())
}
propose_curator {
setup_pot_account::<T>();
let bounty_setup = activate_bounty::<T>(0, T::MaximumReasonLength::get())?;
let child_curator_lookup = T::Lookup::unlookup(bounty_setup.child_curator.clone());
ChildBounties::<T>::add_child_bounty(
RawOrigin::Signed(bounty_setup.curator.clone()).into(),
bounty_setup.bounty_id,
bounty_setup.child_bounty_value,
bounty_setup.reason.clone(),
)?;
let child_bounty_id = ChildBountyCount::<T>::get() - 1;
}: _(RawOrigin::Signed(bounty_setup.curator), bounty_setup.bounty_id,
child_bounty_id, child_curator_lookup, bounty_setup.child_bounty_fee)
accept_curator {
setup_pot_account::<T>();
let mut bounty_setup = activate_bounty::<T>(0, T::MaximumReasonLength::get())?;
let child_curator_lookup = T::Lookup::unlookup(bounty_setup.child_curator.clone());
ChildBounties::<T>::add_child_bounty(
RawOrigin::Signed(bounty_setup.curator.clone()).into(),
bounty_setup.bounty_id,
bounty_setup.child_bounty_value,
bounty_setup.reason.clone(),
)?;
bounty_setup.child_bounty_id = ChildBountyCount::<T>::get() - 1;
ChildBounties::<T>::propose_curator(
RawOrigin::Signed(bounty_setup.curator.clone()).into(),
bounty_setup.bounty_id,
bounty_setup.child_bounty_id,
child_curator_lookup.clone(),
bounty_setup.child_bounty_fee,
)?;
}: _(RawOrigin::Signed(bounty_setup.child_curator), bounty_setup.bounty_id,
bounty_setup.child_bounty_id)
// Worst case when curator is inactive and any sender un-assigns the curator.
unassign_curator {
setup_pot_account::<T>();
let bounty_setup = activate_child_bounty::<T>(0, T::MaximumReasonLength::get())?;
Bounties::<T>::on_initialize(T::BlockNumber::zero());
frame_system::Pallet::<T>::set_block_number(T::BountyUpdatePeriod::get() + 1u32.into());
let caller = whitelisted_caller();
}: _(RawOrigin::Signed(caller), bounty_setup.bounty_id,
bounty_setup.child_bounty_id)
award_child_bounty {
setup_pot_account::<T>();
let bounty_setup = activate_child_bounty::<T>(0, T::MaximumReasonLength::get())?;
let beneficiary_account: T::AccountId = account("beneficiary", 0, SEED);
let beneficiary = T::Lookup::unlookup(beneficiary_account.clone());
}: _(RawOrigin::Signed(bounty_setup.child_curator), bounty_setup.bounty_id,
bounty_setup.child_bounty_id, beneficiary)
verify {
assert_last_event::<T>(Event::Awarded {
index: bounty_setup.bounty_id,
child_index: bounty_setup.child_bounty_id,
beneficiary: beneficiary_account
}.into())
}
claim_child_bounty {
setup_pot_account::<T>();
let bounty_setup = activate_child_bounty::<T>(0, T::MaximumReasonLength::get())?;
let beneficiary_account: T::AccountId = account("beneficiary", 0, SEED);
let beneficiary = T::Lookup::unlookup(beneficiary_account.clone());
ChildBounties::<T>::award_child_bounty(
RawOrigin::Signed(bounty_setup.child_curator.clone()).into(),
bounty_setup.bounty_id,
bounty_setup.child_bounty_id,
beneficiary
)?;
let beneficiary_account: T::AccountId = account("beneficiary", 0, SEED);
let beneficiary = T::Lookup::unlookup(beneficiary_account.clone());
frame_system::Pallet::<T>::set_block_number(T::BountyDepositPayoutDelay::get());
ensure!(T::Currency::free_balance(&beneficiary_account).is_zero(),
"Beneficiary already has balance.");
}: _(RawOrigin::Signed(bounty_setup.curator), bounty_setup.bounty_id,
bounty_setup.child_bounty_id)
verify {
ensure!(!T::Currency::free_balance(&beneficiary_account).is_zero(),
"Beneficiary didn't get paid.");
}
// Best case scenario.
close_child_bounty_added {
setup_pot_account::<T>();
let mut bounty_setup = activate_bounty::<T>(0, T::MaximumReasonLength::get())?;
ChildBounties::<T>::add_child_bounty(
RawOrigin::Signed(bounty_setup.curator.clone()).into(),
bounty_setup.bounty_id,
bounty_setup.child_bounty_value,
bounty_setup.reason.clone(),
)?;
bounty_setup.child_bounty_id = ChildBountyCount::<T>::get() - 1;
}: close_child_bounty(RawOrigin::Root, bounty_setup.bounty_id,
bounty_setup.child_bounty_id)
verify {
assert_last_event::<T>(Event::Canceled {
index: bounty_setup.bounty_id,
child_index: bounty_setup.child_bounty_id
}.into())
}
// Worst case scenario.
close_child_bounty_active {
setup_pot_account::<T>();
let bounty_setup = activate_child_bounty::<T>(0, T::MaximumReasonLength::get())?;
Bounties::<T>::on_initialize(T::BlockNumber::zero());
}: close_child_bounty(RawOrigin::Root, bounty_setup.bounty_id, bounty_setup.child_bounty_id)
verify {
assert_last_event::<T>(Event::Canceled {
index: bounty_setup.bounty_id,
child_index: bounty_setup.child_bounty_id,
}.into())
}
impl_benchmark_test_suite!(ChildBounties, crate::tests::new_test_ext(), crate::tests::Test)
}
+897
View File
@@ -0,0 +1,897 @@
// 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.
//! # Child Bounties Pallet ( pallet-child-bounties )
//!
//! ## Child Bounty
//!
//! > NOTE: This pallet is tightly coupled with pallet-treasury and pallet-bounties.
//!
//! With child bounties, a large bounty proposal can be divided into smaller chunks,
//! for parallel execution, and for efficient governance and tracking of spent funds.
//! A child-bounty is a smaller piece of work, extracted from a parent bounty.
//! A curator is assigned after the child-bounty is created by the parent bounty curator,
//! to be delegated with the responsibility of assigning a payout address once the specified
//! set of tasks is completed.
//!
//! ## Interface
//!
//! ### Dispatchable Functions
//!
//! Child Bounty protocol:
//! - `add_child_bounty` - Add a child-bounty for a parent-bounty to for dividing the work in
//! smaller tasks.
//! - `propose_curator` - Assign an account to a child-bounty as candidate curator.
//! - `accept_curator` - Accept a child-bounty assignment from the parent-bounty curator, setting a
//! curator deposit.
//! - `award_child_bounty` - Close and pay out the specified amount for the completed work.
//! - `claim_child_bounty` - Claim a specific child-bounty amount from the payout address.
//! - `unassign_curator` - Unassign an accepted curator from a specific child-bounty.
//! - `close_child_bounty` - Cancel the child-bounty for a specific treasury amount and close the
//! bounty.
// Most of the business logic in this pallet has been
// originally contributed by "https://github.com/shamb0",
// as part of the PR - https://github.com/paritytech/substrate/pull/7965.
// The code has been moved here and then refactored in order to
// extract child-bounties as a separate pallet.
#![cfg_attr(not(feature = "std"), no_std)]
mod benchmarking;
mod tests;
pub mod weights;
use sp_std::prelude::*;
use frame_support::traits::{
Currency,
ExistenceRequirement::{AllowDeath, KeepAlive},
Get, OnUnbalanced, ReservableCurrency, WithdrawReasons,
};
use sp_runtime::{
traits::{AccountIdConversion, BadOrigin, CheckedSub, Saturating, StaticLookup, Zero},
DispatchResult, Permill, RuntimeDebug,
};
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use pallet_bounties::BountyStatus;
use scale_info::TypeInfo;
pub use weights::WeightInfo;
pub use pallet::*;
type BalanceOf<T> = pallet_treasury::BalanceOf<T>;
type BountiesError<T> = pallet_bounties::Error<T>;
type BountyIndex = pallet_bounties::BountyIndex;
/// A child bounty proposal.
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub struct ChildBounty<AccountId, Balance, BlockNumber> {
/// The parent of this child-bounty.
parent_bounty: BountyIndex,
/// The (total) amount that should be paid if this child-bounty is rewarded.
value: Balance,
/// The child bounty curator fee.
fee: Balance,
/// The deposit of child-bounty curator.
curator_deposit: Balance,
/// The status of this child-bounty.
status: ChildBountyStatus<AccountId, BlockNumber>,
}
/// The status of a child-bounty.
#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
pub enum ChildBountyStatus<AccountId, BlockNumber> {
/// The child-bounty is added and waiting for curator assignment.
Added,
/// A curator has been proposed by the parent-bounty curator. Waiting for
/// acceptance from the child-bounty curator.
CuratorProposed {
/// The assigned child-bounty curator of this bounty.
curator: AccountId,
},
/// The child-bounty is active and waiting to be awarded.
Active {
/// The curator of this child-bounty.
curator: AccountId,
},
/// The child-bounty is awarded and waiting to released after a delay.
PendingPayout {
/// The curator of this child-bounty.
curator: AccountId,
/// The beneficiary of the child-bounty.
beneficiary: AccountId,
/// When the child-bounty can be claimed.
unlock_at: BlockNumber,
},
}
#[frame_support::pallet]
pub mod pallet {
use super::*;
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
#[pallet::generate_storage_info]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config:
frame_system::Config + pallet_treasury::Config + pallet_bounties::Config
{
/// Maximum number of child-bounties that can be added to a parent bounty.
#[pallet::constant]
type MaxActiveChildBountyCount: Get<u32>;
/// Minimum value for a child-bounty.
#[pallet::constant]
type ChildBountyValueMinimum: Get<BalanceOf<Self>>;
/// Percentage of child-bounty value to be reserved as curator deposit
/// when curator fee is zero.
#[pallet::constant]
type ChildBountyCuratorDepositBase: Get<Permill>;
/// The overarching event type.
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;
}
#[pallet::error]
pub enum Error<T> {
/// The parent bounty is not in active state.
ParentBountyNotActive,
/// The bounty balance is not enough to add new child-bounty.
InsufficientBountyBalance,
/// Number of child-bounties exceeds limit `MaxActiveChildBountyCount`.
TooManyChildBounties,
}
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// A child-bounty is added.
Added { index: BountyIndex, child_index: BountyIndex },
/// A child-bounty is awarded to a beneficiary.
Awarded { index: BountyIndex, child_index: BountyIndex, beneficiary: T::AccountId },
/// A child-bounty is claimed by beneficiary.
Claimed {
index: BountyIndex,
child_index: BountyIndex,
payout: BalanceOf<T>,
beneficiary: T::AccountId,
},
/// A child-bounty is cancelled.
Canceled { index: BountyIndex, child_index: BountyIndex },
}
/// Number of total child bounties.
#[pallet::storage]
#[pallet::getter(fn child_bounty_count)]
pub type ChildBountyCount<T: Config> = StorageValue<_, BountyIndex, ValueQuery>;
/// Number of child-bounties per parent bounty.
/// Map of parent bounty index to number of child bounties.
#[pallet::storage]
#[pallet::getter(fn parent_child_bounties)]
pub type ParentChildBounties<T: Config> =
StorageMap<_, Twox64Concat, BountyIndex, u32, ValueQuery>;
/// Child-bounties that have been added.
#[pallet::storage]
#[pallet::getter(fn child_bounties)]
pub type ChildBounties<T: Config> = StorageDoubleMap<
_,
Twox64Concat,
BountyIndex,
Twox64Concat,
BountyIndex,
ChildBounty<T::AccountId, BalanceOf<T>, T::BlockNumber>,
>;
/// The description of each child-bounty.
#[pallet::storage]
#[pallet::getter(fn child_bounty_descriptions)]
pub type ChildBountyDescriptions<T: Config> =
StorageMap<_, Twox64Concat, BountyIndex, BoundedVec<u8, T::MaximumReasonLength>>;
/// The cumulative child-bounty curator fee for each parent bounty.
#[pallet::storage]
#[pallet::getter(fn children_curator_fees)]
pub type ChildrenCuratorFees<T: Config> =
StorageMap<_, Twox64Concat, BountyIndex, BalanceOf<T>, ValueQuery>;
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Add a new child-bounty.
///
/// The dispatch origin for this call must be the curator of parent
/// bounty and the parent bounty must be in "active" state.
///
/// Child-bounty gets added successfully & fund gets transferred from
/// parent bounty to child-bounty account, if parent bounty has enough
/// funds, else the call fails.
///
/// Upper bound to maximum number of active child-bounties that can be
/// added are managed via runtime trait config
/// [`Config::MaxActiveChildBountyCount`].
///
/// If the call is success, the status of child-bounty is updated to
/// "Added".
///
/// - `parent_bounty_id`: Index of parent bounty for which child-bounty is being added.
/// - `value`: Value for executing the proposal.
/// - `description`: Text description for the child-bounty.
#[pallet::weight(<T as Config>::WeightInfo::add_child_bounty(description.len() as u32))]
pub fn add_child_bounty(
origin: OriginFor<T>,
#[pallet::compact] parent_bounty_id: BountyIndex,
#[pallet::compact] value: BalanceOf<T>,
description: Vec<u8>,
) -> DispatchResult {
let signer = ensure_signed(origin)?;
// Verify the arguments.
let bounded_description =
description.try_into().map_err(|_| BountiesError::<T>::ReasonTooBig)?;
ensure!(value >= T::ChildBountyValueMinimum::get(), BountiesError::<T>::InvalidValue);
ensure!(
Self::parent_child_bounties(parent_bounty_id) <=
T::MaxActiveChildBountyCount::get() as u32,
Error::<T>::TooManyChildBounties,
);
let (curator, _) = Self::ensure_bounty_active(parent_bounty_id)?;
ensure!(signer == curator, BountiesError::<T>::RequireCurator);
// Read parent bounty account info.
let parent_bounty_account =
pallet_bounties::Pallet::<T>::bounty_account_id(parent_bounty_id);
// Ensure parent bounty has enough balance after adding child-bounty.
let bounty_balance = T::Currency::free_balance(&parent_bounty_account);
let new_bounty_balance = bounty_balance
.checked_sub(&value)
.ok_or(Error::<T>::InsufficientBountyBalance)?;
T::Currency::ensure_can_withdraw(
&parent_bounty_account,
value,
WithdrawReasons::TRANSFER,
new_bounty_balance,
)?;
// Get child-bounty ID.
let child_bounty_id = Self::child_bounty_count();
let child_bounty_account = Self::child_bounty_account_id(child_bounty_id);
// Transfer funds from parent bounty to child-bounty.
T::Currency::transfer(&parent_bounty_account, &child_bounty_account, value, KeepAlive)?;
// Increment the active child-bounty count.
<ParentChildBounties<T>>::mutate(parent_bounty_id, |count| count.saturating_inc());
<ChildBountyCount<T>>::put(child_bounty_id.saturating_add(1));
// Create child-bounty instance.
Self::create_child_bounty(
parent_bounty_id,
child_bounty_id,
value,
bounded_description,
);
Ok(())
}
/// Propose curator for funded child-bounty.
///
/// The dispatch origin for this call must be curator of parent bounty.
///
/// Parent bounty must be in active state, for this child-bounty call to
/// work.
///
/// Child-bounty must be in "Added" state, for processing the call. And
/// state of child-bounty is moved to "CuratorProposed" on successful
/// call completion.
///
/// - `parent_bounty_id`: Index of parent bounty.
/// - `child_bounty_id`: Index of child bounty.
/// - `curator`: Address of child-bounty curator.
/// - `fee`: payment fee to child-bounty curator for execution.
#[pallet::weight(<T as Config>::WeightInfo::propose_curator())]
pub fn propose_curator(
origin: OriginFor<T>,
#[pallet::compact] parent_bounty_id: BountyIndex,
#[pallet::compact] child_bounty_id: BountyIndex,
curator: <T::Lookup as StaticLookup>::Source,
#[pallet::compact] fee: BalanceOf<T>,
) -> DispatchResult {
let signer = ensure_signed(origin)?;
let child_bounty_curator = T::Lookup::lookup(curator)?;
let (curator, _) = Self::ensure_bounty_active(parent_bounty_id)?;
ensure!(signer == curator, BountiesError::<T>::RequireCurator);
// Mutate the child-bounty instance.
ChildBounties::<T>::try_mutate_exists(
parent_bounty_id,
child_bounty_id,
|maybe_child_bounty| -> DispatchResult {
let mut child_bounty =
maybe_child_bounty.as_mut().ok_or(BountiesError::<T>::InvalidIndex)?;
// Ensure child-bounty is in expected state.
ensure!(
child_bounty.status == ChildBountyStatus::Added,
BountiesError::<T>::UnexpectedStatus,
);
// Ensure child-bounty curator fee is less than child-bounty value.
ensure!(fee < child_bounty.value, BountiesError::<T>::InvalidFee);
// Add child-bounty curator fee to the cumulative sum. To be
// subtracted from the parent bounty curator when claiming
// bounty.
ChildrenCuratorFees::<T>::mutate(parent_bounty_id, |value| {
*value = value.saturating_add(fee)
});
// Update the child-bounty curator fee.
child_bounty.fee = fee;
// Update the child-bounty state.
child_bounty.status =
ChildBountyStatus::CuratorProposed { curator: child_bounty_curator };
Ok(())
},
)
}
/// Accept the curator role for the child-bounty.
///
/// The dispatch origin for this call must be the curator of this
/// child-bounty.
///
/// A deposit will be reserved from the curator and refund upon
/// successful payout or cancellation.
///
/// Fee for curator is deducted from curator fee of parent bounty.
///
/// Parent bounty must be in active state, for this child-bounty call to
/// work.
///
/// Child-bounty must be in "CuratorProposed" state, for processing the
/// call. And state of child-bounty is moved to "Active" on successful
/// call completion.
///
/// - `parent_bounty_id`: Index of parent bounty.
/// - `child_bounty_id`: Index of child bounty.
#[pallet::weight(<T as Config>::WeightInfo::accept_curator())]
pub fn accept_curator(
origin: OriginFor<T>,
#[pallet::compact] parent_bounty_id: BountyIndex,
#[pallet::compact] child_bounty_id: BountyIndex,
) -> DispatchResult {
let signer = ensure_signed(origin)?;
let _ = Self::ensure_bounty_active(parent_bounty_id)?;
// Mutate child-bounty.
ChildBounties::<T>::try_mutate_exists(
parent_bounty_id,
child_bounty_id,
|maybe_child_bounty| -> DispatchResult {
let mut child_bounty =
maybe_child_bounty.as_mut().ok_or(BountiesError::<T>::InvalidIndex)?;
// Ensure child-bounty is in expected state.
if let ChildBountyStatus::CuratorProposed { ref curator } = child_bounty.status
{
ensure!(signer == *curator, BountiesError::<T>::RequireCurator);
// Reserve child-bounty curator deposit. Curator deposit
// is reserved based on a percentage of child-bounty
// value instead of fee, to avoid no deposit in case the
// fee is set as zero.
let deposit = T::ChildBountyCuratorDepositBase::get() * child_bounty.value;
T::Currency::reserve(curator, deposit)?;
child_bounty.curator_deposit = deposit;
child_bounty.status =
ChildBountyStatus::Active { curator: curator.clone() };
Ok(())
} else {
Err(BountiesError::<T>::UnexpectedStatus.into())
}
},
)
}
/// Unassign curator from a child-bounty.
///
/// The dispatch origin for this call can be either `RejectOrigin`, or
/// the curator of the parent bounty, or any signed origin.
///
/// For the origin other than T::RejectOrigin and the child-bounty
/// curator, parent-bounty must be in active state, for this call to
/// work. We allow child-bounty curator and T::RejectOrigin to execute
/// this call irrespective of the parent-bounty state.
///
/// If this function is called by the `RejectOrigin` or the
/// parent-bounty curator, we assume that the child-bounty curator is
/// malicious or inactive. As a result, child-bounty curator deposit is
/// slashed.
///
/// If the origin is the child-bounty curator, we take this as a sign
/// that they are unable to do their job, and are willingly giving up.
/// We could slash the deposit, but for now we allow them to unreserve
/// their deposit and exit without issue. (We may want to change this if
/// it is abused.)
///
/// Finally, the origin can be anyone iff the child-bounty curator is
/// "inactive". Expiry update due of parent bounty is used to estimate
/// inactive state of child-bounty curator.
///
/// This allows anyone in the community to call out that a child-bounty
/// curator is not doing their due diligence, and we should pick a new
/// one. In this case the child-bounty curator deposit is slashed.
///
/// State of child-bounty is moved to Added state on successful call
/// completion.
///
/// - `parent_bounty_id`: Index of parent bounty.
/// - `child_bounty_id`: Index of child bounty.
#[pallet::weight(<T as Config>::WeightInfo::unassign_curator())]
pub fn unassign_curator(
origin: OriginFor<T>,
#[pallet::compact] parent_bounty_id: BountyIndex,
#[pallet::compact] child_bounty_id: BountyIndex,
) -> DispatchResult {
let maybe_sender = ensure_signed(origin.clone())
.map(Some)
.or_else(|_| T::RejectOrigin::ensure_origin(origin).map(|_| None))?;
ChildBounties::<T>::try_mutate_exists(
parent_bounty_id,
child_bounty_id,
|maybe_child_bounty| -> DispatchResult {
let mut child_bounty =
maybe_child_bounty.as_mut().ok_or(BountiesError::<T>::InvalidIndex)?;
let slash_curator = |curator: &T::AccountId,
curator_deposit: &mut BalanceOf<T>| {
let imbalance = T::Currency::slash_reserved(curator, *curator_deposit).0;
T::OnSlash::on_unbalanced(imbalance);
*curator_deposit = Zero::zero();
};
match child_bounty.status {
ChildBountyStatus::Added => {
// No curator to unassign at this point.
return Err(BountiesError::<T>::UnexpectedStatus.into())
},
ChildBountyStatus::CuratorProposed { ref curator } => {
// A child-bounty curator has been proposed, but not accepted yet.
// Either `RejectOrigin`, parent-bounty curator or the proposed
// child-bounty curator can unassign the child-bounty curator.
ensure!(
maybe_sender.map_or(true, |sender| {
sender == *curator ||
Self::ensure_bounty_active(parent_bounty_id)
.map_or(false, |(parent_curator, _)| {
sender == parent_curator
})
}),
BadOrigin
);
// Continue to change bounty status below.
},
ChildBountyStatus::Active { ref curator } => {
// The child-bounty is active.
match maybe_sender {
// If the `RejectOrigin` is calling this function, slash the curator
// deposit.
None => {
slash_curator(curator, &mut child_bounty.curator_deposit);
// Continue to change child-bounty status below.
},
Some(sender) if sender == *curator => {
// This is the child-bounty curator, willingly giving up their
// role. Give back their deposit.
T::Currency::unreserve(&curator, child_bounty.curator_deposit);
// Reset curator deposit.
child_bounty.curator_deposit = Zero::zero();
// Continue to change bounty status below.
},
Some(sender) => {
let (parent_curator, update_due) =
Self::ensure_bounty_active(parent_bounty_id)?;
if sender == parent_curator ||
update_due < frame_system::Pallet::<T>::block_number()
{
// Slash the child-bounty curator if
// + the call is made by the parent-bounty curator.
// + or the curator is inactive.
slash_curator(curator, &mut child_bounty.curator_deposit);
// Continue to change bounty status below.
} else {
// Curator has more time to give an update.
return Err(BountiesError::<T>::Premature.into())
}
},
}
},
ChildBountyStatus::PendingPayout { ref curator, .. } => {
let (parent_curator, _) = Self::ensure_bounty_active(parent_bounty_id)?;
ensure!(
maybe_sender.map_or(true, |sender| parent_curator == sender),
BadOrigin,
);
slash_curator(curator, &mut child_bounty.curator_deposit);
// Continue to change child-bounty status below.
},
};
// Move the child-bounty state to Added.
child_bounty.status = ChildBountyStatus::Added;
Ok(())
},
)
}
/// Award child-bounty to a beneficiary.
///
/// The beneficiary will be able to claim the funds after a delay.
///
/// The dispatch origin for this call must be the master curator or
/// curator of this child-bounty.
///
/// Parent bounty must be in active state, for this child-bounty call to
/// work.
///
/// Child-bounty must be in active state, for processing the call. And
/// state of child-bounty is moved to "PendingPayout" on successful call
/// completion.
///
/// - `parent_bounty_id`: Index of parent bounty.
/// - `child_bounty_id`: Index of child bounty.
/// - `beneficiary`: Beneficiary account.
#[pallet::weight(<T as Config>::WeightInfo::award_child_bounty())]
pub fn award_child_bounty(
origin: OriginFor<T>,
#[pallet::compact] parent_bounty_id: BountyIndex,
#[pallet::compact] child_bounty_id: BountyIndex,
beneficiary: <T::Lookup as StaticLookup>::Source,
) -> DispatchResult {
let signer = ensure_signed(origin)?;
let beneficiary = T::Lookup::lookup(beneficiary)?;
// Ensure parent bounty exists, and is active.
let (parent_curator, _) = Self::ensure_bounty_active(parent_bounty_id)?;
ChildBounties::<T>::try_mutate_exists(
parent_bounty_id,
child_bounty_id,
|maybe_child_bounty| -> DispatchResult {
let mut child_bounty =
maybe_child_bounty.as_mut().ok_or(BountiesError::<T>::InvalidIndex)?;
// Ensure child-bounty is in active state.
if let ChildBountyStatus::Active { ref curator } = child_bounty.status {
ensure!(
signer == *curator || signer == parent_curator,
BountiesError::<T>::RequireCurator,
);
// Move the child-bounty state to pending payout.
child_bounty.status = ChildBountyStatus::PendingPayout {
curator: signer,
beneficiary: beneficiary.clone(),
unlock_at: frame_system::Pallet::<T>::block_number() +
T::BountyDepositPayoutDelay::get(),
};
Ok(())
} else {
Err(BountiesError::<T>::UnexpectedStatus.into())
}
},
)?;
// Trigger the event Awarded.
Self::deposit_event(Event::<T>::Awarded {
index: parent_bounty_id,
child_index: child_bounty_id,
beneficiary,
});
Ok(())
}
/// Claim the payout from an awarded child-bounty after payout delay.
///
/// The dispatch origin for this call may be any signed origin.
///
/// Call works independent of parent bounty state, No need for parent
/// bounty to be in active state.
///
/// The Beneficiary is paid out with agreed bounty value. Curator fee is
/// paid & curator deposit is unreserved.
///
/// Child-bounty must be in "PendingPayout" state, for processing the
/// call. And instance of child-bounty is removed from the state on
/// successful call completion.
///
/// - `parent_bounty_id`: Index of parent bounty.
/// - `child_bounty_id`: Index of child bounty.
#[pallet::weight(<T as Config>::WeightInfo::claim_child_bounty())]
pub fn claim_child_bounty(
origin: OriginFor<T>,
#[pallet::compact] parent_bounty_id: BountyIndex,
#[pallet::compact] child_bounty_id: BountyIndex,
) -> DispatchResult {
let _ = ensure_signed(origin)?;
// Ensure child-bounty is in expected state.
ChildBounties::<T>::try_mutate_exists(
parent_bounty_id,
child_bounty_id,
|maybe_child_bounty| -> DispatchResult {
let child_bounty =
maybe_child_bounty.as_mut().ok_or(BountiesError::<T>::InvalidIndex)?;
if let ChildBountyStatus::PendingPayout {
ref curator,
ref beneficiary,
ref unlock_at,
} = child_bounty.status
{
// Ensure block number is elapsed for processing the
// claim.
ensure!(
frame_system::Pallet::<T>::block_number() >= *unlock_at,
BountiesError::<T>::Premature,
);
// Make curator fee payment.
let child_bounty_account = Self::child_bounty_account_id(child_bounty_id);
let balance = T::Currency::free_balance(&child_bounty_account);
let curator_fee = child_bounty.fee.min(balance);
let payout = balance.saturating_sub(curator_fee);
// Unreserve the curator deposit. Should not fail
// because the deposit is always reserved when curator is
// assigned.
let _ = T::Currency::unreserve(&curator, child_bounty.curator_deposit);
// Make payout to child-bounty curator.
// Should not fail because curator fee is always less than bounty value.
let fee_transfer_result = T::Currency::transfer(
&child_bounty_account,
&curator,
curator_fee,
AllowDeath,
);
debug_assert!(fee_transfer_result.is_ok());
// Make payout to beneficiary.
// Should not fail.
let payout_transfer_result = T::Currency::transfer(
&child_bounty_account,
beneficiary,
payout,
AllowDeath,
);
debug_assert!(payout_transfer_result.is_ok());
// Trigger the Claimed event.
Self::deposit_event(Event::<T>::Claimed {
index: parent_bounty_id,
child_index: child_bounty_id,
payout,
beneficiary: beneficiary.clone(),
});
// Update the active child-bounty tracking count.
<ParentChildBounties<T>>::mutate(parent_bounty_id, |count| {
count.saturating_dec()
});
// Remove the child-bounty description.
<ChildBountyDescriptions<T>>::remove(child_bounty_id);
// Remove the child-bounty instance from the state.
*maybe_child_bounty = None;
Ok(())
} else {
Err(BountiesError::<T>::UnexpectedStatus.into())
}
},
)
}
/// Cancel a proposed or active child-bounty. Child-bounty account funds
/// are transferred to parent bounty account. The child-bounty curator
/// deposit may be unreserved if possible.
///
/// The dispatch origin for this call must be either parent curator or
/// `T::RejectOrigin`.
///
/// If the state of child-bounty is `Active`, curator deposit is
/// unreserved.
///
/// If the state of child-bounty is `PendingPayout`, call fails &
/// returns `PendingPayout` error.
///
/// For the origin other than T::RejectOrigin, parent bounty must be in
/// active state, for this child-bounty call to work. For origin
/// T::RejectOrigin execution is forced.
///
/// Instance of child-bounty is removed from the state on successful
/// call completion.
///
/// - `parent_bounty_id`: Index of parent bounty.
/// - `child_bounty_id`: Index of child bounty.
#[pallet::weight(<T as Config>::WeightInfo::close_child_bounty_added()
.max(<T as Config>::WeightInfo::close_child_bounty_active()))]
pub fn close_child_bounty(
origin: OriginFor<T>,
#[pallet::compact] parent_bounty_id: BountyIndex,
#[pallet::compact] child_bounty_id: BountyIndex,
) -> DispatchResult {
let maybe_sender = ensure_signed(origin.clone())
.map(Some)
.or_else(|_| T::RejectOrigin::ensure_origin(origin).map(|_| None))?;
// Ensure parent bounty exist, get parent curator.
let (parent_curator, _) = Self::ensure_bounty_active(parent_bounty_id)?;
ensure!(maybe_sender.map_or(true, |sender| parent_curator == sender), BadOrigin);
Self::impl_close_child_bounty(parent_bounty_id, child_bounty_id)?;
Ok(())
}
}
}
impl<T: Config> Pallet<T> {
/// The account ID of a child-bounty account.
pub fn child_bounty_account_id(id: BountyIndex) -> T::AccountId {
// This function is taken from the parent (bounties) pallet, but the
// prefix is changed to have different AccountId when the index of
// parent and child is same.
T::PalletId::get().into_sub_account(("cb", id))
}
fn create_child_bounty(
parent_bounty_id: BountyIndex,
child_bounty_id: BountyIndex,
child_bounty_value: BalanceOf<T>,
description: BoundedVec<u8, T::MaximumReasonLength>,
) {
let child_bounty = ChildBounty {
parent_bounty: parent_bounty_id,
value: child_bounty_value,
fee: 0u32.into(),
curator_deposit: 0u32.into(),
status: ChildBountyStatus::Added,
};
ChildBounties::<T>::insert(parent_bounty_id, child_bounty_id, &child_bounty);
ChildBountyDescriptions::<T>::insert(child_bounty_id, description);
Self::deposit_event(Event::Added { index: parent_bounty_id, child_index: child_bounty_id });
}
fn ensure_bounty_active(
bounty_id: BountyIndex,
) -> Result<(T::AccountId, T::BlockNumber), DispatchError> {
let parent_bounty = pallet_bounties::Pallet::<T>::bounties(bounty_id)
.ok_or(BountiesError::<T>::InvalidIndex)?;
if let BountyStatus::Active { curator, update_due } = parent_bounty.get_status() {
Ok((curator, update_due))
} else {
Err(Error::<T>::ParentBountyNotActive.into())
}
}
fn impl_close_child_bounty(
parent_bounty_id: BountyIndex,
child_bounty_id: BountyIndex,
) -> DispatchResult {
ChildBounties::<T>::try_mutate_exists(
parent_bounty_id,
child_bounty_id,
|maybe_child_bounty| -> DispatchResult {
let child_bounty =
maybe_child_bounty.as_mut().ok_or(BountiesError::<T>::InvalidIndex)?;
match &child_bounty.status {
ChildBountyStatus::Added | ChildBountyStatus::CuratorProposed { .. } => {
// Nothing extra to do besides the removal of the child-bounty below.
},
ChildBountyStatus::Active { curator } => {
// Cancelled by master curator or RejectOrigin,
// refund deposit of the working child-bounty curator.
let _ = T::Currency::unreserve(curator, child_bounty.curator_deposit);
// Then execute removal of the child-bounty below.
},
ChildBountyStatus::PendingPayout { .. } => {
// Child-bounty is already in pending payout. If parent
// curator or RejectOrigin wants to close this
// child-bounty, it should mean the child-bounty curator
// was acting maliciously. So first unassign the
// child-bounty curator, slashing their deposit.
return Err(BountiesError::<T>::PendingPayout.into())
},
}
// Revert the curator fee back to parent-bounty curator &
// reduce the active child-bounty count.
ChildrenCuratorFees::<T>::mutate(parent_bounty_id, |value| {
*value = value.saturating_sub(child_bounty.fee)
});
<ParentChildBounties<T>>::mutate(parent_bounty_id, |count| {
*count = count.saturating_sub(1)
});
// Transfer fund from child-bounty to parent bounty.
let parent_bounty_account =
pallet_bounties::Pallet::<T>::bounty_account_id(parent_bounty_id);
let child_bounty_account = Self::child_bounty_account_id(child_bounty_id);
let balance = T::Currency::free_balance(&child_bounty_account);
let transfer_result = T::Currency::transfer(
&child_bounty_account,
&parent_bounty_account,
balance,
AllowDeath,
); // Should not fail; child bounty account gets this balance during creation.
debug_assert!(transfer_result.is_ok());
// Remove the child-bounty description.
<ChildBountyDescriptions<T>>::remove(child_bounty_id);
*maybe_child_bounty = None;
Self::deposit_event(Event::<T>::Canceled {
index: parent_bounty_id,
child_index: child_bounty_id,
});
Ok(())
},
)
}
}
// Implement ChildBountyManager to connect with the bounties pallet. This is
// where we pass the active child-bounties and child curator fees to the parent
// bounty.
impl<T: Config> pallet_bounties::ChildBountyManager<BalanceOf<T>> for Pallet<T> {
fn child_bounties_count(
bounty_id: pallet_bounties::BountyIndex,
) -> pallet_bounties::BountyIndex {
Self::parent_child_bounties(bounty_id)
}
fn children_curator_fees(bounty_id: pallet_bounties::BountyIndex) -> BalanceOf<T> {
// This is asked for when the parent bounty is being claimed. No use of
// keeping it in state after that. Hence removing.
let children_fee_total = Self::children_curator_fees(bounty_id);
<ChildrenCuratorFees<T>>::remove(bounty_id);
children_fee_total
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,215 @@
// 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 pallet_child_bounties
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
//! DATE: 2021-12-03, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 128
// Executed Command:
// ./target/release/substrate
// benchmark
// --chain=dev
// --steps=50
// --repeat=20
// --pallet
// pallet_child_bounties
// --extrinsic=*
// --execution=wasm
// --wasm-execution=compiled
// --heap-pages=4096
// --output=./frame/child-bounties/src/
// --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 pallet_child_bounties.
pub trait WeightInfo {
fn add_child_bounty(d: u32, ) -> Weight;
fn propose_curator() -> Weight;
fn accept_curator() -> Weight;
fn unassign_curator() -> Weight;
fn award_child_bounty() -> Weight;
fn claim_child_bounty() -> Weight;
fn close_child_bounty_added() -> Weight;
fn close_child_bounty_active() -> Weight;
}
/// Weights for pallet_child_bounties using the Substrate node and recommended hardware.
pub struct SubstrateWeight<T>(PhantomData<T>);
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
// Storage: ChildBounties ParentChildBounties (r:1 w:1)
// Storage: Bounties Bounties (r:1 w:0)
// Storage: System Account (r:2 w:2)
// Storage: ChildBounties ChildBountyCount (r:1 w:1)
// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1)
// Storage: ChildBounties ChildBounties (r:0 w:1)
fn add_child_bounty(d: u32, ) -> Weight {
(113_579_000 as Weight)
// Standard Error: 1_000
.saturating_add((4_000 as Weight).saturating_mul(d as Weight))
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(6 as Weight))
}
// Storage: Bounties Bounties (r:1 w:0)
// Storage: ChildBounties ChildBounties (r:1 w:1)
// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1)
fn propose_curator() -> Weight {
(24_000_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(2 as Weight))
}
// Storage: Bounties Bounties (r:1 w:0)
// Storage: ChildBounties ChildBounties (r:1 w:1)
// Storage: System Account (r:1 w:1)
fn accept_curator() -> Weight {
(55_000_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(2 as Weight))
}
// Storage: ChildBounties ChildBounties (r:1 w:1)
// Storage: Bounties Bounties (r:1 w:0)
// Storage: System Account (r:1 w:1)
fn unassign_curator() -> Weight {
(61_000_000 as Weight)
.saturating_add(T::DbWeight::get().reads(3 as Weight))
.saturating_add(T::DbWeight::get().writes(2 as Weight))
}
// Storage: Bounties Bounties (r:1 w:0)
// Storage: ChildBounties ChildBounties (r:1 w:1)
fn award_child_bounty() -> Weight {
(42_000_000 as Weight)
.saturating_add(T::DbWeight::get().reads(2 as Weight))
.saturating_add(T::DbWeight::get().writes(1 as Weight))
}
// Storage: ChildBounties ChildBounties (r:1 w:1)
// Storage: System Account (r:3 w:3)
// Storage: ChildBounties ParentChildBounties (r:1 w:1)
// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1)
fn claim_child_bounty() -> Weight {
(153_000_000 as Weight)
.saturating_add(T::DbWeight::get().reads(5 as Weight))
.saturating_add(T::DbWeight::get().writes(6 as Weight))
}
// Storage: Bounties Bounties (r:1 w:0)
// Storage: ChildBounties ChildBounties (r:1 w:1)
// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1)
// Storage: ChildBounties ParentChildBounties (r:1 w:1)
// Storage: System Account (r:2 w:2)
// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1)
fn close_child_bounty_added() -> Weight {
(101_000_000 as Weight)
.saturating_add(T::DbWeight::get().reads(6 as Weight))
.saturating_add(T::DbWeight::get().writes(6 as Weight))
}
// Storage: Bounties Bounties (r:1 w:0)
// Storage: ChildBounties ChildBounties (r:1 w:1)
// Storage: System Account (r:3 w:3)
// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1)
// Storage: ChildBounties ParentChildBounties (r:1 w:1)
// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1)
fn close_child_bounty_active() -> Weight {
(130_000_000 as Weight)
.saturating_add(T::DbWeight::get().reads(7 as Weight))
.saturating_add(T::DbWeight::get().writes(7 as Weight))
}
}
// For backwards compatibility and tests
impl WeightInfo for () {
// Storage: ChildBounties ParentChildBounties (r:1 w:1)
// Storage: Bounties Bounties (r:1 w:0)
// Storage: System Account (r:2 w:2)
// Storage: ChildBounties ChildBountyCount (r:1 w:1)
// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1)
// Storage: ChildBounties ChildBounties (r:0 w:1)
fn add_child_bounty(d: u32, ) -> Weight {
(113_579_000 as Weight)
// Standard Error: 1_000
.saturating_add((4_000 as Weight).saturating_mul(d as Weight))
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(6 as Weight))
}
// Storage: Bounties Bounties (r:1 w:0)
// Storage: ChildBounties ChildBounties (r:1 w:1)
// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1)
fn propose_curator() -> Weight {
(24_000_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
}
// Storage: Bounties Bounties (r:1 w:0)
// Storage: ChildBounties ChildBounties (r:1 w:1)
// Storage: System Account (r:1 w:1)
fn accept_curator() -> Weight {
(55_000_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
}
// Storage: ChildBounties ChildBounties (r:1 w:1)
// Storage: Bounties Bounties (r:1 w:0)
// Storage: System Account (r:1 w:1)
fn unassign_curator() -> Weight {
(61_000_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(3 as Weight))
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
}
// Storage: Bounties Bounties (r:1 w:0)
// Storage: ChildBounties ChildBounties (r:1 w:1)
fn award_child_bounty() -> Weight {
(42_000_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(2 as Weight))
.saturating_add(RocksDbWeight::get().writes(1 as Weight))
}
// Storage: ChildBounties ChildBounties (r:1 w:1)
// Storage: System Account (r:3 w:3)
// Storage: ChildBounties ParentChildBounties (r:1 w:1)
// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1)
fn claim_child_bounty() -> Weight {
(153_000_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(5 as Weight))
.saturating_add(RocksDbWeight::get().writes(6 as Weight))
}
// Storage: Bounties Bounties (r:1 w:0)
// Storage: ChildBounties ChildBounties (r:1 w:1)
// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1)
// Storage: ChildBounties ParentChildBounties (r:1 w:1)
// Storage: System Account (r:2 w:2)
// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1)
fn close_child_bounty_added() -> Weight {
(101_000_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(6 as Weight))
.saturating_add(RocksDbWeight::get().writes(6 as Weight))
}
// Storage: Bounties Bounties (r:1 w:0)
// Storage: ChildBounties ChildBounties (r:1 w:1)
// Storage: System Account (r:3 w:3)
// Storage: ChildBounties ChildrenCuratorFees (r:1 w:1)
// Storage: ChildBounties ParentChildBounties (r:1 w:1)
// Storage: ChildBounties ChildBountyDescriptions (r:0 w:1)
fn close_child_bounty_active() -> Weight {
(130_000_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(7 as Weight))
.saturating_add(RocksDbWeight::get().writes(7 as Weight))
}
}