Improve Bounties and Child Bounties Deposit Logic (#11014)

* basic idea

* make tests better

* update bounties pallet to also have similar logic

* new test verifies logic for bounty pallet

* add test for new child logic

* better name

* make `node` compile with bounties changes

* * formatting
* use uniform notion of parent and child, no "master" or "general" entity
* README updated to match comments

* Revert "* formatting"

This reverts commit 1ab729e7c23b5db24a8e229d487bbc2ed81d38c3.

* update bounties logic to use bounds

* fix child

* bounties test for max

* update tests

* check min bound

* update node

* remove stale comment

* Update frame/bounties/src/lib.rs

Co-authored-by: Dan Shields <nukemandan@protonmail.com>
This commit is contained in:
Shawn Tabrizi
2022-03-25 09:42:54 -04:00
committed by GitHub
parent fa8fa8fa33
commit a95dbedee6
5 changed files with 460 additions and 133 deletions
+27 -5
View File
@@ -196,10 +196,20 @@ pub mod pallet {
#[pallet::constant]
type BountyUpdatePeriod: Get<Self::BlockNumber>;
/// Percentage of the curator fee that will be reserved upfront as deposit for bounty
/// curator.
/// The curator deposit is calculated as a percentage of the curator fee.
///
/// This deposit has optional upper and lower bounds with `CuratorDepositMax` and
/// `CuratorDepositMin`.
#[pallet::constant]
type BountyCuratorDeposit: Get<Permill>;
type CuratorDepositMultiplier: Get<Permill>;
/// Maximum amount of funds that should be placed in a deposit for making a proposal.
#[pallet::constant]
type CuratorDepositMax: Get<Option<BalanceOf<Self>>>;
/// Minimum amount of funds that should be placed in a deposit for making a proposal.
#[pallet::constant]
type CuratorDepositMin: Get<Option<BalanceOf<Self>>>;
/// Minimum value for a bounty.
#[pallet::constant]
@@ -502,7 +512,7 @@ pub mod pallet {
BountyStatus::CuratorProposed { ref curator } => {
ensure!(signer == *curator, Error::<T>::RequireCurator);
let deposit = T::BountyCuratorDeposit::get() * bounty.fee;
let deposit = Self::calculate_curator_deposit(&bounty.fee);
T::Currency::reserve(curator, deposit)?;
bounty.curator_deposit = deposit;
@@ -762,7 +772,19 @@ pub mod pallet {
}
impl<T: Config> Pallet<T> {
// Add public immutables and private mutables.
pub fn calculate_curator_deposit(fee: &BalanceOf<T>) -> BalanceOf<T> {
let mut deposit = T::CuratorDepositMultiplier::get() * *fee;
if let Some(max_deposit) = T::CuratorDepositMax::get() {
deposit = deposit.min(max_deposit)
}
if let Some(min_deposit) = T::CuratorDepositMin::get() {
deposit = deposit.max(min_deposit)
}
deposit
}
/// The account ID of the treasury pot.
///
+111 -23
View File
@@ -60,6 +60,8 @@ parameter_types! {
pub const AvailableBlockRatio: Perbill = Perbill::one();
}
type Balance = u64;
impl frame_system::Config for Test {
type BaseCallFilter = frame_support::traits::Everything;
type BlockWeights = ();
@@ -91,7 +93,7 @@ impl pallet_balances::Config for Test {
type MaxLocks = ();
type MaxReserves = ();
type ReserveIdentifier = [u8; 8];
type Balance = u64;
type Balance = Balance;
type Event = Event;
type DustRemoval = ();
type ExistentialDeposit = ConstU64<1>;
@@ -125,15 +127,23 @@ impl pallet_treasury::Config for Test {
type SpendFunds = Bounties;
type MaxApprovals = ConstU32<100>;
}
parameter_types! {
pub const BountyCuratorDeposit: Permill = Permill::from_percent(50);
// This will be 50% of the bounty fee.
pub const CuratorDepositMultiplier: Permill = Permill::from_percent(50);
pub const CuratorDepositMax: Balance = 1_000;
pub const CuratorDepositMin: Balance = 3;
}
impl Config for Test {
type Event = Event;
type BountyDepositBase = ConstU64<80>;
type BountyDepositPayoutDelay = ConstU64<3>;
type BountyUpdatePeriod = ConstU64<20>;
type BountyCuratorDeposit = BountyCuratorDeposit;
type CuratorDepositMultiplier = CuratorDepositMultiplier;
type CuratorDepositMax = CuratorDepositMax;
type CuratorDepositMin = CuratorDepositMin;
type BountyValueMinimum = ConstU64<1>;
type DataDepositPerByte = ConstU64<1>;
type MaximumReasonLength = ConstU32<16384>;
@@ -543,13 +553,14 @@ fn assign_curator_works() {
Error::<Test>::InvalidFee
);
assert_ok!(Bounties::propose_curator(Origin::root(), 0, 4, 4));
let fee = 4;
assert_ok!(Bounties::propose_curator(Origin::root(), 0, 4, fee));
assert_eq!(
Bounties::bounties(0).unwrap(),
Bounty {
proposer: 0,
fee: 4,
fee,
curator_deposit: 0,
value: 50,
bond: 85,
@@ -567,20 +578,22 @@ fn assign_curator_works() {
assert_ok!(Bounties::accept_curator(Origin::signed(4), 0));
let expected_deposit = Bounties::calculate_curator_deposit(&fee);
assert_eq!(
Bounties::bounties(0).unwrap(),
Bounty {
proposer: 0,
fee: 4,
curator_deposit: 2,
fee,
curator_deposit: expected_deposit,
value: 50,
bond: 85,
status: BountyStatus::Active { curator: 4, update_due: 22 },
}
);
assert_eq!(Balances::free_balance(&4), 8);
assert_eq!(Balances::reserved_balance(&4), 2);
assert_eq!(Balances::free_balance(&4), 10 - expected_deposit);
assert_eq!(Balances::reserved_balance(&4), expected_deposit);
});
}
@@ -596,17 +609,17 @@ fn unassign_curator_works() {
System::set_block_number(2);
<Treasury as OnInitialize<u64>>::on_initialize(2);
assert_ok!(Bounties::propose_curator(Origin::root(), 0, 4, 4));
let fee = 4;
assert_ok!(Bounties::propose_curator(Origin::root(), 0, 4, fee));
assert_noop!(Bounties::unassign_curator(Origin::signed(1), 0), BadOrigin);
assert_ok!(Bounties::unassign_curator(Origin::signed(4), 0));
assert_eq!(
Bounties::bounties(0).unwrap(),
Bounty {
proposer: 0,
fee: 4,
fee,
curator_deposit: 0,
value: 50,
bond: 85,
@@ -614,19 +627,17 @@ fn unassign_curator_works() {
}
);
assert_ok!(Bounties::propose_curator(Origin::root(), 0, 4, 4));
assert_ok!(Bounties::propose_curator(Origin::root(), 0, 4, fee));
Balances::make_free_balance_be(&4, 10);
assert_ok!(Bounties::accept_curator(Origin::signed(4), 0));
let expected_deposit = Bounties::calculate_curator_deposit(&fee);
assert_ok!(Bounties::unassign_curator(Origin::root(), 0));
assert_eq!(
Bounties::bounties(0).unwrap(),
Bounty {
proposer: 0,
fee: 4,
fee,
curator_deposit: 0,
value: 50,
bond: 85,
@@ -634,8 +645,8 @@ fn unassign_curator_works() {
}
);
assert_eq!(Balances::free_balance(&4), 8);
assert_eq!(Balances::reserved_balance(&4), 0); // slashed 2
assert_eq!(Balances::free_balance(&4), 10 - expected_deposit);
assert_eq!(Balances::reserved_balance(&4), 0); // slashed curator deposit
});
}
@@ -652,10 +663,12 @@ fn award_and_claim_bounty_works() {
System::set_block_number(2);
<Treasury as OnInitialize<u64>>::on_initialize(2);
assert_ok!(Bounties::propose_curator(Origin::root(), 0, 4, 4));
let fee = 4;
assert_ok!(Bounties::propose_curator(Origin::root(), 0, 4, fee));
assert_ok!(Bounties::accept_curator(Origin::signed(4), 0));
assert_eq!(Balances::free_balance(4), 8); // inital 10 - 2 deposit
let expected_deposit = Bounties::calculate_curator_deposit(&fee);
assert_eq!(Balances::free_balance(4), 10 - expected_deposit);
assert_noop!(
Bounties::award_bounty(Origin::signed(1), 0, 3),
@@ -668,8 +681,8 @@ fn award_and_claim_bounty_works() {
Bounties::bounties(0).unwrap(),
Bounty {
proposer: 0,
fee: 4,
curator_deposit: 2,
fee,
curator_deposit: expected_deposit,
value: 50,
bond: 85,
status: BountyStatus::PendingPayout { curator: 4, beneficiary: 3, unlock_at: 5 },
@@ -1034,3 +1047,78 @@ fn unassign_curator_self() {
assert_eq!(Balances::reserved_balance(1), 0); // not slashed
});
}
#[test]
fn accept_curator_handles_different_deposit_calculations() {
// This test will verify that a bounty with and without a fee results
// in a different curator deposit: one using the value, and one using the fee.
new_test_ext().execute_with(|| {
// Case 1: With a fee
let user = 1;
let bounty_index = 0;
let value = 88;
let fee = 42;
System::set_block_number(1);
Balances::make_free_balance_be(&Treasury::account_id(), 101);
Balances::make_free_balance_be(&user, 100);
assert_ok!(Bounties::propose_bounty(Origin::signed(0), value, b"12345".to_vec()));
assert_ok!(Bounties::approve_bounty(Origin::root(), bounty_index));
System::set_block_number(2);
<Treasury as OnInitialize<u64>>::on_initialize(2);
assert_ok!(Bounties::propose_curator(Origin::root(), bounty_index, user, fee));
assert_ok!(Bounties::accept_curator(Origin::signed(user), bounty_index));
let expected_deposit = CuratorDepositMultiplier::get() * fee;
assert_eq!(Balances::free_balance(&user), 100 - expected_deposit);
assert_eq!(Balances::reserved_balance(&user), expected_deposit);
// Case 2: Lower bound
let user = 2;
let bounty_index = 1;
let value = 35;
let fee = 0;
Balances::make_free_balance_be(&Treasury::account_id(), 101);
Balances::make_free_balance_be(&user, 100);
assert_ok!(Bounties::propose_bounty(Origin::signed(0), value, b"12345".to_vec()));
assert_ok!(Bounties::approve_bounty(Origin::root(), bounty_index));
System::set_block_number(3);
<Treasury as OnInitialize<u64>>::on_initialize(3);
assert_ok!(Bounties::propose_curator(Origin::root(), bounty_index, user, fee));
assert_ok!(Bounties::accept_curator(Origin::signed(user), bounty_index));
let expected_deposit = CuratorDepositMin::get();
assert_eq!(Balances::free_balance(&user), 100 - expected_deposit);
assert_eq!(Balances::reserved_balance(&user), expected_deposit);
// Case 3: Upper bound
let user = 3;
let bounty_index = 2;
let value = 1_000_000;
let fee = 50_000;
let starting_balance = fee * 2;
Balances::make_free_balance_be(&Treasury::account_id(), value * 2);
Balances::make_free_balance_be(&user, starting_balance);
Balances::make_free_balance_be(&0, starting_balance);
assert_ok!(Bounties::propose_bounty(Origin::signed(0), value, b"12345".to_vec()));
assert_ok!(Bounties::approve_bounty(Origin::root(), bounty_index));
System::set_block_number(3);
<Treasury as OnInitialize<u64>>::on_initialize(3);
assert_ok!(Bounties::propose_curator(Origin::root(), bounty_index, user, fee));
assert_ok!(Bounties::accept_curator(Origin::signed(user), bounty_index));
let expected_deposit = CuratorDepositMax::get();
assert_eq!(Balances::free_balance(&user), starting_balance - expected_deposit);
assert_eq!(Balances::reserved_balance(&user), expected_deposit);
});
}