mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 20:21:06 +00:00
Asset Pallet: Support repeated destroys to safely destroy large assets (#12310)
* Support repeated destroys to safely destroy large assets * require freezing accounts before destroying * support only deleting asset as final stage when there's no assets left * pre: introduce the RemoveKeyLimit config parameter * debug_ensure empty account in the right if block * update to having separate max values for accounts and approvals * add tests and use RemoveKeyLimit constant * add useful comments to the extrinsics, and calculate returned weight * add benchmarking for start_destroy and finish destroy * push failing benchmark logic * add benchmark tests for new functions * update weights via local benchmarks * remove extra weight file * Update frame/assets/src/lib.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/assets/src/types.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * Update frame/assets/src/lib.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * effect some changes from codereview * use NotFrozen error * remove origin checks, as anyone can complete destruction after owner has begun the process; Add live check for other extrinsics * fix comments about Origin behaviour * add AssetStatus docs * modularize logic to allow calling logic in on_idle and on_initialize hooks * introduce simple migration for assets details * reintroduce logging in the migrations * move deposit_Event out of the mutate block * Update frame/assets/src/functions.rs Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com> * Update frame/assets/src/migration.rs Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com> * move AssetNotLive checkout out of the mutate blocks * rename RemoveKeysLimit to RemoveItemsLimit * update docs * fix event name in benchmark * fix cargo fmt. * fix lint in benchmarking * Empty commit to trigger CI * Update frame/assets/src/lib.rs Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Update frame/assets/src/lib.rs Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Update frame/assets/src/functions.rs Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Update frame/assets/src/functions.rs Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Update frame/assets/src/functions.rs Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Update frame/assets/src/lib.rs Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Update frame/assets/src/functions.rs Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * effect change suggested during code review * move limit to a single location * Update frame/assets/src/functions.rs Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> * rename events * fix weight typo, using rocksdb instead of T::DbWeight. Pending generating weights * switch to using dead_account.len() * rename event in the benchmarks * empty to retrigger CI * trigger CI to check cumulus dependency * trigger CI for dependent cumulus * Update frame/assets/src/migration.rs Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * move is-frozen to the assetStatus enum (#12547) * add pre and post migration hooks * update do_transfer logic to add new assert for more correct error messages * trigger CI * switch checking AssetStatus from checking Destroying state to checking live state * fix error type in tests from Frozen to AssetNotLive * trigger CI * change ensure check for fn reducible_balance() * change the error type to Error:<T,I>::IncorrectStatus to be clearer * Trigger CI Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> Co-authored-by: parity-processbot <> Co-authored-by: Muharem Ismailov <ismailov.m.h@gmail.com> Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
This commit is contained in:
@@ -328,8 +328,12 @@ fn lifecycle_should_work() {
|
||||
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 20, 100));
|
||||
assert_eq!(Account::<Test>::iter_prefix(0).count(), 2);
|
||||
|
||||
let w = Asset::<Test>::get(0).unwrap().destroy_witness();
|
||||
assert_ok!(Assets::destroy(RuntimeOrigin::signed(1), 0, w));
|
||||
assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::destroy_accounts(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::destroy_approvals(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::finish_destroy(RuntimeOrigin::signed(1), 0));
|
||||
|
||||
assert_eq!(Balances::reserved_balance(&1), 0);
|
||||
|
||||
assert!(!Asset::<Test>::contains_key(0));
|
||||
@@ -348,8 +352,12 @@ fn lifecycle_should_work() {
|
||||
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 20, 100));
|
||||
assert_eq!(Account::<Test>::iter_prefix(0).count(), 2);
|
||||
|
||||
let w = Asset::<Test>::get(0).unwrap().destroy_witness();
|
||||
assert_ok!(Assets::destroy(RuntimeOrigin::root(), 0, w));
|
||||
assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::destroy_accounts(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::destroy_approvals(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::finish_destroy(RuntimeOrigin::signed(1), 0));
|
||||
|
||||
assert_eq!(Balances::reserved_balance(&1), 0);
|
||||
|
||||
assert!(!Asset::<Test>::contains_key(0));
|
||||
@@ -358,24 +366,6 @@ fn lifecycle_should_work() {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn destroy_with_bad_witness_should_not_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
Balances::make_free_balance_be(&1, 100);
|
||||
assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1));
|
||||
let mut w = Asset::<Test>::get(0).unwrap().destroy_witness();
|
||||
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 10, 100));
|
||||
assert_eq!(asset_ids(), vec![0, 999]);
|
||||
// witness too low
|
||||
assert_noop!(Assets::destroy(RuntimeOrigin::signed(1), 0, w), Error::<Test>::BadWitness);
|
||||
// witness too high is okay though
|
||||
w.accounts += 2;
|
||||
w.sufficients += 2;
|
||||
assert_ok!(Assets::destroy(RuntimeOrigin::signed(1), 0, w));
|
||||
assert_eq!(asset_ids(), vec![999]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn destroy_should_refund_approvals() {
|
||||
new_test_ext().execute_with(|| {
|
||||
@@ -388,8 +378,13 @@ fn destroy_should_refund_approvals() {
|
||||
assert_eq!(Balances::reserved_balance(&1), 3);
|
||||
assert_eq!(asset_ids(), vec![0, 999]);
|
||||
|
||||
let w = Asset::<Test>::get(0).unwrap().destroy_witness();
|
||||
assert_ok!(Assets::destroy(RuntimeOrigin::signed(1), 0, w));
|
||||
assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(1), 0));
|
||||
|
||||
assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::destroy_accounts(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::destroy_approvals(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::finish_destroy(RuntimeOrigin::signed(1), 0));
|
||||
|
||||
assert_eq!(Balances::reserved_balance(&1), 0);
|
||||
assert_eq!(asset_ids(), vec![999]);
|
||||
|
||||
@@ -398,6 +393,58 @@ fn destroy_should_refund_approvals() {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_destroy_should_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1));
|
||||
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 10));
|
||||
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 2, 10));
|
||||
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 3, 10));
|
||||
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 4, 10));
|
||||
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 5, 10));
|
||||
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 6, 10));
|
||||
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 7, 10));
|
||||
assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(1), 0));
|
||||
|
||||
assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::destroy_accounts(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::destroy_approvals(RuntimeOrigin::signed(1), 0));
|
||||
// Asset is in use, as all the accounts have not yet been destroyed.
|
||||
// We need to call destroy_accounts or destroy_approvals again until asset is completely
|
||||
// cleaned up.
|
||||
assert_noop!(Assets::finish_destroy(RuntimeOrigin::signed(1), 0), Error::<Test>::InUse);
|
||||
|
||||
System::assert_has_event(RuntimeEvent::Assets(crate::Event::AccountsDestroyed {
|
||||
asset_id: 0,
|
||||
accounts_destroyed: 5,
|
||||
accounts_remaining: 2,
|
||||
}));
|
||||
System::assert_has_event(RuntimeEvent::Assets(crate::Event::ApprovalsDestroyed {
|
||||
asset_id: 0,
|
||||
approvals_destroyed: 0,
|
||||
approvals_remaining: 0,
|
||||
}));
|
||||
// Partially destroyed Asset should continue to exist
|
||||
assert!(Asset::<Test>::contains_key(0));
|
||||
|
||||
// Second call to destroy on PartiallyDestroyed asset
|
||||
assert_ok!(Assets::destroy_accounts(RuntimeOrigin::signed(1), 0));
|
||||
System::assert_has_event(RuntimeEvent::Assets(crate::Event::AccountsDestroyed {
|
||||
asset_id: 0,
|
||||
accounts_destroyed: 2,
|
||||
accounts_remaining: 0,
|
||||
}));
|
||||
assert_ok!(Assets::destroy_approvals(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::destroy_approvals(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::finish_destroy(RuntimeOrigin::signed(1), 0));
|
||||
|
||||
System::assert_has_event(RuntimeEvent::Assets(crate::Event::Destroyed { asset_id: 0 }));
|
||||
|
||||
// Destroyed Asset should not exist
|
||||
assert!(!Asset::<Test>::contains_key(0));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_providing_should_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
@@ -540,7 +587,10 @@ fn transferring_frozen_asset_should_not_work() {
|
||||
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100));
|
||||
assert_eq!(Assets::balance(0, 1), 100);
|
||||
assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(1), 0));
|
||||
assert_noop!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50), Error::<Test>::Frozen);
|
||||
assert_noop!(
|
||||
Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50),
|
||||
Error::<Test>::AssetNotLive
|
||||
);
|
||||
assert_ok!(Assets::thaw_asset(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::transfer(RuntimeOrigin::signed(1), 0, 2, 50));
|
||||
});
|
||||
@@ -556,7 +606,7 @@ fn approve_transfer_frozen_asset_should_not_work() {
|
||||
assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(1), 0));
|
||||
assert_noop!(
|
||||
Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50),
|
||||
Error::<Test>::Frozen
|
||||
Error::<Test>::AssetNotLive
|
||||
);
|
||||
assert_ok!(Assets::thaw_asset(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::approve_transfer(RuntimeOrigin::signed(1), 0, 2, 50));
|
||||
@@ -590,8 +640,10 @@ fn origin_guards_should_work() {
|
||||
Assets::force_transfer(RuntimeOrigin::signed(2), 0, 1, 2, 100),
|
||||
Error::<Test>::NoPermission
|
||||
);
|
||||
let w = Asset::<Test>::get(0).unwrap().destroy_witness();
|
||||
assert_noop!(Assets::destroy(RuntimeOrigin::signed(2), 0, w), Error::<Test>::NoPermission);
|
||||
assert_noop!(
|
||||
Assets::start_destroy(RuntimeOrigin::signed(2), 0),
|
||||
Error::<Test>::NoPermission
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -803,22 +855,37 @@ fn set_metadata_should_work() {
|
||||
|
||||
/// Destroying an asset calls the `FrozenBalance::died` hooks of all accounts.
|
||||
#[test]
|
||||
fn destroy_calls_died_hooks() {
|
||||
fn destroy_accounts_calls_died_hooks() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 50));
|
||||
// Create account 1 and 2.
|
||||
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100));
|
||||
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 2, 100));
|
||||
// Destroy the asset.
|
||||
let w = Asset::<Test>::get(0).unwrap().destroy_witness();
|
||||
assert_ok!(Assets::destroy(RuntimeOrigin::signed(1), 0, w));
|
||||
// Destroy the accounts.
|
||||
assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::destroy_accounts(RuntimeOrigin::signed(1), 0));
|
||||
|
||||
// Asset is gone and accounts 1 and 2 died.
|
||||
assert!(Asset::<Test>::get(0).is_none());
|
||||
// Accounts 1 and 2 died.
|
||||
assert_eq!(hooks(), vec![Hook::Died(0, 1), Hook::Died(0, 2)]);
|
||||
})
|
||||
}
|
||||
|
||||
/// Destroying an asset calls the `FrozenBalance::died` hooks of all accounts.
|
||||
#[test]
|
||||
fn finish_destroy_asset_destroys_asset() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 50));
|
||||
// Destroy the accounts.
|
||||
assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0));
|
||||
assert_ok!(Assets::finish_destroy(RuntimeOrigin::signed(1), 0));
|
||||
|
||||
// Asset is gone
|
||||
assert!(Asset::<Test>::get(0).is_none());
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn freezer_should_work() {
|
||||
new_test_ext().execute_with(|| {
|
||||
|
||||
Reference in New Issue
Block a user