-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Pallet assets: new status LiveAndNoPrivilege and new call revoke_all_privilege #4150
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
base: master
Are you sure you want to change the base?
Changes from 11 commits
89ccc72
e286945
a716e0f
9430c24
f185216
ef36ecf
4f61356
31ebf61
1c8185d
fb0a90c
602eeab
18358ad
f7021bb
a2a3a26
e2315c6
27cf5a8
b5884a2
a9f51b5
250b321
4dc68c4
0dd982a
51ae60c
e2a3782
d3c2801
bc31d85
9176cd9
1aec7d5
0ac4845
bb1a490
43b61d8
c4f98f6
3cda448
615f5ae
c1d67aa
266cbb8
f146170
a76350b
e1853a6
7dd5394
f76b23f
3036e0c
3794a11
d6d83e2
6976d91
247c84a
a0c0f1f
85e4fcc
47d4bfd
69ee8b4
5962bb5
36a3d7d
a49b673
905f658
35fb7e2
c0929cb
5be3576
ec8f2d0
50634c9
6594d94
04bd5ae
24ab012
d0a8ab2
d029b29
0fbdeb3
5d30a4e
6b99f08
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| title: "pallet assets: Add the new status live and no privileges. and new call to revoke privileges" | ||
| topic: pallet assets | ||
|
|
||
|
|
||
| doc: | ||
| - audience: Runtime Dev | ||
| description: | | ||
| The new status "live and no privileges" (`LiveAndNoPrivileges`) is created. Under this | ||
| status, owner, issuer, admin and freezer has no privileges and metadata are frozen. | ||
| The new call `revoke_all_privileges` is created. This call allows `ForceOrigin` or the | ||
| `Owner` to revoke all the privileges on the asset. This operation is irrevesible for the | ||
| `Owner`, `ForceOrigin` can always reverse by using `force_asset_status`. | ||
|
|
||
| Storage is backward compatible so the migration consist of updating storage version | ||
| from V1 to V2. | ||
|
|
||
| `Config` trait has a new associated type `DepositDestinationOnRevocation` | ||
gui1117 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| This type is used for the destination of the deposit when privileges are revoked. | ||
| Set it to `()` to burn them. | ||
|
|
||
| - audience: Runtime User | ||
| description: | | ||
| The new status "live and no privileges" (`LiveAndNoPrivileges`) is created. Under this | ||
| status, owner, issuer, admin and freezer has no privileges and metadata are frozen. | ||
| The new call `revoke_all_privileges` is created. This call allows `ForceOrigin` or the | ||
| `Owner` to revoke all the privileges on the asset. This operation is irrevesible for the | ||
| `Owner`, `ForceOrigin` can always reverse by using `force_asset_status`. | ||
|
|
||
| crates: | ||
| - name: pallet-assets | ||
| bump: major | ||
|
|
||
| migrations: | ||
| runtime: | ||
| - reference: "pallet-assets: storage version 2" | ||
| description: "V2: update is backward compatible with V1, so only storage version 2 needs to be stored" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| [package] | ||
| name = "pallet-assets" | ||
| version = "29.1.0" | ||
| version = "30.0.0" | ||
|
||
| authors.workspace = true | ||
| edition.workspace = true | ||
| license = "Apache-2.0" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -216,7 +216,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { | |
| keep_alive: bool, | ||
| ) -> Result<T::Balance, DispatchError> { | ||
| let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?; | ||
| ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive); | ||
| Self::ensure_live_asset(&details)?; | ||
|
|
||
| let account = Account::<T, I>::get(&id, who).ok_or(Error::<T, I>::NoAccount)?; | ||
| ensure!(!account.status.is_frozen(), Error::<T, I>::Frozen); | ||
|
|
@@ -317,11 +317,13 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { | |
| ensure!(!Account::<T, I>::contains_key(&id, &who), Error::<T, I>::AlreadyExists); | ||
| let deposit = T::AssetAccountDeposit::get(); | ||
| let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?; | ||
| ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive); | ||
| ensure!( | ||
| !check_depositor || &depositor == &details.admin || &depositor == &details.freezer, | ||
| Error::<T, I>::NoPermission | ||
| ); | ||
| Self::ensure_live_asset(&details)?; | ||
| if check_depositor { | ||
| ensure!( | ||
| details.admin() == Some(&depositor) || details.freezer() == Some(&depositor), | ||
| Error::<T, I>::NoPermission | ||
| ); | ||
| } | ||
| let reason = Self::new_account(&who, &mut details, Some((&depositor, deposit)))?; | ||
| T::Currency::reserve(&depositor, deposit)?; | ||
| Asset::<T, I>::insert(&id, details); | ||
|
|
@@ -347,7 +349,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { | |
| let mut account = Account::<T, I>::get(&id, &who).ok_or(Error::<T, I>::NoDeposit)?; | ||
| ensure!(matches!(account.reason, Consumer | DepositHeld(..)), Error::<T, I>::NoDeposit); | ||
| let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?; | ||
| ensure!(matches!(details.status, Live | Frozen), Error::<T, I>::IncorrectStatus); | ||
| ensure!(details.status != Destroying, Error::<T, I>::DestroyingAsset); | ||
| ensure!(account.balance.is_zero() || allow_burn, Error::<T, I>::WouldBurn); | ||
|
|
||
| if let Some(deposit) = account.reason.take_deposit() { | ||
|
|
@@ -381,10 +383,13 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { | |
| let (depositor, deposit) = | ||
| account.reason.take_deposit_from().ok_or(Error::<T, I>::NoDeposit)?; | ||
| let mut details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?; | ||
| ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive); | ||
| Self::ensure_live_asset(&details)?; | ||
| ensure!(!account.status.is_frozen(), Error::<T, I>::Frozen); | ||
| if let Some(caller) = maybe_check_caller { | ||
| ensure!(caller == depositor || caller == details.admin, Error::<T, I>::NoPermission); | ||
| ensure!( | ||
| caller == depositor || Some(&caller) == details.admin(), | ||
| Error::<T, I>::NoPermission | ||
| ); | ||
| } | ||
| ensure!(account.balance.is_zero(), Error::<T, I>::WouldBurn); | ||
|
|
||
|
|
@@ -417,7 +422,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { | |
| ) -> DispatchResult { | ||
| Self::increase_balance(id.clone(), beneficiary, amount, |details| -> DispatchResult { | ||
| if let Some(check_issuer) = maybe_check_issuer { | ||
| ensure!(check_issuer == details.issuer, Error::<T, I>::NoPermission); | ||
| ensure!(details.issuer() == Some(&check_issuer), Error::<T, I>::NoPermission); | ||
| } | ||
| debug_assert!(details.supply.checked_add(&amount).is_some(), "checked in prep; qed"); | ||
|
|
||
|
|
@@ -452,7 +457,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { | |
| Self::can_increase(id.clone(), beneficiary, amount, true).into_result()?; | ||
| Asset::<T, I>::try_mutate(&id, |maybe_details| -> DispatchResult { | ||
| let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?; | ||
| ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive); | ||
| Self::ensure_live_asset(&details)?; | ||
| check(details)?; | ||
|
|
||
| Account::<T, I>::try_mutate(&id, beneficiary, |maybe_account| -> DispatchResult { | ||
|
|
@@ -494,15 +499,12 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { | |
| f: DebitFlags, | ||
| ) -> Result<T::Balance, DispatchError> { | ||
| let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?; | ||
| ensure!( | ||
| d.status == AssetStatus::Live || d.status == AssetStatus::Frozen, | ||
| Error::<T, I>::IncorrectStatus | ||
| ); | ||
| ensure!(d.status != AssetStatus::Destroying, Error::<T, I>::DestroyingAsset); | ||
|
|
||
| let actual = Self::decrease_balance(id.clone(), target, amount, f, |actual, details| { | ||
| // Check admin rights. | ||
| if let Some(check_admin) = maybe_check_admin { | ||
| ensure!(check_admin == details.admin, Error::<T, I>::NoPermission); | ||
| ensure!(details.admin() == Some(&check_admin), Error::<T, I>::NoPermission); | ||
| } | ||
|
|
||
| debug_assert!(details.supply >= actual, "checked in prep; qed"); | ||
|
|
@@ -537,7 +539,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { | |
| } | ||
|
|
||
| let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?; | ||
| ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive); | ||
| Self::ensure_live_asset(&details)?; | ||
|
|
||
| let actual = Self::prep_debit(id.clone(), target, amount, f)?; | ||
| let mut target_died: Option<DeadConsequence> = None; | ||
|
|
@@ -612,7 +614,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { | |
| return Ok((amount, None)) | ||
| } | ||
| let details = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?; | ||
| ensure!(details.status == AssetStatus::Live, Error::<T, I>::AssetNotLive); | ||
| Self::ensure_live_asset(&details)?; | ||
|
|
||
| // Figure out the debit and credit, together with side-effects. | ||
| let debit = Self::prep_debit(id.clone(), source, amount, f.into())?; | ||
|
|
@@ -627,7 +629,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { | |
|
|
||
| // Check admin rights. | ||
| if let Some(need_admin) = maybe_need_admin { | ||
| ensure!(need_admin == details.admin, Error::<T, I>::NoPermission); | ||
| ensure!(details.admin() == Some(&need_admin), Error::<T, I>::NoPermission); | ||
| } | ||
|
|
||
| // Skip if source == dest | ||
|
|
@@ -712,20 +714,20 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { | |
|
|
||
| Asset::<T, I>::insert( | ||
| &id, | ||
| AssetDetails { | ||
| owner: owner.clone(), | ||
| issuer: owner.clone(), | ||
| admin: owner.clone(), | ||
| freezer: owner.clone(), | ||
| supply: Zero::zero(), | ||
| deposit: Zero::zero(), | ||
| AssetDetails::new( | ||
| owner.clone(), | ||
| owner.clone(), | ||
| owner.clone(), | ||
| owner.clone(), | ||
| Zero::zero(), | ||
| Zero::zero(), | ||
| min_balance, | ||
| is_sufficient, | ||
| accounts: 0, | ||
| sufficients: 0, | ||
| approvals: 0, | ||
| status: AssetStatus::Live, | ||
| }, | ||
| 0, | ||
| 0, | ||
| 0, | ||
| AssetStatus::Live, | ||
| ), | ||
| ); | ||
| ensure!(T::CallbackHandle::created(&id, &owner).is_ok(), Error::<T, I>::CallbackFailed); | ||
| Self::deposit_event(Event::ForceCreated { asset_id: id, owner: owner.clone() }); | ||
|
|
@@ -741,7 +743,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { | |
| Asset::<T, I>::try_mutate_exists(id.clone(), |maybe_details| -> Result<(), DispatchError> { | ||
| let details = maybe_details.as_mut().ok_or(Error::<T, I>::Unknown)?; | ||
| if let Some(check_owner) = maybe_check_owner { | ||
| ensure!(details.owner == check_owner, Error::<T, I>::NoPermission); | ||
| ensure!(details.owner() == Some(&check_owner), Error::<T, I>::NoPermission); | ||
| } | ||
| details.status = AssetStatus::Destroying; | ||
|
|
||
|
|
@@ -848,10 +850,9 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { | |
| ensure!(T::CallbackHandle::destroyed(&id).is_ok(), Error::<T, I>::CallbackFailed); | ||
|
|
||
| let metadata = Metadata::<T, I>::take(&id); | ||
| T::Currency::unreserve( | ||
| &details.owner, | ||
| details.deposit.saturating_add(metadata.deposit), | ||
| ); | ||
| if let Some(owner) = details.owner() { | ||
| T::Currency::unreserve(owner, details.deposit.saturating_add(metadata.deposit)); | ||
| } | ||
| Self::deposit_event(Event::Destroyed { asset_id: id }); | ||
|
|
||
| Ok(()) | ||
|
|
@@ -869,7 +870,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { | |
| amount: T::Balance, | ||
| ) -> DispatchResult { | ||
| let mut d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?; | ||
| ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive); | ||
| Self::ensure_live_asset(&d)?; | ||
| Approvals::<T, I>::try_mutate( | ||
| (id.clone(), &owner, &delegate), | ||
| |maybe_approved| -> DispatchResult { | ||
|
|
@@ -920,7 +921,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { | |
| let mut owner_died: Option<DeadConsequence> = None; | ||
|
|
||
| let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?; | ||
| ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive); | ||
| Self::ensure_live_asset(&d)?; | ||
|
|
||
| Approvals::<T, I>::try_mutate_exists( | ||
| (id.clone(), &owner, delegate), | ||
|
|
@@ -969,8 +970,8 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { | |
| symbol.clone().try_into().map_err(|_| Error::<T, I>::BadMetadata)?; | ||
|
|
||
| let d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?; | ||
| ensure!(d.status == AssetStatus::Live, Error::<T, I>::AssetNotLive); | ||
| ensure!(from == &d.owner, Error::<T, I>::NoPermission); | ||
| Self::ensure_live_asset(&d)?; | ||
| ensure!(d.owner() == Some(&from), Error::<T, I>::NoPermission); | ||
|
|
||
| Metadata::<T, I>::try_mutate_exists(id.clone(), |metadata| { | ||
| ensure!(metadata.as_ref().map_or(true, |m| !m.is_frozen), Error::<T, I>::NoPermission); | ||
|
|
@@ -1019,8 +1020,21 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { | |
| .collect::<Vec<_>>() | ||
| } | ||
|
|
||
| /// Ensure the asset is live or live and with no privileges. | ||
| pub(super) fn ensure_live_asset( | ||
| d: &AssetDetails<T::Balance, T::AccountId, DepositBalanceOf<T, I>>, | ||
| ) -> DispatchResult { | ||
| ensure!( | ||
| d.status == AssetStatus::Live || d.status == AssetStatus::LiveAndNoPrivileges, | ||
| Error::<T, I>::AssetNotLive | ||
| ); | ||
| Ok(()) | ||
| } | ||
|
|
||
| /// Reset the team for the asset with the given `id`. | ||
| /// | ||
| /// If the asset status is `LiveAndNoPrivileges` then it is changed to `Live`. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe worth mentioning something in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would you prefer to change the trait to have 2 functions: EDIT: keeping the trait with just an additional doc could be fine considering any trait using
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I only improved the doc: d0a8ab2 I think it is fine to keep the trait as it is, because |
||
| /// | ||
| /// ### Parameters | ||
| /// - `id`: The identifier of the asset for which the team is being reset. | ||
| /// - `owner`: The new `owner` account for the asset. | ||
|
|
@@ -1035,11 +1049,22 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { | |
| freezer: T::AccountId, | ||
| ) -> DispatchResult { | ||
| let mut d = Asset::<T, I>::get(&id).ok_or(Error::<T, I>::Unknown)?; | ||
| d.owner = owner; | ||
| d.admin = admin; | ||
| d.issuer = issuer; | ||
| d.freezer = freezer; | ||
| if d.status == AssetStatus::LiveAndNoPrivileges { | ||
| d.status = AssetStatus::Live | ||
| } | ||
| match d.try_set_team(&owner, &issuer, &admin, &freezer) { | ||
| Ok(()) => (), | ||
| Err(SetTeamError::AssetStatusLiveAndNoPrivileges) => log::error!( | ||
|
||
| target: LOG_TARGET, | ||
| "Operation failed because status is `LiveAndNoPrivileges`, but is was set to | ||
gui1117 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| `Live` before; qed" | ||
| ), | ||
| } | ||
| Asset::<T, I>::insert(&id, d); | ||
|
|
||
| Self::deposit_event(Event::TeamChanged { asset_id: id.clone(), issuer, admin, freezer }); | ||
| Self::deposit_event(Event::OwnerChanged { asset_id: id, owner }); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added events here. This is unrelated to the PR, Let me know if this is unwanted |
||
|
|
||
| Ok(()) | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.