From 6d0926e239776a509586f2030fa5b5d9c6aac965 Mon Sep 17 00:00:00 2001 From: Muharem <ismailov.m.h@gmail.com> Date: Tue, 23 Jul 2024 15:53:10 +0200 Subject: [PATCH] Make `on_unbalanceds` work with `fungibles` `imbalances` (#4564) Make `on_unbalanceds` work with `fungibles` `imbalances`. The `fungibles` `imbalances` cannot be handled by the default implementation of `on_unbalanceds` from the `OnUnbalanced` trait. This is because the `fungibles` `imbalances` types do not implement the `Imbalance` trait (and cannot with its current semantics). The `on_unbalanceds` function requires only the `merge` function for the imbalance type. In this PR, we provide the `TryMerge` trait, which can be implemented by all imbalance types and make `OnUnbalanced` require it instead `Imbalance`. ### Migration for `OnUnbalanced` trait implementations: In case if you have a custom implementation of `on_unbalanceds` trait function, remove it's `<B>` type argument. ### Migration for custom imbalance types: If you have your own imbalance types implementations, implement the `TryMerge` trait for it introduced with this update. The applicability of the `on_unbalanceds` function to fungibles imbalances is useful in cases like - [link](https://github.com/paritytech/polkadot-sdk/blob/3a8e675e9f6f283514c00c14d3d1d90ed5bf59c0/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/payment.rs#L267) from https://github.com/paritytech/polkadot-sdk/pull/4488. --------- Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> --- cumulus/parachains/common/src/impls.rs | 2 +- polkadot/runtime/common/src/impls.rs | 2 +- prdoc/pr_4564.prdoc | 32 +++++++++++++++++++ substrate/bin/node/runtime/src/lib.rs | 2 +- substrate/frame/balances/src/impl_currency.rs | 14 +++++++- .../src/traits/tokens/fungible/imbalance.rs | 10 +++++- .../src/traits/tokens/fungibles/imbalance.rs | 17 +++++++++- .../support/src/traits/tokens/imbalance.rs | 16 +++++++++- .../traits/tokens/imbalance/on_unbalanced.rs | 21 ++++++++++-- .../asset-conversion-tx-payment/src/mock.rs | 2 +- .../frame/transaction-payment/src/mock.rs | 2 +- 11 files changed, 108 insertions(+), 12 deletions(-) create mode 100644 prdoc/pr_4564.prdoc diff --git a/cumulus/parachains/common/src/impls.rs b/cumulus/parachains/common/src/impls.rs index 42ea50c75a8..c1797c0a751 100644 --- a/cumulus/parachains/common/src/impls.rs +++ b/cumulus/parachains/common/src/impls.rs @@ -67,7 +67,7 @@ where AccountIdOf<R>: From<polkadot_primitives::AccountId> + Into<polkadot_primitives::AccountId>, <R as frame_system::Config>::RuntimeEvent: From<pallet_balances::Event<R>>, { - fn on_unbalanceds<B>( + fn on_unbalanceds( mut fees_then_tips: impl Iterator< Item = fungible::Credit<R::AccountId, pallet_balances::Pallet<R>>, >, diff --git a/polkadot/runtime/common/src/impls.rs b/polkadot/runtime/common/src/impls.rs index 7fdff814a2d..b6a93cf5368 100644 --- a/polkadot/runtime/common/src/impls.rs +++ b/polkadot/runtime/common/src/impls.rs @@ -51,7 +51,7 @@ where <R as frame_system::Config>::AccountId: From<polkadot_primitives::AccountId>, <R as frame_system::Config>::AccountId: Into<polkadot_primitives::AccountId>, { - fn on_unbalanceds<B>( + fn on_unbalanceds( mut fees_then_tips: impl Iterator<Item = Credit<R::AccountId, pallet_balances::Pallet<R>>>, ) { if let Some(fees) = fees_then_tips.next() { diff --git a/prdoc/pr_4564.prdoc b/prdoc/pr_4564.prdoc new file mode 100644 index 00000000000..896e49ee6b9 --- /dev/null +++ b/prdoc/pr_4564.prdoc @@ -0,0 +1,32 @@ +title: "Make `OnUnbalanced::on_unbalanceds` work with `fungibles` `imbalances`" + +doc: + - audience: Runtime Dev + description: | + The `on_unbalanceds` function of `OnUnbalanced` trait accepts the `fungibles` `imbalances` + imbalances. This is done by replacing the `Imbalance` trait bound on imbalance type with + the `TryMerge` trait bound. The `TryMerge` trait is implemented for all imbalance types. + + ### Migration for `OnUnbalanced` trait implementations: + In case if you have a custom implementation of `on_unbalanceds` trait function, remove + it's `<B>` type argument. + + ### Migration for custom imbalance types: + If you have your own imbalance types implementations, implement the `TryMerge` trait for it + introduced with this update. + +crates: + - name: frame-support + bump: major + - name: pallet-balances + bump: minor + - name: pallet-asset-conversion-tx-payment + bump: patch + - name: pallet-transaction-payment + bump: patch + - name: kitchensink-runtime + bump: patch + - name: polkadot-runtime-common + bump: patch + - name: parachains-common + bump: minor diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 78c7bba6457..99721344726 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -189,7 +189,7 @@ type NegativeImbalance = <Balances as Currency<AccountId>>::NegativeImbalance; pub struct DealWithFees; impl OnUnbalanced<NegativeImbalance> for DealWithFees { - fn on_unbalanceds<B>(mut fees_then_tips: impl Iterator<Item = NegativeImbalance>) { + fn on_unbalanceds(mut fees_then_tips: impl Iterator<Item = NegativeImbalance>) { if let Some(fees) = fees_then_tips.next() { // for fees, 80% to treasury, 20% to author let mut split = fees.ration(80, 20); diff --git a/substrate/frame/balances/src/impl_currency.rs b/substrate/frame/balances/src/impl_currency.rs index 049f0cc626c..23feb46b72c 100644 --- a/substrate/frame/balances/src/impl_currency.rs +++ b/substrate/frame/balances/src/impl_currency.rs @@ -41,7 +41,7 @@ use sp_runtime::traits::Bounded; mod imbalances { use super::*; use core::mem; - use frame_support::traits::SameOrOther; + use frame_support::traits::{tokens::imbalance::TryMerge, SameOrOther}; /// Opaque, move-only struct with private fields that serves as a token denoting that /// funds have been created without any equal and opposite accounting. @@ -133,6 +133,12 @@ mod imbalances { } } + impl<T: Config<I>, I: 'static> TryMerge for PositiveImbalance<T, I> { + fn try_merge(self, other: Self) -> Result<Self, (Self, Self)> { + Ok(self.merge(other)) + } + } + impl<T: Config<I>, I: 'static> TryDrop for NegativeImbalance<T, I> { fn try_drop(self) -> result::Result<(), Self> { self.drop_zero() @@ -197,6 +203,12 @@ mod imbalances { } } + impl<T: Config<I>, I: 'static> TryMerge for NegativeImbalance<T, I> { + fn try_merge(self, other: Self) -> Result<Self, (Self, Self)> { + Ok(self.merge(other)) + } + } + impl<T: Config<I>, I: 'static> Drop for PositiveImbalance<T, I> { /// Basic drop handler will just square up the total issuance. fn drop(&mut self) { diff --git a/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs b/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs index 41907b2aa00..a9367d0f164 100644 --- a/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs +++ b/substrate/frame/support/src/traits/tokens/fungible/imbalance.rs @@ -24,7 +24,7 @@ use super::{super::Imbalance as ImbalanceT, Balanced, *}; use crate::traits::{ fungibles, misc::{SameOrOther, TryDrop}, - tokens::{AssetId, Balance}, + tokens::{imbalance::TryMerge, AssetId, Balance}, }; use core::marker::PhantomData; use frame_support_procedural::{EqNoBound, PartialEqNoBound, RuntimeDebugNoBound}; @@ -157,6 +157,14 @@ impl<B: Balance, OnDrop: HandleImbalanceDrop<B>, OppositeOnDrop: HandleImbalance } } +impl<B: Balance, OnDrop: HandleImbalanceDrop<B>, OppositeOnDrop: HandleImbalanceDrop<B>> TryMerge + for Imbalance<B, OnDrop, OppositeOnDrop> +{ + fn try_merge(self, other: Self) -> Result<Self, (Self, Self)> { + Ok(self.merge(other)) + } +} + /// Converts a `fungibles` `imbalance` instance to an instance of a `fungible` imbalance type. /// /// This function facilitates imbalance conversions within the implementations of diff --git a/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs b/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs index c3b213cc8fc..349d9d7c65e 100644 --- a/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs +++ b/substrate/frame/support/src/traits/tokens/fungibles/imbalance.rs @@ -24,7 +24,10 @@ use super::*; use crate::traits::{ fungible, misc::{SameOrOther, TryDrop}, - tokens::{imbalance::Imbalance as ImbalanceT, AssetId, Balance}, + tokens::{ + imbalance::{Imbalance as ImbalanceT, TryMerge}, + AssetId, Balance, + }, }; use core::marker::PhantomData; use frame_support_procedural::{EqNoBound, PartialEqNoBound, RuntimeDebugNoBound}; @@ -176,6 +179,18 @@ impl< } } +impl< + A: AssetId, + B: Balance, + OnDrop: HandleImbalanceDrop<A, B>, + OppositeOnDrop: HandleImbalanceDrop<A, B>, + > TryMerge for Imbalance<A, B, OnDrop, OppositeOnDrop> +{ + fn try_merge(self, other: Self) -> Result<Self, (Self, Self)> { + self.merge(other) + } +} + /// Converts a `fungible` `imbalance` instance to an instance of a `fungibles` imbalance type using /// a specified `asset`. /// diff --git a/substrate/frame/support/src/traits/tokens/imbalance.rs b/substrate/frame/support/src/traits/tokens/imbalance.rs index 8eb9b355a4c..ee0d7a81c36 100644 --- a/substrate/frame/support/src/traits/tokens/imbalance.rs +++ b/substrate/frame/support/src/traits/tokens/imbalance.rs @@ -58,7 +58,7 @@ pub use split_two_ways::SplitTwoWays; /// /// You can always retrieve the raw balance value using `peek`. #[must_use] -pub trait Imbalance<Balance>: Sized + TryDrop + Default { +pub trait Imbalance<Balance>: Sized + TryDrop + Default + TryMerge { /// The oppositely imbalanced type. They come in pairs. type Opposite: Imbalance<Balance>; @@ -182,6 +182,13 @@ pub trait Imbalance<Balance>: Sized + TryDrop + Default { fn peek(&self) -> Balance; } +/// Try to merge two imbalances. +pub trait TryMerge: Sized { + /// Consume `self` and an `other` to return a new instance that combines both. Errors with + /// Err(self, other) if the imbalances cannot be merged (e.g. imbalances of different assets). + fn try_merge(self, other: Self) -> Result<Self, (Self, Self)>; +} + #[cfg(feature = "std")] impl<Balance: Default> Imbalance<Balance> for () { type Opposite = (); @@ -236,3 +243,10 @@ impl<Balance: Default> Imbalance<Balance> for () { Default::default() } } + +#[cfg(feature = "std")] +impl TryMerge for () { + fn try_merge(self, _: Self) -> Result<Self, (Self, Self)> { + Ok(()) + } +} diff --git a/substrate/frame/support/src/traits/tokens/imbalance/on_unbalanced.rs b/substrate/frame/support/src/traits/tokens/imbalance/on_unbalanced.rs index 4bf9af3fbb1..461fd203668 100644 --- a/substrate/frame/support/src/traits/tokens/imbalance/on_unbalanced.rs +++ b/substrate/frame/support/src/traits/tokens/imbalance/on_unbalanced.rs @@ -35,11 +35,26 @@ pub trait OnUnbalanced<Imbalance: TryDrop> { /// Handler for some imbalances. The different imbalances might have different origins or /// meanings, dependent on the context. Will default to simply calling on_unbalanced for all /// of them. Infallible. - fn on_unbalanceds<B>(amounts: impl Iterator<Item = Imbalance>) + fn on_unbalanceds(mut amounts: impl Iterator<Item = Imbalance>) where - Imbalance: crate::traits::Imbalance<B>, + Imbalance: crate::traits::tokens::imbalance::TryMerge, { - Self::on_unbalanced(amounts.fold(Imbalance::zero(), |i, x| x.merge(i))) + let mut sum: Option<Imbalance> = None; + while let Some(next) = amounts.next() { + sum = match sum { + Some(sum) => match sum.try_merge(next) { + Ok(sum) => Some(sum), + Err((sum, next)) => { + Self::on_unbalanced(next); + Some(sum) + }, + }, + None => Some(next), + } + } + if let Some(sum) = sum { + Self::on_unbalanced(sum) + } } /// Handler for some imbalance. Infallible. diff --git a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs index 3f8c7bc0ea3..245900760de 100644 --- a/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/asset-conversion-tx-payment/src/mock.rs @@ -131,7 +131,7 @@ pub struct DealWithFees; impl OnUnbalanced<fungible::Credit<<Runtime as frame_system::Config>::AccountId, Balances>> for DealWithFees { - fn on_unbalanceds<B>( + fn on_unbalanceds( mut fees_then_tips: impl Iterator< Item = fungible::Credit<<Runtime as frame_system::Config>::AccountId, Balances>, >, diff --git a/substrate/frame/transaction-payment/src/mock.rs b/substrate/frame/transaction-payment/src/mock.rs index fa61572e983..8767024ee23 100644 --- a/substrate/frame/transaction-payment/src/mock.rs +++ b/substrate/frame/transaction-payment/src/mock.rs @@ -105,7 +105,7 @@ pub struct DealWithFees; impl OnUnbalanced<fungible::Credit<<Runtime as frame_system::Config>::AccountId, Balances>> for DealWithFees { - fn on_unbalanceds<B>( + fn on_unbalanceds( mut fees_then_tips: impl Iterator< Item = fungible::Credit<<Runtime as frame_system::Config>::AccountId, Balances>, >, -- GitLab