Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Proposal: Defensive trait for infallible frame operations #10626

Merged
merged 17 commits into from
Jan 24, 2022
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions frame/balances/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ use frame_support::{
pallet_prelude::DispatchResult,
traits::{
tokens::{fungible, BalanceStatus as Status, DepositConsequence, WithdrawConsequence},
Currency, ExistenceRequirement,
Currency, DefensiveSaturating, ExistenceRequirement,
ExistenceRequirement::{AllowDeath, KeepAlive},
Get, Imbalance, LockIdentifier, LockableCurrency, NamedReservableCurrency, OnUnbalanced,
ReservableCurrency, SignedImbalance, StoredMap, TryDrop, WithdrawReasons,
Expand Down Expand Up @@ -1783,7 +1783,7 @@ where
account.reserved -= actual;
// defensive only: this can never fail since total issuance which is at least
// free+reserved fits into the same data type.
account.free = account.free.saturating_add(actual);
account.free = account.free.defensive_saturating_add(actual);
actual
}) {
Ok(x) => x,
Expand Down Expand Up @@ -1896,7 +1896,7 @@ where
match reserves.binary_search_by_key(id, |data| data.id) {
Ok(index) => {
// this add can't overflow but just to be defensive.
reserves[index].amount = reserves[index].amount.saturating_add(value);
reserves[index].amount = reserves[index].amount.defensive_saturating_add(value);
},
Err(index) => {
reserves
Expand Down Expand Up @@ -1929,8 +1929,8 @@ where

let remain = <Self as ReservableCurrency<_>>::unreserve(who, to_change);

// remain should always be zero but just to be defensive here
let actual = to_change.saturating_sub(remain);
// remain should always be zero but just to be defensive here.
let actual = to_change.defensive_saturating_sub(remain);

// `actual <= to_change` and `to_change <= amount`; qed;
reserves[index].amount -= actual;
Expand Down Expand Up @@ -1976,8 +1976,8 @@ where
let (imb, remain) =
<Self as ReservableCurrency<_>>::slash_reserved(who, to_change);

// remain should always be zero but just to be defensive here
let actual = to_change.saturating_sub(remain);
// remain should always be zero but just to be defensive here.
let actual = to_change.defensive_saturating_sub(remain);

// `actual <= to_change` and `to_change <= amount`; qed;
reserves[index].amount -= actual;
Expand Down Expand Up @@ -2036,12 +2036,12 @@ where
)?;

// remain should always be zero but just to be defensive
// here
let actual = to_change.saturating_sub(remain);
// here.
let actual = to_change.defensive_saturating_sub(remain);

// this add can't overflow but just to be defensive.
reserves[index].amount =
reserves[index].amount.saturating_add(actual);
reserves[index].amount.defensive_saturating_add(actual);

Ok(actual)
},
Expand All @@ -2056,7 +2056,7 @@ where

// remain should always be zero but just to be defensive
// here
let actual = to_change.saturating_sub(remain);
let actual = to_change.defensive_saturating_sub(remain);

reserves
.try_insert(
Expand All @@ -2079,7 +2079,7 @@ where
)?;

// remain should always be zero but just to be defensive here
to_change.saturating_sub(remain)
to_change.defensive_saturating_sub(remain)
};

// `actual <= to_change` and `to_change <= amount`; qed;
Expand Down
3 changes: 2 additions & 1 deletion frame/democracy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ use codec::{Decode, Encode, Input};
use frame_support::{
ensure,
traits::{
defensive_prelude::*,
schedule::{DispatchTime, Named as ScheduleNamed},
BalanceStatus, Currency, Get, LockIdentifier, LockableCurrency, OnUnbalanced,
ReservableCurrency, WithdrawReasons,
Expand Down Expand Up @@ -1630,7 +1631,7 @@ impl<T: Config> Pallet<T> {
let mut public_props = Self::public_props();
if let Some((winner_index, _)) = public_props.iter().enumerate().max_by_key(
// defensive only: All current public proposals have an amount locked
|x| Self::backing_for((x.1).0).unwrap_or_else(Zero::zero),
|x| Self::backing_for((x.1).0).defensive_unwrap_or_else(Zero::zero),
) {
let (prop_index, proposal, _) = public_props.swap_remove(winner_index);
<PublicProps<T>>::put(public_props);
Expand Down
4 changes: 2 additions & 2 deletions frame/election-provider-multi-phase/src/signed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use crate::{
use codec::{Decode, Encode, HasCompact};
use frame_support::{
storage::bounded_btree_map::BoundedBTreeMap,
traits::{Currency, Get, OnUnbalanced, ReservableCurrency},
traits::{defensive_prelude::*, Currency, Get, OnUnbalanced, ReservableCurrency},
};
use sp_arithmetic::traits::SaturatedConversion;
use sp_npos_elections::{is_score_better, ElectionScore, NposSolution};
Expand Down Expand Up @@ -365,7 +365,7 @@ impl<T: Config> Pallet<T> {
let active_voters = raw_solution.solution.voter_count() as u32;
let feasibility_weight = {
// defensive only: at the end of signed phase, snapshot will exits.
let desired_targets = Self::desired_targets().unwrap_or_default();
let desired_targets = Self::desired_targets().defensive_unwrap_or_default();
T::WeightInfo::feasibility_check(voters, targets, active_voters, desired_targets)
};
// the feasibility check itself has some weight
Expand Down
2 changes: 1 addition & 1 deletion frame/election-provider-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ impl<AccountId, BlockNumber> ElectionProvider for NoElection<(AccountId, BlockNu
/// used on the implementing side of [`ElectionDataProvider`].
pub trait SortedListProvider<AccountId> {
/// The list's error type.
type Error;
type Error: sp_std::fmt::Debug;

/// An iterator over the list, which can have `take` called on it.
fn iter() -> Box<dyn Iterator<Item = AccountId>>;
Expand Down
8 changes: 4 additions & 4 deletions frame/elections-phragmen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,9 @@ use codec::{Decode, Encode};
use frame_support::{
dispatch::WithPostDispatchInfo,
traits::{
ChangeMembers, Contains, ContainsLengthBound, Currency, CurrencyToVote, Get,
InitializeMembers, LockIdentifier, LockableCurrency, OnUnbalanced, ReservableCurrency,
SortedMembers, StorageVersion, WithdrawReasons,
defensive_prelude::*, ChangeMembers, Contains, ContainsLengthBound, Currency,
CurrencyToVote, Get, InitializeMembers, LockIdentifier, LockableCurrency, OnUnbalanced,
ReservableCurrency, SortedMembers, StorageVersion, WithdrawReasons,
},
weights::Weight,
};
Expand Down Expand Up @@ -1028,7 +1028,7 @@ impl<T: Config> Pallet<T> {
candidates_and_deposit
.iter()
.find_map(|(c, d)| if c == x { Some(*d) } else { None })
.unwrap_or_default()
.defensive_unwrap_or_default()
};
// fetch deposits from the one recorded one. This will make sure that a candidate who
// submitted candidacy before a change to candidacy deposit will have the correct amount
Expand Down
11 changes: 7 additions & 4 deletions frame/gilt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub mod pallet {
pub use crate::weights::WeightInfo;
use frame_support::{
pallet_prelude::*,
traits::{Currency, OnUnbalanced, ReservableCurrency},
traits::{Currency, DefensiveSaturating, OnUnbalanced, ReservableCurrency},
};
use frame_system::pallet_prelude::*;
use sp_arithmetic::{PerThing, Perquintill};
Expand Down Expand Up @@ -599,10 +599,12 @@ pub mod pallet {
remaining -= amount;
// Should never underflow since it should track the total of the
// bids exactly, but we'll be defensive.
qs[queue_index].1 = qs[queue_index].1.saturating_sub(bid.amount);
qs[queue_index].1 =
qs[queue_index].1.defensive_saturating_sub(bid.amount);

// Now to activate the bid...
let nongilt_issuance = total_issuance.saturating_sub(totals.frozen);
let nongilt_issuance =
total_issuance.defensive_saturating_sub(totals.frozen);
let effective_issuance = totals
.proportion
.left_from_one()
Expand All @@ -613,7 +615,8 @@ pub mod pallet {
let who = bid.who;
let index = totals.index;
totals.frozen += bid.amount;
totals.proportion = totals.proportion.saturating_add(proportion);
totals.proportion =
totals.proportion.defensive_saturating_add(proportion);
totals.index += 1;
let e =
Event::GiltIssued { index, expiry, who: who.clone(), amount };
Expand Down
2 changes: 1 addition & 1 deletion frame/identity/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ fn create_sub_accounts<T: Config>(
}

// Set identity so `set_subs` does not fail.
let _ = T::Currency::make_free_balance_be(&who, BalanceOf::<T>::max_value());
let _ = T::Currency::make_free_balance_be(&who, BalanceOf::<T>::max_value() / 2u32.into());
let info = create_identity_info::<T>(1);
Identity::<T>::set_identity(who_origin.clone().into(), Box::new(info))?;

Expand Down
16 changes: 8 additions & 8 deletions frame/proxy/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ fn assert_last_event<T: Config>(generic_event: <T as Config>::Event) {

fn add_proxies<T: Config>(n: u32, maybe_who: Option<T::AccountId>) -> Result<(), &'static str> {
let caller = maybe_who.unwrap_or_else(|| whitelisted_caller());
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value() / 2u32.into());
for i in 0..n {
Proxy::<T>::add_proxy(
RawOrigin::Signed(caller.clone()).into(),
Expand All @@ -51,12 +51,12 @@ fn add_announcements<T: Config>(
maybe_real: Option<T::AccountId>,
) -> Result<(), &'static str> {
let caller = maybe_who.unwrap_or_else(|| account("caller", 0, SEED));
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value() / 2u32.into());
let real = if let Some(real) = maybe_real {
real
} else {
let real = account("real", 0, SEED);
T::Currency::make_free_balance_be(&real, BalanceOf::<T>::max_value());
T::Currency::make_free_balance_be(&real, BalanceOf::<T>::max_value() / 2u32.into());
Proxy::<T>::add_proxy(
RawOrigin::Signed(real.clone()).into(),
caller.clone(),
Expand All @@ -80,7 +80,7 @@ benchmarks! {
let p in 1 .. (T::MaxProxies::get() - 1).into() => add_proxies::<T>(p, None)?;
// In this case the caller is the "target" proxy
let caller: T::AccountId = account("target", p - 1, SEED);
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value() / 2u32.into());
// ... and "real" is the traditional caller. This is not a typo.
let real: T::AccountId = whitelisted_caller();
let call: <T as Config>::Call = frame_system::Call::<T>::remark { remark: vec![] }.into();
Expand All @@ -95,7 +95,7 @@ benchmarks! {
// In this case the caller is the "target" proxy
let caller: T::AccountId = account("anonymous", 0, SEED);
let delegate: T::AccountId = account("target", p - 1, SEED);
T::Currency::make_free_balance_be(&delegate, BalanceOf::<T>::max_value());
T::Currency::make_free_balance_be(&delegate, BalanceOf::<T>::max_value() / 2u32.into());
// ... and "real" is the traditional caller. This is not a typo.
let real: T::AccountId = whitelisted_caller();
let call: <T as Config>::Call = frame_system::Call::<T>::remark { remark: vec![] }.into();
Expand All @@ -115,7 +115,7 @@ benchmarks! {
let p in 1 .. (T::MaxProxies::get() - 1).into() => add_proxies::<T>(p, None)?;
// In this case the caller is the "target" proxy
let caller: T::AccountId = account("target", p - 1, SEED);
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value() / 2u32.into());
// ... and "real" is the traditional caller. This is not a typo.
let real: T::AccountId = whitelisted_caller();
let call: <T as Config>::Call = frame_system::Call::<T>::remark { remark: vec![] }.into();
Expand All @@ -136,7 +136,7 @@ benchmarks! {
let p in 1 .. (T::MaxProxies::get() - 1).into() => add_proxies::<T>(p, None)?;
// In this case the caller is the "target" proxy
let caller: T::AccountId = account("target", p - 1, SEED);
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value() / 2u32.into());
// ... and "real" is the traditional caller. This is not a typo.
let real: T::AccountId = whitelisted_caller();
let call: <T as Config>::Call = frame_system::Call::<T>::remark { remark: vec![] }.into();
Expand All @@ -157,7 +157,7 @@ benchmarks! {
let p in 1 .. (T::MaxProxies::get() - 1).into() => add_proxies::<T>(p, None)?;
// In this case the caller is the "target" proxy
let caller: T::AccountId = account("target", p - 1, SEED);
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value() / 2u32.into());
// ... and "real" is the traditional caller. This is not a typo.
let real: T::AccountId = whitelisted_caller();
add_announcements::<T>(a, Some(caller.clone()), None)?;
Expand Down
10 changes: 4 additions & 6 deletions frame/staking/src/pallet/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ use frame_election_provider_support::{
use frame_support::{
pallet_prelude::*,
traits::{
Currency, CurrencyToVote, EstimateNextNewSession, Get, Imbalance, LockableCurrency,
OnUnbalanced, UnixTime, WithdrawReasons,
Currency, CurrencyToVote, DefensiveUnwrap, EstimateNextNewSession, Get, Imbalance,
LockableCurrency, OnUnbalanced, UnixTime, WithdrawReasons,
},
weights::{Weight, WithPostDispatchInfo},
};
Expand Down Expand Up @@ -775,10 +775,8 @@ impl<T: Config> Pallet<T> {
pub fn do_add_nominator(who: &T::AccountId, nominations: Nominations<T::AccountId>) {
if !Nominators::<T>::contains_key(who) {
// maybe update sorted list. Error checking is defensive-only - this should never fail.
if T::SortedListProvider::on_insert(who.clone(), Self::weight_of(who)).is_err() {
log!(warn, "attempt to insert duplicate nominator ({:#?})", who);
debug_assert!(false, "attempt to insert duplicate nominator");
};
let _ = T::SortedListProvider::on_insert(who.clone(), Self::weight_of(who))
.defensive_unwrap_or_default();

debug_assert_eq!(T::SortedListProvider::sanity_check(), Ok(()));
}
Expand Down
11 changes: 6 additions & 5 deletions frame/support/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,13 @@ pub use filter::{ClearFilterGuard, FilterStack, FilterStackGuard, InstanceFilter

mod misc;
pub use misc::{
defensive_prelude::{self, *},
Backing, ConstBool, ConstI128, ConstI16, ConstI32, ConstI64, ConstI8, ConstU128, ConstU16,
ConstU32, ConstU64, ConstU8, EnsureInherentsAreFirst, EqualPrivilegeOnly, EstimateCallFee,
ExecuteBlock, ExtrinsicCall, Get, GetBacking, GetDefault, HandleLifetime, IsSubType, IsType,
Len, OffchainWorker, OnKilledAccount, OnNewAccount, PreimageProvider, PreimageRecipient,
PrivilegeCmp, SameOrOther, Time, TryCollect, TryDrop, UnixTime, WrapperKeepOpaque,
WrapperOpaque,
ConstU32, ConstU64, ConstU8, DefensiveSaturating, EnsureInherentsAreFirst, EqualPrivilegeOnly,
EstimateCallFee, ExecuteBlock, ExtrinsicCall, Get, GetBacking, GetDefault, HandleLifetime,
IsSubType, IsType, Len, OffchainWorker, OnKilledAccount, OnNewAccount, PreimageProvider,
PreimageRecipient, PrivilegeCmp, SameOrOther, Time, TryCollect, TryDrop, UnixTime,
WrapperKeepOpaque, WrapperOpaque,
};

mod stored_map;
Expand Down
Loading