diff --git a/substrate/srml/balances/src/lib.rs b/substrate/srml/balances/src/lib.rs index e205fa0aaa..c8f5c33d06 100644 --- a/substrate/srml/balances/src/lib.rs +++ b/substrate/srml/balances/src/lib.rs @@ -346,6 +346,21 @@ decl_module! { /// of the transfer, the account will be reaped. /// /// The dispatch origin for this call must be `Signed` by the transactor. + /// + /// # + /// - Dependent on arguments but not critical, given proper implementations for + /// input config types. See related functions below. + /// - It contains a limited number of reads and writes internally and no complex computation. + /// + /// Related functions: + /// + /// - `ensure_can_withdraw` is always called internally but has a bounded complexity. + /// - Transferring balances to accounts that did not exist before will cause + /// `T::OnNewAccount::on_new_account` to be called. + /// - Removing enough funds from an account will trigger + /// `T::DustRemoval::on_unbalanced` and `T::OnFreeBalanceZero::on_free_balance_zero`. + /// + /// # pub fn transfer( origin, dest: ::Source, @@ -364,6 +379,11 @@ decl_module! { /// and reset the account nonce (`system::AccountNonce`). /// /// The dispatch origin for this call is `root`. + /// + /// # + /// - Independent of the arguments. + /// - Contains a limited number of reads and writes. + /// # fn set_balance( who: ::Source, #[compact] free: T::Balance, @@ -700,6 +720,10 @@ where >::get(who) } + // # + // Despite iterating over a list of locks, they are limited by the number of + // lock IDs, which means the number of runtime modules that intend to use and create locks. + // # fn ensure_can_withdraw( who: &T::AccountId, _amount: T::Balance, diff --git a/substrate/srml/council/src/motions.rs b/substrate/srml/council/src/motions.rs index df357ac8c8..8f31ee582f 100644 --- a/substrate/srml/council/src/motions.rs +++ b/substrate/srml/council/src/motions.rs @@ -120,6 +120,10 @@ decl_module! { Self::deposit_event(RawEvent::MemberExecuted(proposal_hash, ok)); } + /// # + /// - Bounded storage reads and writes. + /// - Argument `threshold` has bearing on weight. + /// # fn propose(origin, #[compact] threshold: MemberCount, proposal: Box<::Proposal>) { let who = ensure_signed(origin)?; @@ -145,6 +149,10 @@ decl_module! { } } + /// # + /// - Bounded storage read and writes. + /// - Will be slightly heavier if the proposal is approved / disapproved after the vote. + /// # fn vote(origin, proposal: T::Hash, #[compact] index: ProposalIndex, approve: bool) { let who = ensure_signed(origin)?; diff --git a/substrate/srml/council/src/seats.rs b/substrate/srml/council/src/seats.rs index 3e80584900..84b6f388f2 100644 --- a/substrate/srml/council/src/seats.rs +++ b/substrate/srml/council/src/seats.rs @@ -171,6 +171,12 @@ decl_module! { /// /// Note that any trailing `false` votes in `votes` is ignored; In approval voting, not voting for a candidate /// and voting false, are equal. + /// + /// # + /// - O(1). + /// - Two extra DB entries, one DB change. + /// - Argument `votes` is limited in length to number of candidates. + /// # fn set_approvals(origin, votes: Vec, #[compact] index: VoteIndex, hint: SetIndex) -> Result { let who = ensure_signed(origin)?; Self::do_set_approvals(who, votes, index, hint) @@ -178,6 +184,10 @@ decl_module! { /// Set candidate approvals from a proxy. Approval slots stay valid as long as candidates in those slots /// are registered. + /// + /// # + /// - Same as `set_approvals` with one additional storage read. + /// # fn proxy_set_approvals(origin, votes: Vec, #[compact] index: VoteIndex, hint: SetIndex) -> Result { let who = >::proxy(ensure_signed(origin)?).ok_or("not a proxy")?; Self::do_set_approvals(who, votes, index, hint) @@ -190,6 +200,11 @@ decl_module! { /// Both indices must be provided as explained in [`voter_at`] function. /// /// May be called by anyone. Returns the voter deposit to `signed`. + /// + /// # + /// - O(1). + /// - Two fewer DB entries, one DB change. + /// # fn reap_inactive_voter( origin, #[compact] reporter_index: u32, @@ -258,6 +273,11 @@ decl_module! { /// The index must be provided as explained in [`voter_at`] function. /// /// Also removes the lock on the balance of the voter. See [`do_set_approvals()`]. + /// + /// # + /// - O(1). + /// - Two fewer DB entries, one DB change. + /// # fn retract_voter(origin, #[compact] index: u32) { let who = ensure_signed(origin)?; @@ -280,6 +300,11 @@ decl_module! { /// it will NOT have any usable funds to pass candidacy bond and must first retract. /// Note that setting approvals will lock the entire balance of the voter until /// retraction or being reported. + /// + /// # + /// - Independent of input. + /// - Three DB changes. + /// # fn submit_candidacy(origin, #[compact] slot: u32) { let who = ensure_signed(origin)?; @@ -310,6 +335,11 @@ decl_module! { /// Claim that `signed` is one of the top Self::carry_count() + current_vote().1 candidates. /// Only works if the `block_number >= current_vote().0` and `< current_vote().0 + presentation_duration()` /// `signed` should have at least + /// + /// # + /// - O(voters) compute. + /// - One DB change. + /// # fn present_winner( origin, candidate: ::Source, diff --git a/substrate/srml/democracy/src/lib.rs b/substrate/srml/democracy/src/lib.rs index 2ccff71869..b6d032d791 100644 --- a/substrate/srml/democracy/src/lib.rs +++ b/substrate/srml/democracy/src/lib.rs @@ -321,6 +321,11 @@ decl_module! { fn deposit_event() = default; /// Propose a sensitive action to be taken. + /// + /// # + /// - O(1). + /// - Two DB changes, one DB entry. + /// # fn propose(origin, proposal: Box, #[compact] value: BalanceOf @@ -343,6 +348,11 @@ decl_module! { } /// Propose a sensitive action to be taken. + /// + /// # + /// - O(1). + /// - One DB entry. + /// # fn second(origin, #[compact] proposal: PropIndex) { let who = ensure_signed(origin)?; let mut deposit = Self::deposit_of(proposal) @@ -355,6 +365,11 @@ decl_module! { /// Vote in a referendum. If `vote.is_aye()`, the vote is to enact the proposal; /// otherwise it is a vote to keep the status quo. + /// + /// # + /// - O(1). + /// - One DB change, one DB entry. + /// # fn vote(origin, #[compact] ref_index: ReferendumIndex, vote: Vote @@ -365,6 +380,11 @@ decl_module! { /// Vote in a referendum on behalf of a stash. If `vote.is_aye()`, the vote is to enact /// the proposal; otherwise it is a vote to keep the status quo. + /// + /// # + /// - O(1). + /// - One DB change, one DB entry. + /// # fn proxy_vote(origin, #[compact] ref_index: ReferendumIndex, vote: Vote @@ -492,6 +512,10 @@ decl_module! { } /// Specify a proxy. Called by the stash. + /// + /// # + /// - One extra DB entry. + /// # fn set_proxy(origin, proxy: T::AccountId) { let who = ensure_signed(origin)?; ensure!(!>::exists(&proxy), "already a proxy"); @@ -499,12 +523,20 @@ decl_module! { } /// Clear the proxy. Called by the proxy. + /// + /// # + /// - One DB clear. + /// # fn resign_proxy(origin) { let who = ensure_signed(origin)?; >::remove(who); } /// Clear the proxy. Called by the stash. + /// + /// # + /// - One DB clear. + /// # fn remove_proxy(origin, proxy: T::AccountId) { let who = ensure_signed(origin)?; ensure!(&Self::proxy(&proxy).ok_or("not a proxy")? == &who, "wrong proxy"); @@ -512,6 +544,10 @@ decl_module! { } /// Delegate vote. + /// + /// # + /// - One extra DB entry. + /// # pub fn delegate(origin, to: T::AccountId, conviction: Conviction) { let who = ensure_signed(origin)?; >::insert(who.clone(), (to.clone(), conviction)); @@ -527,6 +563,10 @@ decl_module! { } /// Undelegate vote. + /// + /// # + /// - O(1). + /// # fn undelegate(origin) { let who = ensure_signed(origin)?; ensure!(>::exists(&who), "not delegated"); diff --git a/substrate/srml/indices/src/lib.rs b/substrate/srml/indices/src/lib.rs index 38f7ee668d..45487e3b51 100644 --- a/substrate/srml/indices/src/lib.rs +++ b/substrate/srml/indices/src/lib.rs @@ -156,6 +156,21 @@ impl Module { } impl OnNewAccount for Module { + // Implementation of the config type managing the creation of new accounts. + // See Balances module for a concrete example. + // + // # + // - Independent of the arguments. + // - Given the correct value of `Self::next_enum_set`, it always has a limited + // number of reads and writes and no complex computation. + // + // As for storage, calling this function with _non-dead-indices_ will linearly grow the length of + // of `Self::enum_set`. Appropriate economic incentives should exist to make callers of this + // function provide a `who` argument that reclaims a dead account. + // + // At the time of this writing, only the Balances module calls this function upon creation + // of new accounts. + // # fn on_new_account(who: &T::AccountId) { let enum_set_size = Self::enum_set_size(); let next_set_index = Self::next_enum_set(); diff --git a/substrate/srml/session/src/lib.rs b/substrate/srml/session/src/lib.rs index d13795e4bb..5c36f6fadc 100644 --- a/substrate/srml/session/src/lib.rs +++ b/substrate/srml/session/src/lib.rs @@ -161,8 +161,16 @@ decl_module! { pub struct Module for enum Call where origin: T::Origin { fn deposit_event() = default; - /// Sets the session key of a validator (function caller) to `key`. + /// Sets the session key of the function caller to `key`. + /// Allows an account to set its session key prior to becoming a validator. /// This doesn't take effect until the next session. + /// + /// The dispatch origin of this function must be signed. + /// + /// # + /// - O(1). + /// - One extra DB entry. + /// # fn set_key(origin, key: T::SessionKey) { let who = ensure_signed(origin)?; // set new value for next session @@ -170,11 +178,15 @@ decl_module! { } /// Set a new session length. Won't kick in until the next session change (at current length). + /// + /// Dispatch origin of this call must be _root_. fn set_length(#[compact] new: T::BlockNumber) { >::put(new); } /// Forces a new session. + /// + /// Dispatch origin of this call must be _root_. fn force_new_session(apply_rewards: bool) -> Result { Self::apply_force_new_session(apply_rewards) } diff --git a/substrate/srml/staking/src/lib.rs b/substrate/srml/staking/src/lib.rs index 3f6905f654..e76b73a749 100644 --- a/substrate/srml/staking/src/lib.rs +++ b/substrate/srml/staking/src/lib.rs @@ -155,7 +155,7 @@ //! //! The term [`SlotStake`](./struct.Module.html#method.slot_stake) will be used throughout this section. It refers //! to a value calculated at the end of each era, containing the _minimum value at stake among all validators._ -//! Note that a validator's value at stake might be a combination of The validator's own stake +//! Note that a validator's value at stake might be a combination of the validator's own stake //! and the votes it received. See [`Exposure`](./struct.Exposure.html) for more details. //! //! ### Reward Calculation @@ -226,7 +226,7 @@ //! //! The election algorithm, aside from electing the validators with the most stake value and votes, tries to divide //! the nominator votes among candidates in an equal manner. To further assure this, an optional post-processing -//! can be applied that iteractively normalizes the nominator staked values until the total difference among +//! can be applied that iteratively normalizes the nominator staked values until the total difference among //! votes of a particular nominator are less than a threshold. //! //! ## GenesisConfig @@ -569,6 +569,19 @@ decl_module! { /// account that controls it. /// /// The dispatch origin for this call must be _Signed_ by the stash account. + /// + /// # + /// - Independent of the arguments. Moderate complexity. + /// - O(1). + /// - Three extra DB entries. + /// + /// NOTE: Two of the storage writes (`Self::bonded`, `Self::payee`) are _never_ cleaned unless + /// the `origin` falls below _existential deposit_ and gets removed as dust. + /// + /// NOTE: At the moment, there are no financial restrictions to bond + /// (which creates a bunch of storage items for an account). In essence, nothing prevents many accounts from + /// spamming `Staking` storage by bonding 1 UNIT. See test case: `bond_with_no_staked_value`. + /// # fn bond(origin, controller: ::Source, #[compact] value: BalanceOf, payee: RewardDestination) { let stash = ensure_signed(origin)?; @@ -583,7 +596,7 @@ decl_module! { } // You're auto-bonded forever, here. We might improve this by only bonding when - // you actually validate/nominate. + // you actually validate/nominate and remove once you unbond __everything__. >::insert(&stash, controller.clone()); >::insert(&stash, payee); @@ -598,6 +611,12 @@ decl_module! { /// Use this if there are additional funds in your stash account that you wish to bond. /// /// The dispatch origin for this call must be _Signed_ by the stash, not the controller. + /// + /// # + /// - Independent of the arguments. Insignificant complexity. + /// - O(1). + /// - One DB entry. + /// # fn bond_extra(origin, #[compact] max_additional: BalanceOf) { let stash = ensure_signed(origin)?; @@ -628,6 +647,15 @@ decl_module! { /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. /// /// See also [`Call::withdraw_unbonded`]. + /// + /// # + /// - Independent of the arguments. Limited but potentially exploitable complexity. + /// - Contains a limited number of reads. + /// - Each call (requires the remainder of the bonded balance to be above `minimum_balance`) + /// will cause a new entry to be inserted into a vector (`Ledger.unlocking`) kept in storage. + /// The only way to clean the aforementioned storage item is also user-controlled via `withdraw_unbonded`. + /// - One DB entry. + /// fn unbond(origin, #[compact] value: BalanceOf) { let controller = ensure_signed(origin)?; let mut ledger = Self::ledger(&controller).ok_or("not a controller")?; @@ -661,6 +689,14 @@ decl_module! { /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. /// /// See also [`Call::unbond`]. + /// + /// # + /// - Could be dependent on the `origin` argument and how much `unlocking` chunks exist. It implies + /// `consolidate_unlocked` which loops over `Ledger.unlocking`, which is indirectly + /// user-controlled. See [`unbond`] for more detail. + /// - Contains a limited number of reads, yet the size of which could be large based on `ledger`. + /// - Writes are limited to the `origin` account key. + /// # fn withdraw_unbonded(origin) { let controller = ensure_signed(origin)?; let ledger = Self::ledger(&controller).ok_or("not a controller")?; @@ -673,6 +709,12 @@ decl_module! { /// Effects will be felt at the beginning of the next era. /// /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. + /// + /// # + /// - Independent of the arguments. Insignificant complexity. + /// - Contains a limited number of reads. + /// - Writes are limited to the `origin` account key. + /// # fn validate(origin, prefs: ValidatorPrefs>) { let controller = ensure_signed(origin)?; let ledger = Self::ledger(&controller).ok_or("not a controller")?; @@ -687,6 +729,12 @@ decl_module! { /// Effects will be felt at the beginning of the next era. /// /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. + /// + /// # + /// - The transaction's complexity is proportional to the size of `targets`, + /// which is capped at `MAX_NOMINATIONS`. + /// - Both the reads and writes follow a similar pattern. + /// # fn nominate(origin, targets: Vec<::Source>) { let controller = ensure_signed(origin)?; let ledger = Self::ledger(&controller).ok_or("not a controller")?; @@ -706,6 +754,12 @@ decl_module! { /// Effects will be felt at the beginning of the next era. /// /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. + /// + /// # + /// - Independent of the arguments. Insignificant complexity. + /// - Contains one read. + /// - Writes are limited to the `origin` account key. + /// # fn chill(origin) { let controller = ensure_signed(origin)?; let ledger = Self::ledger(&controller).ok_or("not a controller")?; @@ -719,6 +773,12 @@ decl_module! { /// Effects will be felt at the beginning of the next era. /// /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. + /// + /// # + /// - Independent of the arguments. Insignificant complexity. + /// - Contains a limited number of reads. + /// - Writes are limited to the `origin` account key. + /// # fn set_payee(origin, payee: RewardDestination) { let controller = ensure_signed(origin)?; let ledger = Self::ledger(&controller).ok_or("not a controller")?; @@ -731,6 +791,12 @@ decl_module! { /// Effects will be felt at the beginning of the next era. /// /// The dispatch origin for this call must be _Signed_ by the stash, not the controller. + /// + /// # + /// - Independent of the arguments. Insignificant complexity. + /// - Contains a limited number of reads. + /// - Writes are limited to the `origin` account key. + /// # fn set_controller(origin, controller: ::Source) { let stash = ensure_signed(origin)?; let old_controller = Self::bonded(&stash).ok_or("not a stash")?; @@ -744,6 +810,8 @@ decl_module! { } } + // ----- Root calls. + /// Set the number of sessions in an era. fn set_sessions_per_era(#[compact] new: T::BlockNumber) { >::put(new); @@ -761,6 +829,12 @@ decl_module! { /// Force there to be a new era. This also forces a new session immediately after. /// `apply_rewards` should be true for validators to get the session reward. + /// + /// # + /// - Independent of the arguments. + /// - Triggers the Phragmen election. Expensive but not user-controlled. + /// - Depends on state: `O(|edges| * |validators|)`. + /// # fn force_new_era(apply_rewards: bool) -> Result { Self::apply_force_new_era(apply_rewards) } diff --git a/substrate/srml/staking/src/tests.rs b/substrate/srml/staking/src/tests.rs index eba6ce77a5..357a1c1427 100644 --- a/substrate/srml/staking/src/tests.rs +++ b/substrate/srml/staking/src/tests.rs @@ -1669,20 +1669,15 @@ fn bond_with_no_staked_value() { System::set_block_number(1); Session::check_rotate_session(System::block_number()); - // Not elected even though we want 3. assert_eq_uvec!(Session::validators(), vec![30, 20, 10]); // min of 10, 20 and 30 (30 got a payout into staking so it raised it from 1 to 11). assert_eq!(Staking::slot_stake(), 11); - // let's make the stingy one elected. + // make the stingy one elected. assert_ok!(Staking::bond(Origin::signed(3), 4, 500, RewardDestination::Controller)); assert_ok!(Staking::nominate(Origin::signed(4), vec![1])); - // no rewards paid to 2 and 4 yet - assert_eq!(Balances::free_balance(&2), initial_balance_2); - assert_eq!(Balances::free_balance(&4), initial_balance_4); - System::set_block_number(2); Session::check_rotate_session(System::block_number()); @@ -1692,10 +1687,6 @@ fn bond_with_no_staked_value() { // New slot stake. assert_eq!(Staking::slot_stake(), 501); - // no rewards paid to 2 and 4 yet - assert_eq!(Balances::free_balance(&2), initial_balance_2); - assert_eq!(Balances::free_balance(&4), initial_balance_4); - System::set_block_number(3); Session::check_rotate_session(System::block_number()); diff --git a/substrate/srml/sudo/src/lib.rs b/substrate/srml/sudo/src/lib.rs index 1caeac73b8..a421bdae68 100644 --- a/substrate/srml/sudo/src/lib.rs +++ b/substrate/srml/sudo/src/lib.rs @@ -110,6 +110,12 @@ decl_module! { /// Authenticates the sudo key and dispatches a function call with `Root` origin. /// /// The dispatch origin for this call must be _Signed_. + /// + /// # + /// - O(1). + /// - Limited storage reads. + /// - No DB writes. + /// # fn sudo(origin, proposal: Box) { // This is a public call, so we ensure that the origin is some signed account. let sender = ensure_signed(origin)?; @@ -129,6 +135,12 @@ decl_module! { /// Authenticates the current sudo key and sets the given AccountId (`new`) as the new sudo key. /// /// The dispatch origin for this call must be _Signed_. + /// + /// # + /// - O(1). + /// - Limited storage reads. + /// - One DB change. + /// # fn set_key(origin, new: ::Source) { // This is a public call, so we ensure that the origin is some signed account. let sender = ensure_signed(origin)?; diff --git a/substrate/srml/system/src/lib.rs b/substrate/srml/system/src/lib.rs index b08321f860..276b389dc1 100644 --- a/substrate/srml/system/src/lib.rs +++ b/substrate/srml/system/src/lib.rs @@ -475,7 +475,7 @@ impl Module { /// Deposits an event into this block's event record adding this event /// to the corresponding topic indexes. /// - /// This will update storage entries that correpond to the specified topics. + /// This will update storage entries that correspond to the specified topics. /// It is expected that light-clients could subscribe to this topics. pub fn deposit_event_indexed(topics: &[T::Hash], event: T::Event) { let extrinsic_index = Self::extrinsic_index(); diff --git a/substrate/srml/treasury/src/lib.rs b/substrate/srml/treasury/src/lib.rs index cd9e781f66..fb3b68a6e9 100644 --- a/substrate/srml/treasury/src/lib.rs +++ b/substrate/srml/treasury/src/lib.rs @@ -110,6 +110,12 @@ decl_module! { /// Put forward a suggestion for spending. A deposit proportional to the value /// is reserved and slashed if the proposal is rejected. It is returned once the /// proposal is awarded. + /// + /// # + /// - O(1). + /// - Limited storage reads. + /// - One DB change, one extra DB entry. + /// # fn propose_spend( origin, #[compact] value: BalanceOf, @@ -149,6 +155,12 @@ decl_module! { } /// Reject a proposed spend. The original deposit will be slashed. + /// + /// # + /// - O(1). + /// - Limited storage reads. + /// - One DB clear. + /// # fn reject_proposal(origin, #[compact] proposal_id: ProposalIndex) { T::RejectOrigin::ensure_origin(origin)?; let proposal = >::take(proposal_id).ok_or("No proposal at that index")?; @@ -160,6 +172,12 @@ decl_module! { /// Approve a proposal. At a later time, the proposal will be allocated to the beneficiary /// and the original deposit will be returned. + /// + /// # + /// - O(1). + /// - Limited storage reads. + /// - One DB change. + /// # fn approve_proposal(origin, #[compact] proposal_id: ProposalIndex) { T::ApproveOrigin::ensure_origin(origin)?;