Balances: repatriate_reserved should respect freezes (#13885)

* repatriate_reserved should respect freezes

* Docs

* Fix and clean

* Formatting

* Update frame/balances/src/types.rs

Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com>

* Fix

* Simplify

* Fixes

* Fixes

---------

Co-authored-by: Jegor Sidorenko <5252494+jsidorenko@users.noreply.github.com>
This commit is contained in:
Gavin Wood
2023-04-17 19:13:55 +01:00
committed by GitHub
parent b66bc22252
commit d23a251ee9
3 changed files with 46 additions and 39 deletions
@@ -22,7 +22,7 @@ use frame_support::{
ensure, ensure,
pallet_prelude::DispatchResult, pallet_prelude::DispatchResult,
traits::{ traits::{
tokens::{fungible, BalanceStatus as Status}, tokens::{fungible, BalanceStatus as Status, Fortitude::Polite, Precision::BestEffort},
Currency, DefensiveSaturating, ExistenceRequirement, Currency, DefensiveSaturating, ExistenceRequirement,
ExistenceRequirement::AllowDeath, ExistenceRequirement::AllowDeath,
Get, Imbalance, LockIdentifier, LockableCurrency, NamedReservableCurrency, Get, Imbalance, LockIdentifier, LockableCurrency, NamedReservableCurrency,
@@ -590,13 +590,18 @@ where
/// Is a no-op if: /// Is a no-op if:
/// - the value to be moved is zero; or /// - the value to be moved is zero; or
/// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`. /// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`.
///
/// This is `Polite` and thus will not repatriate any funds which would lead the total balance
/// to be less than the frozen amount. Returns `Ok` with the actual amount of funds moved,
/// which may be less than `value` since the operation is done an a `BestEffort` basis.
fn repatriate_reserved( fn repatriate_reserved(
slashed: &T::AccountId, slashed: &T::AccountId,
beneficiary: &T::AccountId, beneficiary: &T::AccountId,
value: Self::Balance, value: Self::Balance,
status: Status, status: Status,
) -> Result<Self::Balance, DispatchError> { ) -> Result<Self::Balance, DispatchError> {
let actual = Self::do_transfer_reserved(slashed, beneficiary, value, true, status)?; let actual =
Self::do_transfer_reserved(slashed, beneficiary, value, BestEffort, Polite, status)?;
Ok(value.saturating_sub(actual)) Ok(value.saturating_sub(actual))
} }
} }
+36 -34
View File
@@ -205,7 +205,10 @@ type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup
#[frame_support::pallet] #[frame_support::pallet]
pub mod pallet { pub mod pallet {
use super::*; use super::*;
use frame_support::{pallet_prelude::*, traits::fungible::Credit}; use frame_support::{
pallet_prelude::*,
traits::{fungible::Credit, tokens::Precision},
};
use frame_system::pallet_prelude::*; use frame_system::pallet_prelude::*;
pub type CreditOf<T, I> = Credit<<T as frame_system::Config>::AccountId, Pallet<T, I>>; pub type CreditOf<T, I> = Credit<<T as frame_system::Config>::AccountId, Pallet<T, I>>;
@@ -1100,59 +1103,58 @@ pub mod pallet {
} }
/// Move the reserved balance of one account into the balance of another, according to /// Move the reserved balance of one account into the balance of another, according to
/// `status`. /// `status`. This will respect freezes/locks only if `fortitude` is `Polite`.
/// ///
/// Is a no-op if: /// Is a no-op if the value to be moved is zero.
/// - the value to be moved is zero; or
/// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`.
/// ///
/// NOTE: returns actual amount of transferred value in `Ok` case. /// NOTE: returns actual amount of transferred value in `Ok` case.
pub(crate) fn do_transfer_reserved( pub(crate) fn do_transfer_reserved(
slashed: &T::AccountId, slashed: &T::AccountId,
beneficiary: &T::AccountId, beneficiary: &T::AccountId,
value: T::Balance, value: T::Balance,
best_effort: bool, precision: Precision,
fortitude: Fortitude,
status: Status, status: Status,
) -> Result<T::Balance, DispatchError> { ) -> Result<T::Balance, DispatchError> {
if value.is_zero() { if value.is_zero() {
return Ok(Zero::zero()) return Ok(Zero::zero())
} }
let max = <Self as fungible::InspectHold<_>>::reducible_total_balance_on_hold(
slashed, fortitude,
);
let actual = match precision {
Precision::BestEffort => value.min(max),
Precision::Exact => value,
};
ensure!(actual <= max, TokenError::FundsUnavailable);
if slashed == beneficiary { if slashed == beneficiary {
return match status { return match status {
Status::Free => Ok(value.saturating_sub(Self::unreserve(slashed, value))), Status::Free => Ok(actual.saturating_sub(Self::unreserve(slashed, actual))),
Status::Reserved => Ok(value.saturating_sub(Self::reserved_balance(slashed))), Status::Reserved => Ok(actual),
} }
} }
let ((actual, maybe_dust_1), maybe_dust_2) = Self::try_mutate_account( let ((_, maybe_dust_1), maybe_dust_2) = Self::try_mutate_account(
beneficiary, beneficiary,
|to_account, is_new| -> Result<(T::Balance, Option<T::Balance>), DispatchError> { |to_account, is_new| -> Result<((), Option<T::Balance>), DispatchError> {
ensure!(!is_new, Error::<T, I>::DeadAccount); ensure!(!is_new, Error::<T, I>::DeadAccount);
Self::try_mutate_account( Self::try_mutate_account(slashed, |from_account, _| -> DispatchResult {
slashed, match status {
|from_account, _| -> Result<T::Balance, DispatchError> { Status::Free =>
let actual = cmp::min(from_account.reserved, value); to_account.free = to_account
ensure!( .free
best_effort || actual == value, .checked_add(&actual)
Error::<T, I>::InsufficientBalance .ok_or(ArithmeticError::Overflow)?,
); Status::Reserved =>
match status { to_account.reserved = to_account
Status::Free => .reserved
to_account.free = to_account .checked_add(&actual)
.free .ok_or(ArithmeticError::Overflow)?,
.checked_add(&actual) }
.ok_or(ArithmeticError::Overflow)?, from_account.reserved.saturating_reduce(actual);
Status::Reserved => Ok(())
to_account.reserved = to_account })
.reserved
.checked_add(&actual)
.ok_or(ArithmeticError::Overflow)?,
}
from_account.reserved -= actual;
Ok(actual)
},
)
}, },
)?; )?;
+3 -3
View File
@@ -102,9 +102,9 @@ pub struct AccountData<Balance> {
/// This is the sum of all individual holds together with any sums still under the (deprecated) /// This is the sum of all individual holds together with any sums still under the (deprecated)
/// reserves API. /// reserves API.
pub reserved: Balance, pub reserved: Balance,
/// The amount that `free` may not drop below when reducing the balance, except for actions /// The amount that `free + reserved` may not drop below when reducing the balance, except for
/// where the account owner cannot reasonably benefit from thr balance reduction, such as /// actions where the account owner cannot reasonably benefit from the balance reduction, such
/// slashing. /// as slashing.
pub frozen: Balance, pub frozen: Balance,
/// Extra information about this account. The MSB is a flag indicating whether the new ref- /// Extra information about this account. The MSB is a flag indicating whether the new ref-
/// counting logic is in place for this account. /// counting logic is in place for this account.