Child bounties comments (#11053)

* * formatting
* use uniform notion of parent and child, no "master" or "general" entity
* README updated to match comments
* `parent_index` used over simply `index`

* rm `parent_*` change

* parent_bounty_id

* parent_index rm

* fmt

* Apply suggestions from code review
This commit is contained in:
Dan Shields
2022-04-16 02:55:37 -07:00
committed by GitHub
parent 362dc50940
commit c6e452108b
4 changed files with 90 additions and 62 deletions
+29 -17
View File
@@ -2,28 +2,38 @@
## Bounty ## Bounty
**Note :: This pallet is tightly coupled with pallet-treasury** > NOTE: This pallet is tightly coupled with pallet-treasury.
A Bounty Spending is a reward for a specified body of work - or specified set of objectives - that A Bounty Spending is a reward for a specified body of work - or specified set of objectives -
needs to be executed for a predefined Treasury amount to be paid out. A curator is assigned after that needs to be executed for a predefined Treasury amount to be paid out. A curator is assigned
the bounty is approved and funded by Council, to be delegated with the responsibility of assigning a after the bounty is approved and funded by Council, to be delegated with the responsibility of
payout address once the specified set of objectives is completed. assigning a payout address once the specified set of objectives is completed.
After the Council has activated a bounty, it delegates the work that requires expertise to a curator After the Council has activated a bounty, it delegates the work that requires expertise to a
in exchange of a deposit. Once the curator accepts the bounty, they get to close the active bounty. curator in exchange of a deposit. Once the curator accepts the bounty, they get to close the
Closing the active bounty enacts a delayed payout to the payout address, the curator fee and the active bounty. Closing the active bounty enacts a delayed payout to the payout address, the
return of the curator deposit. The delay allows for intervention through regular democracy. The curator fee and the return of the curator deposit. The delay allows for intervention through
Council gets to unassign the curator, resulting in a new curator election. The Council also gets to regular democracy. The Council gets to unassign the curator, resulting in a new curator
cancel the bounty if deemed necessary before assigning a curator or once the bounty is active or election. The Council also gets to cancel the bounty if deemed necessary before assigning a
payout is pending, resulting in the slash of the curator's deposit. curator or once the bounty is active or payout is pending, resulting in the slash of the
curator's deposit.
This pallet may opt into using a [`ChildBountyManager`] that enables bounties to be split into
sub-bounties, as children of anh established bounty (called the parent in the context of it's
children).
> NOTE: The parent bounty cannot be closed if it has a non-zero number of it has active child
> bounties associated with it.
### Terminology ### Terminology
- **Bounty spending proposal:** A proposal to reward a predefined body of work upon completion by Bounty:
the Treasury.
- **Bounty spending proposal:** A proposal to reward a predefined body of work upon completion
by the Treasury.
- **Proposer:** An account proposing a bounty spending. - **Proposer:** An account proposing a bounty spending.
- **Curator:** An account managing the bounty and assigning a payout address receiving the reward - **Curator:** An account managing the bounty and assigning a payout address receiving the
for the completion of work. reward for the completion of work.
- **Deposit:** The amount held on deposit for placing a bounty proposal plus the amount held on - **Deposit:** The amount held on deposit for placing a bounty proposal plus the amount held on
deposit per byte within the bounty description. deposit per byte within the bounty description.
- **Curator deposit:** The payment from a candidate willing to curate an approved bounty. The - **Curator deposit:** The payment from a candidate willing to curate an approved bounty. The
@@ -31,7 +41,8 @@ payout is pending, resulting in the slash of the curator's deposit.
- **Bounty value:** The total amount that should be paid to the Payout Address if the bounty is - **Bounty value:** The total amount that should be paid to the Payout Address if the bounty is
rewarded. rewarded.
- **Payout address:** The account to which the total or part of the bounty is assigned to. - **Payout address:** The account to which the total or part of the bounty is assigned to.
- **Payout Delay:** The delay period for which a bounty beneficiary needs to wait before claiming. - **Payout Delay:** The delay period for which a bounty beneficiary needs to wait before
claiming.
- **Curator fee:** The reserved upfront payment for a curator for work related to the bounty. - **Curator fee:** The reserved upfront payment for a curator for work related to the bounty.
## Interface ## Interface
@@ -39,6 +50,7 @@ payout is pending, resulting in the slash of the curator's deposit.
### Dispatchable Functions ### Dispatchable Functions
Bounty protocol: Bounty protocol:
- `propose_bounty` - Propose a specific treasury amount to be earmarked for a predefined set of - `propose_bounty` - Propose a specific treasury amount to be earmarked for a predefined set of
tasks and stake the required deposit. tasks and stake the required deposit.
- `approve_bounty` - Accept a specific treasury amount to be earmarked for a predefined body of - `approve_bounty` - Accept a specific treasury amount to be earmarked for a predefined body of
+16 -8
View File
@@ -35,10 +35,17 @@
//! curator or once the bounty is active or payout is pending, resulting in the slash of the //! curator or once the bounty is active or payout is pending, resulting in the slash of the
//! curator's deposit. //! curator's deposit.
//! //!
//! This pallet may opt into using a [`ChildBountyManager`] that enables bounties to be split into
//! sub-bounties, as children of anh established bounty (called the parent in the context of it's
//! children).
//!
//! > NOTE: The parent bounty cannot be closed if it has a non-zero number of it has active child
//! > bounties associated with it.
//! //!
//! ### Terminology //! ### Terminology
//! //!
//! Bounty: //! Bounty:
//!
//! - **Bounty spending proposal:** A proposal to reward a predefined body of work upon completion //! - **Bounty spending proposal:** A proposal to reward a predefined body of work upon completion
//! by the Treasury. //! by the Treasury.
//! - **Proposer:** An account proposing a bounty spending. //! - **Proposer:** An account proposing a bounty spending.
@@ -60,6 +67,7 @@
//! ### Dispatchable Functions //! ### Dispatchable Functions
//! //!
//! Bounty protocol: //! Bounty protocol:
//!
//! - `propose_bounty` - Propose a specific treasury amount to be earmarked for a predefined set of //! - `propose_bounty` - Propose a specific treasury amount to be earmarked for a predefined set of
//! tasks and stake the required deposit. //! tasks and stake the required deposit.
//! - `approve_bounty` - Accept a specific treasury amount to be earmarked for a predefined body of //! - `approve_bounty` - Accept a specific treasury amount to be earmarked for a predefined body of
@@ -165,9 +173,9 @@ pub enum BountyStatus<AccountId, BlockNumber> {
}, },
} }
/// The child-bounty manager. /// The child bounty manager.
pub trait ChildBountyManager<Balance> { pub trait ChildBountyManager<Balance> {
/// Get the active child-bounties for a parent bounty. /// Get the active child bounties for a parent bounty.
fn child_bounties_count(bounty_id: BountyIndex) -> BountyIndex; fn child_bounties_count(bounty_id: BountyIndex) -> BountyIndex;
/// Get total curator fees of children-bounty curators. /// Get total curator fees of children-bounty curators.
@@ -231,7 +239,7 @@ pub mod pallet {
/// Weight information for extrinsics in this pallet. /// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo; type WeightInfo: WeightInfo;
/// The child-bounty manager. /// The child bounty manager.
type ChildBountyManager: ChildBountyManager<BalanceOf<Self>>; type ChildBountyManager: ChildBountyManager<BalanceOf<Self>>;
} }
@@ -256,7 +264,7 @@ pub mod pallet {
PendingPayout, PendingPayout,
/// The bounties cannot be claimed/closed because it's still in the countdown period. /// The bounties cannot be claimed/closed because it's still in the countdown period.
Premature, Premature,
/// The bounty cannot be closed because it has active child-bounties. /// The bounty cannot be closed because it has active child bounties.
HasActiveChildBounty, HasActiveChildBounty,
/// Too many approvals are already queued. /// Too many approvals are already queued.
TooManyQueued, TooManyQueued,
@@ -552,7 +560,7 @@ pub mod pallet {
Bounties::<T>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { Bounties::<T>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult {
let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T>::InvalidIndex)?; let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T>::InvalidIndex)?;
// Ensure no active child-bounties before processing the call. // Ensure no active child bounties before processing the call.
ensure!( ensure!(
T::ChildBountyManager::child_bounties_count(bounty_id) == 0, T::ChildBountyManager::child_bounties_count(bounty_id) == 0,
Error::<T>::HasActiveChildBounty Error::<T>::HasActiveChildBounty
@@ -610,8 +618,8 @@ pub mod pallet {
let err_amount = T::Currency::unreserve(&curator, bounty.curator_deposit); let err_amount = T::Currency::unreserve(&curator, bounty.curator_deposit);
debug_assert!(err_amount.is_zero()); debug_assert!(err_amount.is_zero());
// Get total child-bounties curator fees, and subtract it from master curator // Get total child bounties curator fees, and subtract it from the parent
// fee. // curator fee (the fee in present referenced bounty, `self`).
let children_fee = T::ChildBountyManager::children_curator_fees(bounty_id); let children_fee = T::ChildBountyManager::children_curator_fees(bounty_id);
debug_assert!(children_fee <= fee); debug_assert!(children_fee <= fee);
@@ -663,7 +671,7 @@ pub mod pallet {
|maybe_bounty| -> DispatchResultWithPostInfo { |maybe_bounty| -> DispatchResultWithPostInfo {
let bounty = maybe_bounty.as_ref().ok_or(Error::<T>::InvalidIndex)?; let bounty = maybe_bounty.as_ref().ok_or(Error::<T>::InvalidIndex)?;
// Ensure no active child-bounties before processing the call. // Ensure no active child bounties before processing the call.
ensure!( ensure!(
T::ChildBountyManager::child_bounties_count(bounty_id) == 0, T::ChildBountyManager::child_bounties_count(bounty_id) == 0,
Error::<T>::HasActiveChildBounty Error::<T>::HasActiveChildBounty
+19 -11
View File
@@ -1,21 +1,29 @@
# Child Bounties Pallet (pallet-child-bounties) # Child Bounties Pallet ( `pallet-child-bounties` )
## Child Bounty ## Child Bounty
> NOTE: This pallet is tightly coupled with pallet-treasury and pallet-bounties. > 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. 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. 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 ## Interface
### Dispatchable Functions ### Dispatchable Functions
- `add_child_bounty` - Add a child-bounty for a parent-bounty to for dividing the work in smaller tasks. Child Bounty protocol:
- `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. - `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. - `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. - `claim_child_bounty` - Claim a specific child bounty amount from the payout address.
- `unassign_curator` - Unassign an accepted curator from a specific child-bounty. - `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. - `close_child_bounty` - Cancel the child bounty for a specific treasury amount
and close the bounty.
+26 -26
View File
@@ -15,16 +15,16 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
//! # Child Bounties Pallet ( pallet-child-bounties ) //! # Child Bounties Pallet ( `pallet-child-bounties` )
//! //!
//! ## Child Bounty //! ## Child Bounty
//! //!
//! > NOTE: This pallet is tightly coupled with pallet-treasury and pallet-bounties. //! > 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, //! 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. //! 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 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, //! 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 //! to be delegated with the responsibility of assigning a payout address once the specified
//! set of tasks is completed. //! set of tasks is completed.
//! //!
@@ -33,22 +33,22 @@
//! ### Dispatchable Functions //! ### Dispatchable Functions
//! //!
//! Child Bounty protocol: //! Child Bounty protocol:
//! - `add_child_bounty` - Add a child-bounty for a parent-bounty to for dividing the work in //! - `add_child_bounty` - Add a child bounty for a parent bounty to for dividing the work in
//! smaller tasks. //! smaller tasks.
//! - `propose_curator` - Assign an account to a child-bounty as candidate curator. //! - `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 //! - `accept_curator` - Accept a child bounty assignment from the parent bounty curator, setting a
//! curator deposit. //! curator deposit.
//! - `award_child_bounty` - Close and pay out the specified amount for the completed work. //! - `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. //! - `claim_child_bounty` - Claim a specific child bounty amount from the payout address.
//! - `unassign_curator` - Unassign an accepted curator from a specific child-bounty. //! - `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 //! - `close_child_bounty` - Cancel the child bounty for a specific treasury amount and close the
//! bounty. //! bounty.
// Most of the business logic in this pallet has been // Most of the business logic in this pallet has been
// originally contributed by "https://github.com/shamb0", // originally contributed by "https://github.com/shamb0",
// as part of the PR - https://github.com/paritytech/substrate/pull/7965. // as part of the PR - https://github.com/paritytech/substrate/pull/7965.
// The code has been moved here and then refactored in order to // The code has been moved here and then refactored in order to
// extract child-bounties as a separate pallet. // extract child bounties as a separate pallet.
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
@@ -101,7 +101,7 @@ pub struct ChildBounty<AccountId, Balance, BlockNumber> {
pub enum ChildBountyStatus<AccountId, BlockNumber> { pub enum ChildBountyStatus<AccountId, BlockNumber> {
/// The child-bounty is added and waiting for curator assignment. /// The child-bounty is added and waiting for curator assignment.
Added, Added,
/// A curator has been proposed by the parent-bounty curator. Waiting for /// A curator has been proposed by the parent bounty curator. Waiting for
/// acceptance from the child-bounty curator. /// acceptance from the child-bounty curator.
CuratorProposed { CuratorProposed {
/// The assigned child-bounty curator of this bounty. /// The assigned child-bounty curator of this bounty.
@@ -136,7 +136,7 @@ pub mod pallet {
pub trait Config: pub trait Config:
frame_system::Config + pallet_treasury::Config + pallet_bounties::Config frame_system::Config + pallet_treasury::Config + pallet_bounties::Config
{ {
/// Maximum number of child-bounties that can be added to a parent bounty. /// Maximum number of child bounties that can be added to a parent bounty.
#[pallet::constant] #[pallet::constant]
type MaxActiveChildBountyCount: Get<u32>; type MaxActiveChildBountyCount: Get<u32>;
@@ -157,7 +157,7 @@ pub mod pallet {
ParentBountyNotActive, ParentBountyNotActive,
/// The bounty balance is not enough to add new child-bounty. /// The bounty balance is not enough to add new child-bounty.
InsufficientBountyBalance, InsufficientBountyBalance,
/// Number of child-bounties exceeds limit `MaxActiveChildBountyCount`. /// Number of child bounties exceeds limit `MaxActiveChildBountyCount`.
TooManyChildBounties, TooManyChildBounties,
} }
@@ -184,14 +184,14 @@ pub mod pallet {
#[pallet::getter(fn child_bounty_count)] #[pallet::getter(fn child_bounty_count)]
pub type ChildBountyCount<T: Config> = StorageValue<_, BountyIndex, ValueQuery>; pub type ChildBountyCount<T: Config> = StorageValue<_, BountyIndex, ValueQuery>;
/// Number of child-bounties per parent bounty. /// Number of child bounties per parent bounty.
/// Map of parent bounty index to number of child bounties. /// Map of parent bounty index to number of child bounties.
#[pallet::storage] #[pallet::storage]
#[pallet::getter(fn parent_child_bounties)] #[pallet::getter(fn parent_child_bounties)]
pub type ParentChildBounties<T: Config> = pub type ParentChildBounties<T: Config> =
StorageMap<_, Twox64Concat, BountyIndex, u32, ValueQuery>; StorageMap<_, Twox64Concat, BountyIndex, u32, ValueQuery>;
/// Child-bounties that have been added. /// Child bounties that have been added.
#[pallet::storage] #[pallet::storage]
#[pallet::getter(fn child_bounties)] #[pallet::getter(fn child_bounties)]
pub type ChildBounties<T: Config> = StorageDoubleMap< pub type ChildBounties<T: Config> = StorageDoubleMap<
@@ -226,7 +226,7 @@ pub mod pallet {
/// parent bounty to child-bounty account, if parent bounty has enough /// parent bounty to child-bounty account, if parent bounty has enough
/// funds, else the call fails. /// funds, else the call fails.
/// ///
/// Upper bound to maximum number of active child-bounties that can be /// Upper bound to maximum number of active child bounties that can be
/// added are managed via runtime trait config /// added are managed via runtime trait config
/// [`Config::MaxActiveChildBountyCount`]. /// [`Config::MaxActiveChildBountyCount`].
/// ///
@@ -427,12 +427,12 @@ pub mod pallet {
/// the curator of the parent bounty, or any signed origin. /// the curator of the parent bounty, or any signed origin.
/// ///
/// For the origin other than T::RejectOrigin and the child-bounty /// For the origin other than T::RejectOrigin and the child-bounty
/// curator, parent-bounty must be in active state, for this call to /// curator, parent bounty must be in active state, for this call to
/// work. We allow child-bounty curator and T::RejectOrigin to execute /// work. We allow child-bounty curator and T::RejectOrigin to execute
/// this call irrespective of the parent-bounty state. /// this call irrespective of the parent bounty state.
/// ///
/// If this function is called by the `RejectOrigin` or the /// If this function is called by the `RejectOrigin` or the
/// parent-bounty curator, we assume that the child-bounty curator is /// parent bounty curator, we assume that the child-bounty curator is
/// malicious or inactive. As a result, child-bounty curator deposit is /// malicious or inactive. As a result, child-bounty curator deposit is
/// slashed. /// slashed.
/// ///
@@ -486,7 +486,7 @@ pub mod pallet {
}, },
ChildBountyStatus::CuratorProposed { ref curator } => { ChildBountyStatus::CuratorProposed { ref curator } => {
// A child-bounty curator has been proposed, but not accepted yet. // A child-bounty curator has been proposed, but not accepted yet.
// Either `RejectOrigin`, parent-bounty curator or the proposed // Either `RejectOrigin`, parent bounty curator or the proposed
// child-bounty curator can unassign the child-bounty curator. // child-bounty curator can unassign the child-bounty curator.
ensure!( ensure!(
maybe_sender.map_or(true, |sender| { maybe_sender.map_or(true, |sender| {
@@ -524,7 +524,7 @@ pub mod pallet {
update_due < frame_system::Pallet::<T>::block_number() update_due < frame_system::Pallet::<T>::block_number()
{ {
// Slash the child-bounty curator if // Slash the child-bounty curator if
// + the call is made by the parent-bounty curator. // + the call is made by the parent bounty curator.
// + or the curator is inactive. // + or the curator is inactive.
slash_curator(curator, &mut child_bounty.curator_deposit); slash_curator(curator, &mut child_bounty.curator_deposit);
// Continue to change bounty status below. // Continue to change bounty status below.
@@ -556,7 +556,7 @@ pub mod pallet {
/// ///
/// The beneficiary will be able to claim the funds after a delay. /// The beneficiary will be able to claim the funds after a delay.
/// ///
/// The dispatch origin for this call must be the master curator or /// The dispatch origin for this call must be the parent curator or
/// curator of this child-bounty. /// curator of this child-bounty.
/// ///
/// Parent bounty must be in active state, for this child-bounty call to /// Parent bounty must be in active state, for this child-bounty call to
@@ -835,7 +835,7 @@ impl<T: Config> Pallet<T> {
// Nothing extra to do besides the removal of the child-bounty below. // Nothing extra to do besides the removal of the child-bounty below.
}, },
ChildBountyStatus::Active { curator } => { ChildBountyStatus::Active { curator } => {
// Cancelled by master curator or RejectOrigin, // Cancelled by parent curator or RejectOrigin,
// refund deposit of the working child-bounty curator. // refund deposit of the working child-bounty curator.
let _ = T::Currency::unreserve(curator, child_bounty.curator_deposit); let _ = T::Currency::unreserve(curator, child_bounty.curator_deposit);
// Then execute removal of the child-bounty below. // Then execute removal of the child-bounty below.
@@ -850,7 +850,7 @@ impl<T: Config> Pallet<T> {
}, },
} }
// Revert the curator fee back to parent-bounty curator & // Revert the curator fee back to parent bounty curator &
// reduce the active child-bounty count. // reduce the active child-bounty count.
ChildrenCuratorFees::<T>::mutate(parent_bounty_id, |value| { ChildrenCuratorFees::<T>::mutate(parent_bounty_id, |value| {
*value = value.saturating_sub(child_bounty.fee) *value = value.saturating_sub(child_bounty.fee)
@@ -888,7 +888,7 @@ impl<T: Config> Pallet<T> {
} }
// Implement ChildBountyManager to connect with the bounties pallet. This is // Implement ChildBountyManager to connect with the bounties pallet. This is
// where we pass the active child-bounties and child curator fees to the parent // where we pass the active child bounties and child curator fees to the parent
// bounty. // bounty.
impl<T: Config> pallet_bounties::ChildBountyManager<BalanceOf<T>> for Pallet<T> { impl<T: Config> pallet_bounties::ChildBountyManager<BalanceOf<T>> for Pallet<T> {
fn child_bounties_count( fn child_bounties_count(