Skip to content

add poke_deposit extrinsic to pallet-bounties #8382

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
76 changes: 46 additions & 30 deletions polkadot/runtime/rococo/src/weights/pallet_bounties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
//! Autogenerated weights for `pallet_bounties`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
//! DATE: 2025-02-22, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! DATE: 2025-04-30, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `d3a9aad6f7a3`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
//! HOSTNAME: `b5f9d80cc353`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: 1024

// Executed Command:
Expand Down Expand Up @@ -64,11 +64,11 @@ impl<T: frame_system::Config> pallet_bounties::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `210`
// Estimated: `3593`
// Minimum execution time: 27_405_000 picoseconds.
Weight::from_parts(28_960_668, 0)
// Minimum execution time: 26_840_000 picoseconds.
Weight::from_parts(28_494_084, 0)
.saturating_add(Weight::from_parts(0, 3593))
// Standard Error: 6
.saturating_add(Weight::from_parts(607, 0).saturating_mul(d.into()))
// Standard Error: 5
.saturating_add(Weight::from_parts(644, 0).saturating_mul(d.into()))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(4))
}
Expand All @@ -80,8 +80,8 @@ impl<T: frame_system::Config> pallet_bounties::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `302`
// Estimated: `3642`
// Minimum execution time: 14_322_000 picoseconds.
Weight::from_parts(14_993_000, 0)
// Minimum execution time: 14_096_000 picoseconds.
Weight::from_parts(14_743_000, 0)
.saturating_add(Weight::from_parts(0, 3642))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
Expand All @@ -92,8 +92,8 @@ impl<T: frame_system::Config> pallet_bounties::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `322`
// Estimated: `3642`
// Minimum execution time: 14_227_000 picoseconds.
Weight::from_parts(14_967_000, 0)
// Minimum execution time: 13_949_000 picoseconds.
Weight::from_parts(14_522_000, 0)
.saturating_add(Weight::from_parts(0, 3642))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
Expand All @@ -106,8 +106,8 @@ impl<T: frame_system::Config> pallet_bounties::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `302`
// Estimated: `3642`
// Minimum execution time: 16_980_000 picoseconds.
Weight::from_parts(18_167_000, 0)
// Minimum execution time: 17_266_000 picoseconds.
Weight::from_parts(17_968_000, 0)
.saturating_add(Weight::from_parts(0, 3642))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
Expand All @@ -120,8 +120,8 @@ impl<T: frame_system::Config> pallet_bounties::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `498`
// Estimated: `3642`
// Minimum execution time: 41_196_000 picoseconds.
Weight::from_parts(42_588_000, 0)
// Minimum execution time: 41_346_000 picoseconds.
Weight::from_parts(42_379_000, 0)
.saturating_add(Weight::from_parts(0, 3642))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
Expand All @@ -134,8 +134,8 @@ impl<T: frame_system::Config> pallet_bounties::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `494`
// Estimated: `3642`
// Minimum execution time: 31_821_000 picoseconds.
Weight::from_parts(32_823_000, 0)
// Minimum execution time: 31_182_000 picoseconds.
Weight::from_parts(32_520_000, 0)
.saturating_add(Weight::from_parts(0, 3642))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(2))
Expand All @@ -148,8 +148,8 @@ impl<T: frame_system::Config> pallet_bounties::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `400`
// Estimated: `3642`
// Minimum execution time: 18_795_000 picoseconds.
Weight::from_parts(19_758_000, 0)
// Minimum execution time: 17_660_000 picoseconds.
Weight::from_parts(18_922_000, 0)
.saturating_add(Weight::from_parts(0, 3642))
.saturating_add(T::DbWeight::get().reads(2))
.saturating_add(T::DbWeight::get().writes(1))
Expand All @@ -170,8 +170,8 @@ impl<T: frame_system::Config> pallet_bounties::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `764`
// Estimated: `8799`
// Minimum execution time: 116_017_000 picoseconds.
Weight::from_parts(117_965_000, 0)
// Minimum execution time: 114_245_000 picoseconds.
Weight::from_parts(119_907_000, 0)
.saturating_add(Weight::from_parts(0, 8799))
.saturating_add(T::DbWeight::get().reads(5))
.saturating_add(T::DbWeight::get().writes(8))
Expand All @@ -188,8 +188,8 @@ impl<T: frame_system::Config> pallet_bounties::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `444`
// Estimated: `3642`
// Minimum execution time: 42_519_000 picoseconds.
Weight::from_parts(44_526_000, 0)
// Minimum execution time: 43_088_000 picoseconds.
Weight::from_parts(44_290_000, 0)
.saturating_add(Weight::from_parts(0, 3642))
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(3))
Expand All @@ -208,8 +208,8 @@ impl<T: frame_system::Config> pallet_bounties::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `680`
// Estimated: `6196`
// Minimum execution time: 80_800_000 picoseconds.
Weight::from_parts(82_838_000, 0)
// Minimum execution time: 81_575_000 picoseconds.
Weight::from_parts(82_919_000, 0)
.saturating_add(Weight::from_parts(0, 6196))
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(6))
Expand All @@ -220,8 +220,8 @@ impl<T: frame_system::Config> pallet_bounties::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `358`
// Estimated: `3642`
// Minimum execution time: 14_685_000 picoseconds.
Weight::from_parts(15_756_000, 0)
// Minimum execution time: 14_680_000 picoseconds.
Weight::from_parts(15_096_000, 0)
.saturating_add(Weight::from_parts(0, 3642))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().writes(1))
Expand All @@ -237,15 +237,31 @@ impl<T: frame_system::Config> pallet_bounties::WeightInfo for WeightInfo<T> {
// Proof Size summary in bytes:
// Measured: `0 + b * (297 ±0)`
// Estimated: `1887 + b * (5206 ±0)`
// Minimum execution time: 3_160_000 picoseconds.
Weight::from_parts(3_311_000, 0)
// Minimum execution time: 3_023_000 picoseconds.
Weight::from_parts(3_260_000, 0)
.saturating_add(Weight::from_parts(0, 1887))
// Standard Error: 13_041
.saturating_add(Weight::from_parts(37_231_411, 0).saturating_mul(b.into()))
// Standard Error: 8_928
.saturating_add(Weight::from_parts(36_833_581, 0).saturating_mul(b.into()))
.saturating_add(T::DbWeight::get().reads(1))
.saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(b.into())))
.saturating_add(T::DbWeight::get().writes(1))
.saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(b.into())))
.saturating_add(Weight::from_parts(0, 5206).saturating_mul(b.into()))
}
/// Storage: `Bounties::Bounties` (r:1 w:1)
/// Proof: `Bounties::Bounties` (`max_values`: None, `max_size`: Some(177), added: 2652, mode: `MaxEncodedLen`)
/// Storage: `Bounties::BountyDescriptions` (r:1 w:0)
/// Proof: `Bounties::BountyDescriptions` (`max_values`: None, `max_size`: Some(16400), added: 18875, mode: `MaxEncodedLen`)
/// Storage: `System::Account` (r:1 w:1)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
fn poke_deposit() -> Weight {
// Proof Size summary in bytes:
// Measured: `16795`
// Estimated: `19865`
// Minimum execution time: 38_100_000 picoseconds.
Weight::from_parts(39_566_000, 0)
.saturating_add(Weight::from_parts(0, 19865))
.saturating_add(T::DbWeight::get().reads(3))
.saturating_add(T::DbWeight::get().writes(2))
}
}
9 changes: 9 additions & 0 deletions prdoc/pr_8382.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
title: add poke_deposit extrinsic to pallet-bounties
doc:
- audience: Runtime Dev
description: This PR adds a new extrinsic `poke_deposit` to `pallet-bounties`. This extrinsic will be used to re-adjust the deposits made in the pallet to create a new bounty.
crates:
- name: rococo-runtime
bump: major
- name: pallet-bounties
bump: major
24 changes: 24 additions & 0 deletions substrate/frame/bounties/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,5 +271,29 @@ benchmarks_instance_pallet! {
}
}

poke_deposit {
// Create a bounty
let (caller, _, _, value, reason) = setup_bounty::<T, I>(0, 5); // 5 bytes description
Bounties::<T, I>::propose_bounty(RawOrigin::Signed(caller.clone()).into(), value, reason)?;
let bounty_id = BountyCount::<T, I>::get() - 1;
let old_deposit = T::Currency::reserved_balance(&caller);
// Modify the description to be maximum length
let max_description: Vec<u8> = vec![0; T::MaximumReasonLength::get() as usize];
let bounded_description: BoundedVec<u8, T::MaximumReasonLength> = max_description.try_into().unwrap();
BountyDescriptions::<T, I>::insert(bounty_id, &bounded_description);

// Ensure caller has enough balance for new deposit
let new_deposit = Bounties::<T, I>::calculate_bounty_deposit(&bounded_description);
let required_balance = new_deposit.saturating_add(minimum_balance::<T, I>());
T::Currency::make_free_balance_be(&caller, required_balance);

}: _(RawOrigin::Signed(caller.clone()), bounty_id)
verify {
let bounty = crate::Bounties::<T, I>::get(bounty_id).unwrap();
assert_eq!(bounty.bond, new_deposit);
assert_eq!(T::Currency::reserved_balance(&caller), new_deposit);
assert_last_event::<T, I>(Event::DepositPoked { bounty_id, proposer: caller, old_deposit: old_deposit, new_deposit: new_deposit }.into());
}

impl_benchmark_test_suite!(Bounties, crate::tests::ExtBuilder::default().build(), crate::tests::Test)
}
89 changes: 87 additions & 2 deletions substrate/frame/bounties/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,8 @@ pub mod pallet {
HasActiveChildBounty,
/// Too many approvals are already queued.
TooManyQueued,
/// User is not the proposer of the bounty.
NotProposer,
}

#[pallet::event]
Expand Down Expand Up @@ -326,6 +328,13 @@ pub mod pallet {
CuratorUnassigned { bounty_id: BountyIndex },
/// A bounty curator is accepted.
CuratorAccepted { bounty_id: BountyIndex, curator: T::AccountId },
/// A bounty deposit has been poked.
DepositPoked {
bounty_id: BountyIndex,
proposer: T::AccountId,
old_deposit: BalanceOf<T, I>,
new_deposit: BalanceOf<T, I>,
},
}

/// Number of bounty proposals that have been made.
Expand Down Expand Up @@ -885,6 +894,34 @@ pub mod pallet {

Ok(())
}

/// Poke the deposit reserved for creating a bounty proposal.
///
/// This can be used by accounts to update their reserved amount.
///
/// The dispatch origin for this call must be _Signed_.
///
/// Parameters:
/// - `bounty_id`: The bounty id for which to adjust the deposit.
///
/// If the deposit is updated, the difference will be reserved/unreserved from the
/// proposer's account.
///
/// The transaction is made free if the deposit is updated and paid otherwise.
///
/// Emits `DepositPoked` if the deposit is updated.
#[pallet::call_index(10)]
#[pallet::weight(<T as Config<I>>::WeightInfo::poke_deposit())]
pub fn poke_deposit(
origin: OriginFor<T>,
#[pallet::compact] bounty_id: BountyIndex,
) -> DispatchResultWithPostInfo {
ensure_signed(origin)?;

let deposit_updated = Self::poke_bounty_deposit(bounty_id)?;

Ok(if deposit_updated { Pays::No } else { Pays::Yes }.into())
}
}

#[pallet::hooks]
Expand Down Expand Up @@ -986,8 +1023,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
let index = BountyCount::<T, I>::get();

// reserve deposit for new bounty
let bond = T::BountyDepositBase::get() +
T::DataDepositPerByte::get() * (bounded_description.len() as u32).into();
let bond = Self::calculate_bounty_deposit(&bounded_description);
T::Currency::reserve(&proposer, bond)
.map_err(|_| Error::<T, I>::InsufficientProposersBalance)?;

Expand All @@ -1009,6 +1045,55 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {

Ok(())
}

/// Helper function to calculate the bounty storage deposit.
fn calculate_bounty_deposit(
description: &BoundedVec<u8, T::MaximumReasonLength>,
) -> BalanceOf<T, I> {
T::BountyDepositBase::get() +
T::DataDepositPerByte::get() * (description.len() as u32).into()
}

/// Helper function to poke the deposit reserved for proposing a bounty.
///
/// Returns true if the deposit was updated and false otherwise.
fn poke_bounty_deposit(bounty_id: BountyIndex) -> Result<bool, DispatchError> {
let mut bounty = Bounties::<T, I>::get(bounty_id).ok_or(Error::<T, I>::InvalidIndex)?;
let bounty_description =
BountyDescriptions::<T, I>::get(bounty_id).ok_or(Error::<T, I>::InvalidIndex)?;
// ensure that the bounty status is proposed.
ensure!(bounty.status == BountyStatus::Proposed, Error::<T, I>::UnexpectedStatus);

let new_bond = Self::calculate_bounty_deposit(&bounty_description);
let old_bond = bounty.bond;
if new_bond == old_bond {
return Ok(false);
}
if new_bond > old_bond {
let extra = new_bond.saturating_sub(old_bond);
T::Currency::reserve(&bounty.proposer, extra)?;
} else {
let excess = old_bond.saturating_sub(new_bond);
let remaining_unreserved = T::Currency::unreserve(&bounty.proposer, excess);
if !remaining_unreserved.is_zero() {
defensive!(
"Failed to unreserve full amount. (Requested, Actual)",
(excess, excess.saturating_sub(remaining_unreserved))
);
}
}
bounty.bond = new_bond;
Bounties::<T, I>::insert(bounty_id, &bounty);

Self::deposit_event(Event::<T, I>::DepositPoked {
bounty_id,
proposer: bounty.proposer,
old_deposit: old_bond,
new_deposit: new_bond,
});

Ok(true)
}
}

impl<T: Config<I>, I: 'static> pallet_treasury::SpendFunds<T, I> for Pallet<T, I> {
Expand Down
Loading
Loading