Update treasury weights. (#5723)

* treasury weight formula

* use max tippers count

* doc

* Fix upper bound

* rounding a bit

* remove unused + doc

* as u64 -> as Weight

* 2 significative digits rounded up

* rename ContainsCountUpperBound -> ContainsLengthBound

* add doc

* sender account -> origin account

* fix
This commit is contained in:
thiolliere
2020-04-28 17:55:56 +02:00
committed by GitHub
parent 2d73ccd65b
commit 8b69609397
4 changed files with 101 additions and 46 deletions
@@ -93,6 +93,7 @@ use frame_support::{
traits::{ traits::{
Currency, Get, LockableCurrency, LockIdentifier, ReservableCurrency, WithdrawReasons, Currency, Get, LockableCurrency, LockIdentifier, ReservableCurrency, WithdrawReasons,
ChangeMembers, OnUnbalanced, WithdrawReason, Contains, BalanceStatus, InitializeMembers, ChangeMembers, OnUnbalanced, WithdrawReason, Contains, BalanceStatus, InitializeMembers,
ContainsLengthBound,
} }
}; };
use sp_phragmen::{build_support_map, ExtendedBalance, VoteWeight, PhragmenResult}; use sp_phragmen::{build_support_map, ExtendedBalance, VoteWeight, PhragmenResult};
@@ -880,6 +881,15 @@ impl<T: Trait> Contains<T::AccountId> for Module<T> {
} }
} }
impl<T: Trait> ContainsLengthBound for Module<T> {
fn min_len() -> usize { 0 }
/// Implementation uses a parameter type so calling is cost-free.
fn max_len() -> usize {
Self::desired_members() as usize
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
+9 -3
View File
@@ -190,9 +190,7 @@ impl<T: Default> Get<T> for () {
fn get() -> T { T::default() } fn get() -> T { T::default() }
} }
/// A trait for querying whether a type can be said to statically "contain" a value. Similar /// A trait for querying whether a type can be said to "contain" a value.
/// in nature to `Get`, except it is designed to be lazy rather than active (you can't ask it to
/// enumerate all values that it contains) and work for multiple values rather than just one.
pub trait Contains<T: Ord> { pub trait Contains<T: Ord> {
/// Return `true` if this "contains" the given value `t`. /// Return `true` if this "contains" the given value `t`.
fn contains(t: &T) -> bool { Self::sorted_members().binary_search(t).is_ok() } fn contains(t: &T) -> bool { Self::sorted_members().binary_search(t).is_ok() }
@@ -211,6 +209,14 @@ pub trait Contains<T: Ord> {
fn add(_t: &T) { unimplemented!() } fn add(_t: &T) { unimplemented!() }
} }
/// A trait for querying bound for the length of an implementation of `Contains`
pub trait ContainsLengthBound {
/// Minimum number of elements contained
fn min_len() -> usize;
/// Maximum number of elements contained
fn max_len() -> usize;
}
/// Determiner to say whether a given account is unused. /// Determiner to say whether a given account is unused.
pub trait IsDeadAccount<AccountId> { pub trait IsDeadAccount<AccountId> {
/// Is the given account dead? /// Is the given account dead?
+76 -43
View File
@@ -99,7 +99,7 @@ use sp_runtime::{Permill, ModuleId, Percent, RuntimeDebug, traits::{
Zero, StaticLookup, AccountIdConversion, Saturating, Hash, BadOrigin Zero, StaticLookup, AccountIdConversion, Saturating, Hash, BadOrigin
}}; }};
use frame_support::weights::{Weight, DispatchClass}; use frame_support::weights::{Weight, DispatchClass};
use frame_support::traits::{Contains, EnsureOrigin}; use frame_support::traits::{Contains, ContainsLengthBound, EnsureOrigin};
use codec::{Encode, Decode}; use codec::{Encode, Decode};
use frame_system::{self as system, ensure_signed, ensure_root}; use frame_system::{self as system, ensure_signed, ensure_root};
@@ -124,7 +124,9 @@ pub trait Trait: frame_system::Trait {
type RejectOrigin: EnsureOrigin<Self::Origin>; type RejectOrigin: EnsureOrigin<Self::Origin>;
/// Origin from which tippers must come. /// Origin from which tippers must come.
type Tippers: Contains<Self::AccountId>; ///
/// `ContainsLengthBound::max_len` must be cost free (i.e. no storage read or heavy operation).
type Tippers: Contains<Self::AccountId> + ContainsLengthBound;
/// The period for which a tip remains open after is has achieved threshold tippers. /// The period for which a tip remains open after is has achieved threshold tippers.
type TipCountdown: Get<Self::BlockNumber>; type TipCountdown: Get<Self::BlockNumber>;
@@ -326,11 +328,11 @@ decl_module! {
/// proposal is awarded. /// proposal is awarded.
/// ///
/// # <weight> /// # <weight>
/// - O(1). /// - Complexity: O(1)
/// - Limited storage reads. /// - DbReads: `ProposalCount`, `origin account`
/// - One DB change, one extra DB entry. /// - DbWrites: `ProposalCount`, `Proposals`, `origin account`
/// # </weight> /// # </weight>
#[weight = 500_000_000] #[weight = 120_000_000 + T::DbWeight::get().reads_writes(1, 2)]
fn propose_spend( fn propose_spend(
origin, origin,
#[compact] value: BalanceOf<T>, #[compact] value: BalanceOf<T>,
@@ -353,11 +355,11 @@ decl_module! {
/// Reject a proposed spend. The original deposit will be slashed. /// Reject a proposed spend. The original deposit will be slashed.
/// ///
/// # <weight> /// # <weight>
/// - O(1). /// - Complexity: O(1)
/// - Limited storage reads. /// - DbReads: `Proposals`, `rejected proposer account`
/// - One DB clear. /// - DbWrites: `Proposals`, `rejected proposer account`
/// # </weight> /// # </weight>
#[weight = (100_000_000, DispatchClass::Operational)] #[weight = (130_000_000 + T::DbWeight::get().reads_writes(2, 2), DispatchClass::Operational)]
fn reject_proposal(origin, #[compact] proposal_id: ProposalIndex) { fn reject_proposal(origin, #[compact] proposal_id: ProposalIndex) {
T::RejectOrigin::try_origin(origin) T::RejectOrigin::try_origin(origin)
.map(|_| ()) .map(|_| ())
@@ -375,11 +377,11 @@ decl_module! {
/// and the original deposit will be returned. /// and the original deposit will be returned.
/// ///
/// # <weight> /// # <weight>
/// - O(1). /// - Complexity: O(1).
/// - Limited storage reads. /// - DbReads: `Proposals`, `Approvals`
/// - One DB change. /// - DbWrite: `Approvals`
/// # </weight> /// # </weight>
#[weight = (100_000_000, DispatchClass::Operational)] #[weight = (34_000_000 + T::DbWeight::get().reads_writes(2, 1), DispatchClass::Operational)]
fn approve_proposal(origin, #[compact] proposal_id: ProposalIndex) { fn approve_proposal(origin, #[compact] proposal_id: ProposalIndex) {
T::ApproveOrigin::try_origin(origin) T::ApproveOrigin::try_origin(origin)
.map(|_| ()) .map(|_| ())
@@ -403,12 +405,12 @@ decl_module! {
/// Emits `NewTip` if successful. /// Emits `NewTip` if successful.
/// ///
/// # <weight> /// # <weight>
/// - `O(R)` where `R` length of `reason`. /// - Complexity: `O(R)` where `R` length of `reason`.
/// - One balance operation. /// - encoding and hashing of 'reason'
/// - One storage mutation (codec `O(R)`). /// - DbReads: `Reasons`, `Tips`, `who account data`
/// - One event. /// - DbWrites: `Tips`, `who account data`
/// # </weight> /// # </weight>
#[weight = 100_000_000] #[weight = 140_000_000 + 4_000 * reason.len() as Weight + T::DbWeight::get().reads_writes(3, 2)]
fn report_awesome(origin, reason: Vec<u8>, who: T::AccountId) { fn report_awesome(origin, reason: Vec<u8>, who: T::AccountId) {
let finder = ensure_signed(origin)?; let finder = ensure_signed(origin)?;
@@ -445,12 +447,12 @@ decl_module! {
/// Emits `TipRetracted` if successful. /// Emits `TipRetracted` if successful.
/// ///
/// # <weight> /// # <weight>
/// - `O(T)` /// - Complexity: `O(1)`
/// - One balance operation. /// - Depends on the length of `T::Hash` which is fixed.
/// - Two storage removals (one read, codec `O(T)`). /// - DbReads: `Tips`, `origin account`
/// - One event. /// - DbWrites: `Reasons`, `Tips`, `origin account`
/// # </weight> /// # </weight>
#[weight = 50_000_000] #[weight = 120_000_000 + T::DbWeight::get().reads_writes(1, 2)]
fn retract_tip(origin, hash: T::Hash) { fn retract_tip(origin, hash: T::Hash) {
let who = ensure_signed(origin)?; let who = ensure_signed(origin)?;
let tip = Tips::<T>::get(&hash).ok_or(Error::<T>::UnknownTip)?; let tip = Tips::<T>::get(&hash).ok_or(Error::<T>::UnknownTip)?;
@@ -477,12 +479,18 @@ decl_module! {
/// Emits `NewTip` if successful. /// Emits `NewTip` if successful.
/// ///
/// # <weight> /// # <weight>
/// - `O(R + T)` where `R` length of `reason`, `T` is the number of tippers. `T` is /// - Complexity: `O(R + T)` where `R` length of `reason`, `T` is the number of tippers.
/// naturally capped as a membership set, `R` is limited through transaction-size. /// - `O(T)`: decoding `Tipper` vec of length `T`
/// - Two storage insertions (codecs `O(R)`, `O(T)`), one read `O(1)`. /// `T` is charged as upper bound given by `ContainsLengthBound`.
/// - One event. /// The actual cost depends on the implementation of `T::Tippers`.
/// - `O(R)`: hashing and encoding of reason of length `R`
/// - DbReads: `Tippers`, `Reasons`
/// - DbWrites: `Reasons`, `Tips`
/// # </weight> /// # </weight>
#[weight = 150_000_000] #[weight = 110_000_000
+ 4_000 * reason.len() as Weight
+ 480_000 * T::Tippers::max_len() as Weight
+ T::DbWeight::get().reads_writes(2, 2)]
fn tip_new(origin, reason: Vec<u8>, who: T::AccountId, tip_value: BalanceOf<T>) { fn tip_new(origin, reason: Vec<u8>, who: T::AccountId, tip_value: BalanceOf<T>) {
let tipper = ensure_signed(origin)?; let tipper = ensure_signed(origin)?;
ensure!(T::Tippers::contains(&tipper), BadOrigin); ensure!(T::Tippers::contains(&tipper), BadOrigin);
@@ -512,11 +520,18 @@ decl_module! {
/// has started. /// has started.
/// ///
/// # <weight> /// # <weight>
/// - `O(T)` /// - Complexity: `O(T)` where `T` is the number of tippers.
/// - One storage mutation (codec `O(T)`), one storage read `O(1)`. /// decoding `Tipper` vec of length `T`, insert tip and check closing,
/// - Up to one event. /// `T` is charged as upper bound given by `ContainsLengthBound`.
/// The actual cost depends on the implementation of `T::Tippers`.
///
/// Actually weight could be lower as it depends on how many tips are in `OpenTip` but it
/// is weighted as if almost full i.e of length `T-1`.
/// - DbReads: `Tippers`, `Tips`
/// - DbWrites: `Tips`
/// # </weight> /// # </weight>
#[weight = 50_000_000] #[weight = 68_000_000 + 2_000_000 * T::Tippers::max_len() as Weight
+ T::DbWeight::get().reads_writes(2, 1)]
fn tip(origin, hash: T::Hash, tip_value: BalanceOf<T>) { fn tip(origin, hash: T::Hash, tip_value: BalanceOf<T>) {
let tipper = ensure_signed(origin)?; let tipper = ensure_signed(origin)?;
ensure!(T::Tippers::contains(&tipper), BadOrigin); ensure!(T::Tippers::contains(&tipper), BadOrigin);
@@ -538,11 +553,15 @@ decl_module! {
/// as the hash of the tuple of the original tip `reason` and the beneficiary account ID. /// as the hash of the tuple of the original tip `reason` and the beneficiary account ID.
/// ///
/// # <weight> /// # <weight>
/// - `O(T)` /// - Complexity: `O(T)` where `T` is the number of tippers.
/// - One storage retrieval (codec `O(T)`) and two removals. /// decoding `Tipper` vec of length `T`.
/// - Up to three balance operations. /// `T` is charged as upper bound given by `ContainsLengthBound`.
/// The actual cost depends on the implementation of `T::Tippers`.
/// - DbReads: `Tips`, `Tippers`, `tip finder`
/// - DbWrites: `Reasons`, `Tips`, `Tippers`, `tip finder`
/// # </weight> /// # </weight>
#[weight = 50_000_000] #[weight = 220_000_000 + 1_100_000 * T::Tippers::max_len() as Weight
+ T::DbWeight::get().reads_writes(3, 3)]
fn close_tip(origin, hash: T::Hash) { fn close_tip(origin, hash: T::Hash) {
ensure_signed(origin)?; ensure_signed(origin)?;
@@ -555,13 +574,23 @@ decl_module! {
Self::payout_tip(hash, tip); Self::payout_tip(hash, tip);
} }
/// # <weight>
/// - Complexity: `O(A)` where `A` is the number of approvals
/// - Db reads and writes: `Approvals`, `pot account data`
/// - Db reads and writes per approval:
/// `Proposals`, `proposer account data`, `beneficiary account data`
/// - The weight is overestimated if some approvals got missed.
/// # </weight>
fn on_initialize(n: T::BlockNumber) -> Weight { fn on_initialize(n: T::BlockNumber) -> Weight {
// Check to see if we should spend some funds! // Check to see if we should spend some funds!
if (n % T::SpendPeriod::get()).is_zero() { if (n % T::SpendPeriod::get()).is_zero() {
Self::spend_funds(); let approvals_len = Self::spend_funds();
}
0 270_000_000 * approvals_len
+ T::DbWeight::get().reads_writes(2 + approvals_len * 3, 2 + approvals_len * 3)
} else {
0
}
} }
} }
} }
@@ -653,14 +682,15 @@ impl<T: Trait> Module<T> {
Self::deposit_event(RawEvent::TipClosed(hash, tip.who, payout)); Self::deposit_event(RawEvent::TipClosed(hash, tip.who, payout));
} }
// Spend some money! /// Spend some money! returns number of approvals before spend.
fn spend_funds() { fn spend_funds() -> u64 {
let mut budget_remaining = Self::pot(); let mut budget_remaining = Self::pot();
Self::deposit_event(RawEvent::Spending(budget_remaining)); Self::deposit_event(RawEvent::Spending(budget_remaining));
let mut missed_any = false; let mut missed_any = false;
let mut imbalance = <PositiveImbalanceOf<T>>::zero(); let mut imbalance = <PositiveImbalanceOf<T>>::zero();
Approvals::mutate(|v| { let prior_approvals_len = Approvals::mutate(|v| {
let prior_approvals_len = v.len() as u64;
v.retain(|&index| { v.retain(|&index| {
// Should always be true, but shouldn't panic if false or we're screwed. // Should always be true, but shouldn't panic if false or we're screwed.
if let Some(p) = Self::proposals(index) { if let Some(p) = Self::proposals(index) {
@@ -684,6 +714,7 @@ impl<T: Trait> Module<T> {
false false
} }
}); });
prior_approvals_len
}); });
if !missed_any { if !missed_any {
@@ -710,6 +741,8 @@ impl<T: Trait> Module<T> {
} }
Self::deposit_event(RawEvent::Rollover(budget_remaining)); Self::deposit_event(RawEvent::Rollover(budget_remaining));
prior_approvals_len
} }
/// Return the amount of money in the pot. /// Return the amount of money in the pot.
+6
View File
@@ -111,6 +111,12 @@ impl Contains<u64> for TenToFourteen {
}) })
} }
} }
impl ContainsLengthBound for TenToFourteen {
fn max_len() -> usize {
TEN_TO_FOURTEEN.with(|v| v.borrow().len())
}
fn min_len() -> usize { 0 }
}
parameter_types! { parameter_types! {
pub const ProposalBond: Permill = Permill::from_percent(5); pub const ProposalBond: Permill = Permill::from_percent(5);
pub const ProposalBondMinimum: u64 = 1; pub const ProposalBondMinimum: u64 = 1;