Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Inactive balance tracking in Assets pallet #1747

Open
wants to merge 21 commits into
base: master
Choose a base branch
from

Conversation

muraca
Copy link
Contributor

@muraca muraca commented Sep 28, 2023

The trait fungibles::Unbalanced provides methods to increase and decrease active issuance.
This PR addresses the lack of this feature in Assets, by introducing a new field inactive in the Asset struct and implementing the methods accordingly.

closes #228

Polkadot address: 12poSUQPtcF1HUPQGY3zZu2P8emuW9YnsPduA4XG3oCEfJVp

substrate/frame/assets/src/impl_fungibles.rs Outdated Show resolved Hide resolved
substrate/frame/assets/src/impl_fungibles.rs Outdated Show resolved Hide resolved
substrate/frame/assets/src/migration/mod.rs Outdated Show resolved Hide resolved
substrate/frame/assets/src/migration/v1.rs Outdated Show resolved Hide resolved
substrate/frame/assets/src/migration/v2.rs Outdated Show resolved Hide resolved
substrate/frame/assets/src/migration/v2.rs Outdated Show resolved Hide resolved
substrate/frame/assets/src/migration/v2.rs Outdated Show resolved Hide resolved
Comment on lines +1833 to +1841
// Define a constant Vec for migration.
// This is a workaround for the fact that we can't implement Get for Vec.
struct ConstVecForMigrationToV2;
impl Get<Vec<(u32, u64)>> for ConstVecForMigrationToV2 {
fn get() -> Vec<(u32, u64)> {
// AssetId 1, AccountId 2 will be inactive.
vec![(1, 2)]
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you just define a constant vector in the migrations file the same as it would be defined in the runtime?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also how do we calculate what this vec should be when we go to run this for real?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pattern is not something I came up with, rather a solution I found in balances, introduced by @gavofyork in Substrate PR 12832, and used again later in society, introduced in Substrate PR 11324 - not sure on which one was written first, since society was merged later but was opened before.

In the latter, I see the migration has an internal method from_original, which is the one called in the test instead of building a struct as I did. Should I proceed with the same approach?

I can try to search how this pattern is used in runtimes if needed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may be missing something but I'm a bit confused actually why any accounts will need to be seeded with inactive balance when we merge this PR.

Isn't the only way their bal can be made inactive is by calling deactivate, which we are introducing in this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When introducing the inactive balance feature, one might want to populate the state during the migration, by passing a list of accounts which balances are to be considered inactive.

On pallet balances I found:

// Generally this will be used for the XCM teleport checking account.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@KiChjang does xcm need this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

XCM needs this but I don't think Assets does, at least to the extent the pallet is used in Asset Hub. AH doesn't trust any other chains as teleporters for its locally created assets, and for foreign assets it does not maintain a checking account (it expects the asset creator to).

Copy link
Contributor

@liamaharon liamaharon Oct 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @joepetrowski. @muraca let's leave it in just in case there's a use case outside of AH.

@paritytech-ci paritytech-ci requested a review from a team October 26, 2023 07:21
Copy link
Contributor

@juangirini juangirini left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Please bear in mind that this might not be merged before MBM is ready #1781.
In which case you will need to adapt the migration, I will approve it afterwards.

Also please confirm that you have tested this with try-runtime

@paritytech-ci paritytech-ci requested a review from a team October 27, 2023 09:44
@muraca
Copy link
Contributor Author

muraca commented Oct 28, 2023

I tested with try-runtime on Asset Hub Rococo and it works as expected.
To reproduce:

  • Create the snapshot at block 3_075_000
try-runtime \
  --runtime existing \
  create-snapshot block.snap \
  --uri wss://rococo-asset-hub-rpc.polkadot.io:443 \
  --at 0xed9b3c06621f649106eef7c6e6fd53ffceca2cfba0cf3039993b7ce50626ca4a
  • Apply try-runtime-assets-v2.patch, which includes the missing runtime upgrades for other pallets, and the v1 to v2 runtime upgrade for pallet assets. That the pallet assets storage version is moved back to 1 to let the first migration go smoothly.

  • Build the new runtime.

cargo build -p asset-hub-rococo-runtime --features try-runtime --release
  • Run the runtime upgrade.
try-runtime \
  --runtime target/release/wbuild/asset-hub-rococo-runtime/asset_hub_rococo_runtime.wasm \
  on-runtime-upgrade \
  snap -p block.snap

The expected output is the following:
image
As you can see, migration v1 to v2 goes smoothly. The error is wanted because the pallet storage version to v1 otherwise would fail, and we would not be able to perform the v1 to v2 migration.


P.S. in the patch, there's an upgrade to uniques' migration to v1, which was not using the OnRuntimeUpgrade traits. If you think it is worth to be pushed, I can open another PR. @liamaharon

@liamaharon
Copy link
Contributor

The error is wanted because the pallet storage version to v1 otherwise would fail, and we would not be able to perform the v1 to v2 migration.

Shouldn't the current storage versions be set to v2?

P.S. in the patch, there's an upgrade to uniques' migration to v1, which was not using the OnRuntimeUpgrade traits. If you think it is worth to be pushed, I can open another PR. @liamaharon

Not sure what you mean here, could you link to some code?

@muraca
Copy link
Contributor Author

muraca commented Oct 30, 2023

@liamaharon the changes I mentioned are in the patch file I linked:

Apply try-runtime-assets-v2.patch

To answer your question

Shouldn't the current storage versions be set to v2?

The actual on-chain storage version is currently 0, and migration to v2 requires version 1. For this reason, we need to perform a migration from 0 to 1 first.
If we set the current storage version to 2, the first migration will fail, so in the patch I created for this test we set it to 1.
Then when we perform the migration to v2, it works as expected, but the final check made by some procedural macro will fail and produce the output you see in the screenshot.

@liamaharon
Copy link
Contributor

liamaharon commented Oct 30, 2023

If we set the current storage version to 2, the first migration will fail

This is due to poorly written pre/post_upgrade checks in v0 -> v1 migration. you should remove the current version checks.

started working on migration

Signed-off-by: muraca <mmuraca247@gmail.com>
Signed-off-by: muraca <mmuraca247@gmail.com>
Signed-off-by: muraca <mmuraca247@gmail.com>
Signed-off-by: muraca <mmuraca247@gmail.com>
Signed-off-by: muraca <mmuraca247@gmail.com>
Signed-off-by: muraca <mmuraca247@gmail.com>
Signed-off-by: muraca <mmuraca247@gmail.com>
Signed-off-by: muraca <mmuraca247@gmail.com>
Signed-off-by: muraca <mmuraca247@gmail.com>
Signed-off-by: muraca <mmuraca247@gmail.com>
Signed-off-by: muraca <mmuraca247@gmail.com>
Signed-off-by: muraca <mmuraca247@gmail.com>
Signed-off-by: muraca <mmuraca247@gmail.com>
@bkchr
Copy link
Member

bkchr commented Feb 5, 2024

@muraca could you merge master?

@liamaharon could you give this another round of reviews?

@muraca muraca requested a review from a team as a code owner February 5, 2024 18:24
muraca and others added 7 commits March 7, 2024 10:18
@paritytech-cicd-pr
Copy link

The CI pipeline was cancelled due to failure one of the required jobs.
Job name: cargo-clippy
Logs: https://gitlab.parity.io/parity/mirrors/polkadot-sdk/-/jobs/5928578

Comment on lines +170 to +171
// Inactive amount can't exceed supply
asset.inactive = asset.inactive.saturating_add(amount).max(asset.supply);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems this be a defensive check if the runtime is trying to set the inactive amt > total supply, probably indicates a bug?


fn reactivate(asset: Self::AssetId, amount: Self::Balance) {
Asset::<T, I>::mutate(&asset, |maybe_asset| match maybe_asset {
Some(ref mut asset) => asset.inactive.saturating_reduce(amount),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be defensive in case > total inactive amount is attempted to be reactivated?

@@ -218,8 +218,8 @@ pub mod pallet {
};
use frame_system::pallet_prelude::*;

/// The in-code storage version.
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
/// The current storage version.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// The current storage version.
/// The in-code storage version.

}

pub struct MigrateToV1<T, I>(core::marker::PhantomData<(T, I)>);
impl<T: Config<I>, I: 'static> OnRuntimeUpgrade for MigrateToV1<T, I> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can use VersionedMigration for this and avoid doing version checks/sets yourself

Comment on lines +107 to +114
let mut translated = 0u64;
Asset::<T, I>::translate::<
old::AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>,
_,
>(|_key, old_value| {
translated.saturating_inc();
Some(old_value.migrate_to_v1())
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm is there a bound on the number of Assets that could exist for a runtime? Otherwise we may need to write this as an MBM.

Someone could potentially spam the chain with thousands of dummy Assets right before the migration is scheduled to take place, which would cause the migration block PoV size to be too large and brick the chain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T2-pallets This PR/Issue is related to a particular pallet.
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

Assets: Implement Inactive balance tracking in Assets pallet
6 participants