Skip to content

Commit

Permalink
Generic slashing side-effects (#5623)
Browse files Browse the repository at this point in the history
# Description
## What?
Make it possible for other pallets to implement their own logic when a
slash on a balance occurs.

## Why?
In the [introduction of
holds](paritytech/substrate#12951) @gavofyork
said:
> Since Holds are designed to be infallibly slashed, this means that any
logic using a Freeze must handle the possibility of the frozen amount
being reduced, potentially to zero. A permissionless function should be
provided in order to allow bookkeeping to be updated in this instance.

At Polimec we needed to find a way to reduce the vesting schedules of
our users after a slash was made, and after talking to @kianenigma at
the Web3Summit, we realized there was no easy way to implement this with
the current traits, so we came up with this solution.



## How?
- First we abstract the `done_slash` function of holds::Balanced to it's
own trait that any pallet can implement.
- Then we add a config type in pallet-balances that accepts a callback
tuple of all the pallets that implement this trait.
- Finally implement done_slash for pallet-balances such that it calls
the config type.

## Integration
The default implementation of done_slash is still an empty function, and
the new config type of pallet-balances can be set to an empty tuple, so
nothing changes by default.

## Review Notes
- I suggest to focus on the first commit which contains the main logic
changes.
- I also have a working implementation of done_slash for pallet_vesting,
should I add it to this PR?
- If I run `cargo +nightly fmt --all` then I get changes to a lot of
unrelated crates, so not sure if I should run it to avoid the fmt
failure of the CI
- Should I hunt down references to fungible/fungibles documentation and
update it accordingly?

**Polkadot address:** `15fj1UhQp8Xes7y7LSmDYTy349mXvUwrbNmLaP5tQKBxsQY1`

# Checklist

* [x] My PR includes a detailed description as outlined in the
"Description" and its two subsections above.
* [x] My PR follows the [labeling requirements](

https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/CONTRIBUTING.md#Process
) of this project (at minimum one label for `T` required)
* External contributors: ask maintainers to put the right label on your
PR.
* [ ] I have made corresponding changes to the documentation (if
applicable)

---------

Co-authored-by: Kian Paimani <[email protected]>
Co-authored-by: command-bot <>
Co-authored-by: Francisco Aguirre <[email protected]>
  • Loading branch information
3 people authored Oct 7, 2024
1 parent 4b356c4 commit c0ddfba
Show file tree
Hide file tree
Showing 36 changed files with 255 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ impl pallet_balances::Config for Runtime {
type RuntimeFreezeReason = RuntimeFreezeReason;
type FreezeIdentifier = ();
type MaxFreezes = ConstU32<0>;
type DoneSlashHandler = ();
}

parameter_types! {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ impl pallet_balances::Config for Runtime {
type RuntimeFreezeReason = RuntimeFreezeReason;
type FreezeIdentifier = ();
type MaxFreezes = ConstU32<0>;
type DoneSlashHandler = ();
}

parameter_types! {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ impl pallet_balances::Config for Runtime {
type RuntimeFreezeReason = RuntimeFreezeReason;
type FreezeIdentifier = ();
type MaxFreezes = ConstU32<0>;
type DoneSlashHandler = ();
}

parameter_types! {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ impl pallet_balances::Config for Runtime {
type RuntimeFreezeReason = RuntimeFreezeReason;
type FreezeIdentifier = ();
type MaxFreezes = ConstU32<0>;
type DoneSlashHandler = ();
}

parameter_types! {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ impl pallet_balances::Config for Runtime {
type RuntimeFreezeReason = RuntimeFreezeReason;
type FreezeIdentifier = ();
type MaxFreezes = ConstU32<0>;
type DoneSlashHandler = ();
}

parameter_types! {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ impl pallet_balances::Config for Runtime {
type RuntimeFreezeReason = RuntimeFreezeReason;
type FreezeIdentifier = ();
type MaxFreezes = ConstU32<0>;
type DoneSlashHandler = ();
}

parameter_types! {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ impl pallet_balances::Config for Runtime {
type RuntimeFreezeReason = RuntimeFreezeReason;
type FreezeIdentifier = ();
type MaxFreezes = ConstU32<0>;
type DoneSlashHandler = ();
}

parameter_types! {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ impl pallet_balances::Config for Runtime {
type RuntimeFreezeReason = RuntimeFreezeReason;
type FreezeIdentifier = ();
type MaxFreezes = ConstU32<0>;
type DoneSlashHandler = ();
}

parameter_types! {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ impl pallet_balances::Config for Runtime {
type RuntimeHoldReason = RuntimeHoldReason;
type FreezeIdentifier = ();
type MaxFreezes = ConstU32<0>;
type DoneSlashHandler = ();
}

parameter_types! {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ impl pallet_balances::Config for Runtime {
type RuntimeHoldReason = RuntimeHoldReason;
type FreezeIdentifier = ();
type MaxFreezes = ConstU32<0>;
type DoneSlashHandler = ();
}

parameter_types! {
Expand Down
1 change: 1 addition & 0 deletions cumulus/parachains/runtimes/testing/penpal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ impl pallet_balances::Config for Runtime {
type RuntimeFreezeReason = RuntimeFreezeReason;
type FreezeIdentifier = ();
type MaxFreezes = ConstU32<0>;
type DoneSlashHandler = ();
}

parameter_types! {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ impl pallet_balances::Config for Runtime {
type RuntimeFreezeReason = RuntimeFreezeReason;
type FreezeIdentifier = ();
type MaxFreezes = ConstU32<0>;
type DoneSlashHandler = ();
}

impl pallet_transaction_payment::Config for Runtime {
Expand Down
1 change: 1 addition & 0 deletions cumulus/test/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ impl pallet_balances::Config for Runtime {
type RuntimeFreezeReason = RuntimeFreezeReason;
type FreezeIdentifier = ();
type MaxFreezes = ConstU32<0>;
type DoneSlashHandler = ();
}

impl pallet_transaction_payment::Config for Runtime {
Expand Down
2 changes: 2 additions & 0 deletions polkadot/runtime/rococo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ impl pallet_balances::Config for Runtime {
type RuntimeHoldReason = RuntimeHoldReason;
type RuntimeFreezeReason = RuntimeFreezeReason;
type MaxFreezes = ConstU32<1>;
type DoneSlashHandler = ();
}

parameter_types! {
Expand Down Expand Up @@ -1234,6 +1235,7 @@ impl pallet_balances::Config<NisCounterpartInstance> for Runtime {
type RuntimeFreezeReason = RuntimeFreezeReason;
type FreezeIdentifier = ();
type MaxFreezes = ConstU32<1>;
type DoneSlashHandler = ();
}

parameter_types! {
Expand Down
1 change: 1 addition & 0 deletions polkadot/runtime/test-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ impl pallet_balances::Config for Runtime {
type RuntimeFreezeReason = RuntimeFreezeReason;
type FreezeIdentifier = ();
type MaxFreezes = ConstU32<0>;
type DoneSlashHandler = ();
}

parameter_types! {
Expand Down
1 change: 1 addition & 0 deletions polkadot/runtime/westend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ impl pallet_balances::Config for Runtime {
type RuntimeFreezeReason = RuntimeFreezeReason;
type FreezeIdentifier = RuntimeFreezeReason;
type MaxFreezes = VariantCountOf<RuntimeFreezeReason>;
type DoneSlashHandler = ();
}

parameter_types! {
Expand Down
1 change: 1 addition & 0 deletions polkadot/xcm/xcm-builder/src/tests/pay/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ impl pallet_balances::Config for Test {
type RuntimeFreezeReason = RuntimeFreezeReason;
type FreezeIdentifier = ();
type MaxFreezes = ConstU32<0>;
type DoneSlashHandler = ();
}

parameter_types! {
Expand Down
89 changes: 89 additions & 0 deletions prdoc/pr_5623.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json

title: Generic slashing side-effects

doc:
- audience: Runtime Dev
description: |
What?
Make it possible for other pallets to implement their own logic when a slash on a balance occurs.

How?
First we abstract the done_slash function of holds::Balanced to it's own trait that any pallet can implement.
Then we add a config type in pallet-balances that accepts a callback tuple of all the pallets that implement this trait.
Finally implement done_slash for pallet-balances such that it calls the config type.
Integration
The default implementation of done_slash is still an empty function, and the new config type of pallet-balances can be set to an empty tuple, so nothing changes by default.

crates:
- name: frame-support
bump: major

- name: pallet-balances
bump: major

- name: pallet-broker
bump: minor

- name: rococo-runtime
bump: minor

- name: pallet-nis
bump: minor

- name: westend-runtime
bump: minor

- name: pallet-assets-freezer
bump: minor

- name: pallet-contracts-mock-network
bump: minor

- name: pallet-revive-mock-network
bump: minor

- name: asset-hub-rococo-runtime
bump: minor

- name: asset-hub-westend-runtime
bump: minor

- name: bridge-hub-rococo-runtime
bump: minor

- name: bridge-hub-westend-runtime
bump: minor

- name: collectives-westend-runtime
bump: minor

- name: coretime-rococo-runtime
bump: minor

- name: coretime-westend-runtime
bump: minor

- name: people-rococo-runtime
bump: minor

- name: people-westend-runtime
bump: minor

- name: penpal-runtime
bump: minor

- name: contracts-rococo-runtime
bump: minor

- name: rococo-parachain-runtime
bump: minor

- name: staging-xcm-builder
bump: minor

- name: polkadot-sdk
bump: minor


1 change: 1 addition & 0 deletions substrate/bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,7 @@ impl pallet_balances::Config for Runtime {
type WeightInfo = pallet_balances::weights::SubstrateWeight<Runtime>;
type FreezeIdentifier = RuntimeFreezeReason;
type MaxFreezes = VariantCountOf<RuntimeFreezeReason>;
type DoneSlashHandler = ();
}

parameter_types! {
Expand Down
1 change: 1 addition & 0 deletions substrate/frame/assets-freezer/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ impl pallet_balances::Config for Test {
type MaxFreezes = ();
type RuntimeHoldReason = ();
type RuntimeFreezeReason = ();
type DoneSlashHandler = ();
}

impl pallet_assets::Config for Test {
Expand Down
8 changes: 8 additions & 0 deletions substrate/frame/balances/src/impl_fungible.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,14 @@ impl<T: Config<I>, I: 'static> fungible::Balanced<T::AccountId> for Pallet<T, I>

impl<T: Config<I>, I: 'static> fungible::BalancedHold<T::AccountId> for Pallet<T, I> {}

impl<T: Config<I>, I: 'static>
fungible::hold::DoneSlash<T::RuntimeHoldReason, T::AccountId, T::Balance> for Pallet<T, I>
{
fn done_slash(reason: &T::RuntimeHoldReason, who: &T::AccountId, amount: T::Balance) {
T::DoneSlashHandler::done_slash(reason, who, amount);
}
}

impl<T: Config<I>, I: 'static> AccountTouch<(), T::AccountId> for Pallet<T, I> {
type Balance = T::Balance;
fn deposit_required(_: ()) -> Self::Balance {
Expand Down
9 changes: 9 additions & 0 deletions substrate/frame/balances/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ pub mod pallet {
type MaxFreezes = VariantCountOf<Self::RuntimeFreezeReason>;

type WeightInfo = ();
type DoneSlashHandler = ();
}
}

Expand Down Expand Up @@ -312,6 +313,14 @@ pub mod pallet {
/// The maximum number of individual freeze locks that can exist on an account at any time.
#[pallet::constant]
type MaxFreezes: Get<u32>;

/// Allows callbacks to other pallets so they can update their bookkeeping when a slash
/// occurs.
type DoneSlashHandler: fungible::hold::DoneSlash<
Self::RuntimeHoldReason,
Self::AccountId,
Self::Balance,
>;
}

/// The in-code storage version.
Expand Down
14 changes: 14 additions & 0 deletions substrate/frame/broker/src/test_fungibles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,20 @@ where
{
}

impl<
Instance: Get<u32>,
AccountId: Encode,
AssetId: tokens::AssetId + Copy,
MinimumBalance: TypedGet,
HoldReason: Encode + Decode + TypeInfo + 'static,
Balance: tokens::Balance,
> fungibles::hold::DoneSlash<AssetId, HoldReason, AccountId, Balance>
for TestFungibles<Instance, AccountId, AssetId, MinimumBalance, HoldReason>
where
MinimumBalance::Type: tokens::Balance,
{
}

impl<
Instance: Get<u32>,
AccountId: Encode,
Expand Down
1 change: 1 addition & 0 deletions substrate/frame/contracts/mock-network/src/parachain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ impl pallet_balances::Config for Runtime {
type RuntimeHoldReason = RuntimeHoldReason;
type RuntimeFreezeReason = RuntimeFreezeReason;
type WeightInfo = ();
type DoneSlashHandler = ();
}

parameter_types! {
Expand Down
1 change: 1 addition & 0 deletions substrate/frame/contracts/mock-network/src/relay_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ impl pallet_balances::Config for Runtime {
type MaxFreezes = ConstU32<0>;
type RuntimeHoldReason = RuntimeHoldReason;
type RuntimeFreezeReason = RuntimeFreezeReason;
type DoneSlashHandler = ();
}

impl shared::Config for Runtime {
Expand Down
2 changes: 2 additions & 0 deletions substrate/frame/nis/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ impl pallet_balances::Config<Instance1> for Test {
type MaxFreezes = ();
type RuntimeHoldReason = RuntimeHoldReason;
type RuntimeFreezeReason = RuntimeFreezeReason;
type DoneSlashHandler = ();
}

impl pallet_balances::Config<Instance2> for Test {
Expand All @@ -84,6 +85,7 @@ impl pallet_balances::Config<Instance2> for Test {
type MaxFreezes = ();
type RuntimeHoldReason = ();
type RuntimeFreezeReason = ();
type DoneSlashHandler = ();
}

parameter_types! {
Expand Down
1 change: 1 addition & 0 deletions substrate/frame/revive/mock-network/src/parachain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ impl pallet_balances::Config for Runtime {
type RuntimeHoldReason = RuntimeHoldReason;
type RuntimeFreezeReason = RuntimeFreezeReason;
type WeightInfo = ();
type DoneSlashHandler = ();
}

parameter_types! {
Expand Down
1 change: 1 addition & 0 deletions substrate/frame/revive/mock-network/src/relay_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ impl pallet_balances::Config for Runtime {
type MaxFreezes = ConstU32<0>;
type RuntimeHoldReason = RuntimeHoldReason;
type RuntimeFreezeReason = RuntimeFreezeReason;
type DoneSlashHandler = ();
}

impl shared::Config for Runtime {
Expand Down
18 changes: 16 additions & 2 deletions substrate/frame/support/src/traits/tokens/fungible/hold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,11 @@ pub trait Mutate<AccountId>:
}

/// Trait for slashing a fungible asset which can be place on hold.
pub trait Balanced<AccountId>: super::Balanced<AccountId> + Unbalanced<AccountId> {
pub trait Balanced<AccountId>:
super::Balanced<AccountId>
+ Unbalanced<AccountId>
+ DoneSlash<Self::Reason, AccountId, Self::Balance>
{
/// Reduce the balance of some funds on hold in an account.
///
/// The resulting imbalance is the first item of the tuple returned.
Expand All @@ -449,6 +453,16 @@ pub trait Balanced<AccountId>: super::Balanced<AccountId> + Unbalanced<AccountId
Self::done_slash(reason, who, decrease);
(credit, amount.saturating_sub(decrease))
}
}

fn done_slash(_reason: &Self::Reason, _who: &AccountId, _amount: Self::Balance) {}
/// Trait for optional bookkeeping callbacks after a slash.
pub trait DoneSlash<Reason, AccountId, Balance> {
fn done_slash(_reason: &Reason, _who: &AccountId, _amount: Balance) {}
}

#[impl_trait_for_tuples::impl_for_tuples(30)]
impl<Reason, AccountId, Balance: Copy> DoneSlash<Reason, AccountId, Balance> for Tuple {
fn done_slash(reason: &Reason, who: &AccountId, amount: Balance) {
for_tuples!( #( Tuple::done_slash(reason, who, amount); )* );
}
}
Loading

0 comments on commit c0ddfba

Please sign in to comment.