From 24f9c77907a0a72c0666ab52b9705580eff7d5c8 Mon Sep 17 00:00:00 2001 From: PraetorP Date: Fri, 19 May 2023 10:42:33 +0000 Subject: [PATCH] Breaking: migration to `Fungible` traits in app-promotion pallet Migration from `Currency` traits to `Fungible` ones in constraints for `Config::Currency` associated type. --- --- a/Cargo.lock +++ b/Cargo.lock @@ -6125,7 +6125,7 @@ [[package]] name = "pallet-app-promotion" -version = "0.1.6" +version = "0.2.0" dependencies = [ "frame-benchmarking", "frame-support", --- a/pallets/app-promotion/CHANGELOG.md +++ b/pallets/app-promotion/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. +## [0.2.0] - 2023-05-19 + +### Changed + +- **Breaking:** migration from `Currency` traits to `Fungible` ones in constraints + for `Config::Currency` associated type. + ## [0.1.6] - 2023-04-19 - ### Fixed --- a/pallets/app-promotion/Cargo.toml +++ b/pallets/app-promotion/Cargo.toml @@ -9,7 +9,7 @@ license = 'GPLv3' name = 'pallet-app-promotion' repository = 'https://github.com/UniqueNetwork/unique-chain' -version = '0.1.6' +version = '0.2.0' [package.metadata.docs.rs] targets = ['x86_64-unknown-linux-gnu'] --- a/pallets/app-promotion/src/lib.rs +++ b/pallets/app-promotion/src/lib.rs @@ -70,7 +70,9 @@ use frame_support::{ dispatch::{DispatchResult}, traits::{ - Currency, Get, LockableCurrency, WithdrawReasons, tokens::Balance, ExistenceRequirement, + Get, LockableCurrency, + tokens::Balance, + fungible::{Inspect, InspectFreeze, Mutate, MutateFreeze}, }, ensure, BoundedVec, }; @@ -90,24 +92,25 @@ const PENDING_LIMIT_PER_BLOCK: u32 = 3; type BalanceOf = - <::Currency as Currency<::AccountId>>::Balance; + <::Currency as Inspect<::AccountId>>::Balance; #[frame_support::pallet] pub mod pallet { use super::*; use frame_support::{ - Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key, PalletId, - traits::ReservableCurrency, weights::Weight, + Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key, PalletId, weights::Weight, }; use frame_system::pallet_prelude::*; +use sp_runtime::DispatchError; #[pallet::config] pub trait Config: frame_system::Config + pallet_evm::Config + pallet_configuration::Config { /// Type to interact with the native token - type Currency: ExtendedLockableCurrency - + ReservableCurrency; + type Currency: MutateFreeze + + Mutate + + ExtendedLockableCurrency::Currency as Inspect>::Balance>; /// Type for interacting with collections type CollectionHandler: CollectionHandler< @@ -125,6 +128,10 @@ #[pallet::constant] type PalletId: Get; + /// Freeze identifier used by the pallet + #[pallet::constant] + type FreezeIdentifier: Get<<::Currency as InspectFreeze>::Id>; + /// In relay blocks. #[pallet::constant] type RecalculationInterval: Get; @@ -205,10 +212,12 @@ PendingForBlockOverflow, /// The error is due to the fact that the collection/contract must already be sponsored in order to perform the action. SponsorNotSet, - /// Errors caused by incorrect actions with a locked balance. + /// IncorrectLockedBalanceOperation, /// Errors caused by insufficient staked balance. InsufficientStakedBalance, + /// Errors caused by incorrect state of a staker in context of the pallet. + InconsistencyState } /// Stores the total staked amount. @@ -263,6 +272,10 @@ pub type PreviousCalculatedRecord = StorageValue; + // #[pallet::storage] + // pub(crate) type UpgradedToFreezes = + // StorageValue; + #[pallet::hooks] impl Hooks> for Pallet { /// Block overflow is impossible due to the fact that the unstake algorithm in on_initialize @@ -277,22 +290,149 @@ if !block_pending.is_empty() { block_pending.into_iter().for_each(|(staker, amount)| { - Self::get_locked_balance(&staker).map(|b| { - let new_state = b.amount.checked_sub(&amount).unwrap_or_default(); - Self::set_lock_unchecked(&staker, new_state); + Self::get_freezed_balance(&staker).map(|b| { + let new_state = b.checked_sub(&amount).unwrap_or_default(); + Self::set_freeze_unchecked(&staker, new_state); }); }); } ::WeightInfo::on_initialize(counter) } + + // fn on_runtime_upgrade() -> Weight { + // use scale_info::prelude::collections::HashSet; + // let mut consumed_weight = Weight::zero(); + // let mut add_weight = |reads, writes, weight| { + // consumed_weight += T::DbWeight::get().reads_writes(reads, writes); + // consumed_weight += weight; + // }; + + // let mut stakes_unstakes = vec![]; + + // if >::get() { + // add_weight(1, 0, Weight::zero()); + // return consumed_weight; + // } else { + // add_weight(1, 1, Weight::zero()); + // >::set(true); + // } + // >::iter_keys().for_each(|(staker_id, _)| { + // add_weight(1, 0, Weight::zero()); + // stakes_unstakes.push(staker_id); + // }); + + // >::iter().for_each(|(_, v)| { + // add_weight(1, 0, Weight::zero()); + // v.into_iter().for_each(|(staker, _)| { + // stakes_unstakes.push(staker); + // }); + // }); + + // // filter duplicated id. + // stakes_unstakes = stakes_unstakes + // .into_iter() + // .map(|key| key) + // .collect::>() + // .into_iter() + // .collect(); + + // stakes_unstakes + // .map(|a| (a, >::get_locked_balance(&a).amount)) + // .for_each(|(staker, amount)| { + // <::Currency as LockableCurrency>::remove_lock( + // LOCK_IDENTIFIER, + // &staker, + // ); + // <::Currency as MutateFreeze>::set_freeze( + // &::FreezeIdentifier::get(), + // &staker, + // amount, + // ); + // add_weight(1, 2, Weight::zero()) + // }); + + // consumed_weight + // } + + // #[cfg(feature = "try-runtime")] + // fn pre_upgrade() -> Result, &'static str> { + // use sp_std::collections::btree_map::BTreeMap; + // if >::get() { + // return Ok(Default::default()); + // } + // // Staker -> (total (stakes and unstakes) locked by promotion); + // let mut pre_state: BTreeMap> = BTreeMap::new(); + + // >::iter().for_each(|((staker, _), (amount, _))| { + // if let Some(locked_balance) = pre_state.get_mut(&staker) { + // *locked_balance += amount; + // } else { + // pre_state.insert(staker, amount); + // } + // }); + + // >::iter().for_each(|(_, v)| { + // v.into_iter().for_each(|(staker, amount)| { + // if let Some(locked_balance) = pre_state.get_mut(&staker) { + // *locked_balance += amount; + // } else { + // pre_state.insert(staker, amount); + // } + // }) + // }); + + // Ok(pre_state.encode()) + // } + + // #[cfg(feature = "try-runtime")] + // fn post_upgrade(pre_state: Vec) -> Result<(), &'static str> { + // use sp_std::collections::btree_map::BTreeMap; + + // if >::get() { + // return Ok(()); + // } + + // let mut is_ok = true; + + // let pre_state: BTreeMap> = + // Decode::decode(&mut &pre_state[..]).map_err(|_| "failed to decode pre_state")?; + // for (staker, freezed_by_promo) in pre_state.into_iter() { + // let storage_freeze_state = <::Currency as InspectFreeze< + // T::AccountId, + // >>::balance_frozen( + // &::FreezeIdentifier::get(), staker + // ); + // if storage_freeze_state != freezed_by_promo { + // is_ok = false; + // log::error!( + // "Incorrect freezed balance for {:?}. New balance: {:?}. Before runtime upgrade: locked by promo - {:?}", + // staker, storage_freeze_state, freezed_by_promo + // ); + // } + + // if !>::get_locked_balance(&staker).amount.is_zero() { + // is_ok = false; + // log::error!( + // "Incorrect(non-zero) locked by app promo balance for {:?}", + // staker + // ); + // } + // } + + // if is_ok { + // Ok(()) + // } else { + // Err("Incorrect balance for some of stakers... See logs") + // } + // } } #[pallet::call] impl Pallet where T::BlockNumber: From + Into, - <::Currency as Currency>::Balance: Sum + From, + <::Currency as Inspect>::Balance: Sum + From, { /// Sets an address as the the admin. /// @@ -338,22 +478,21 @@ ); let config = >::get(); - let balance = - <::Currency as Currency>::free_balance(&staker_id); + let balance = <::Currency as Inspect>::balance(&staker_id); - // checks that we can lock `amount` on the `staker` account. + // checks that we can freeze `amount` on the `staker` account. ensure!( amount - <= match Self::get_locked_balance(&staker_id) { - Some(lock) => balance - .checked_sub(&lock.amount) + <= match Self::get_freezed_balance(&staker_id) { + Some(freezed_by_pallet) => balance + .checked_sub(&freezed_by_pallet) .ok_or(ArithmeticError::Underflow)?, None => balance, }, ArithmeticError::Underflow ); - Self::add_lock_balance(&staker_id, amount)?; + Self::add_freeze_balance(&staker_id, amount)?; let block_number = T::RelayBlockNumberProvider::current_block_number(); @@ -599,14 +738,14 @@ let flush_stake = || -> DispatchResult { if let Some(last_id) = &*last_id.borrow() { if !income_acc.borrow().is_zero() { - <::Currency as Currency>::transfer( + <::Currency as Mutate>::transfer( &T::TreasuryAccountId::get(), last_id, *income_acc.borrow(), - ExistenceRequirement::KeepAlive, + frame_support::traits::tokens::Preservation::Protect, )?; - Self::add_lock_balance(last_id, *income_acc.borrow())?; + Self::add_freeze_balance(last_id, *income_acc.borrow())?; >::try_mutate(|staked| -> DispatchResult { *staked = staked .checked_add(&*income_acc.borrow()) @@ -681,6 +820,42 @@ Ok(()) } + + /// Migrates lock state into freeze one + /// + /// # Arguments + /// + /// * `origin`: Must be `Signed`. + /// * `stakers`: Accounts to be upgraded. + #[pallet::call_index(9)] + #[pallet::weight(T::DbWeight::get().reads_writes(2, 2) * stakers.len() as u64)] + pub fn upgrade_accounts( + origin: OriginFor, + stakers: Vec, + ) -> DispatchResult { + ensure_signed(origin)?; + + stakers.into_iter().try_for_each(|s| -> Result<_, DispatchError> { + if let Some(lock) = Self::get_locked_balance(&s) { + + if let Some(_) = Self::get_freezed_balance(&s) { + return Err(Error::::InconsistencyState.into()) + } + + <::Currency as LockableCurrency>::remove_lock( + LOCK_IDENTIFIER, + &s, + ); + + Self::set_freeze_unchecked(&s, lock.amount); + Ok(()) + } else { + Ok(()) + } + })?; + + Ok(()) + } } } @@ -791,11 +966,23 @@ /// /// - `staker`: staker account. /// - `amount`: amount of added locked funds. - fn add_lock_balance(staker: &T::AccountId, amount: BalanceOf) -> DispatchResult { - Self::get_locked_balance(staker) - .map_or(>::default(), |l| l.amount) + // fn add_lock_balance(staker: &T::AccountId, amount: BalanceOf) -> DispatchResult { + // Self::get_locked_balance(staker) + // .map_or(>::default(), |l| l.amount) + // .checked_add(&amount) + // .map(|new_lock| Self::set_lock_unchecked(staker, new_lock)) + // .ok_or(ArithmeticError::Overflow.into()) + // } + + /// Adds the balance to freezed by the pallet. + /// + /// - `staker`: staker account. + /// - `amount`: amount of added freezed funds. + fn add_freeze_balance(staker: &T::AccountId, amount: BalanceOf) -> DispatchResult { + Self::get_freezed_balance(staker) + .unwrap_or_default() .checked_add(&amount) - .map(|new_lock| Self::set_lock_unchecked(staker, new_lock)) + .map(|freeze| Self::set_freeze_unchecked(staker, freeze)) .ok_or(ArithmeticError::Overflow.into()) } @@ -803,19 +990,38 @@ /// /// - `staker`: staker account. /// - `amount`: amount of locked funds. - fn set_lock_unchecked(staker: &T::AccountId, amount: BalanceOf) { + // fn set_lock_unchecked(staker: &T::AccountId, amount: BalanceOf) { + // if amount.is_zero() { + // <::Currency as LockableCurrency>::remove_lock( + // LOCK_IDENTIFIER, + // &staker, + // ); + // } else { + // <::Currency as LockableCurrency>::set_lock( + // LOCK_IDENTIFIER, + // staker, + // amount, + // WithdrawReasons::all(), + // ) + // } + // } + + /// Sets the new state of a balance freezed by the pallet. + /// + /// - `staker`: staker account. + /// - `amount`: amount of freezed funds. + fn set_freeze_unchecked(staker: &T::AccountId, amount: BalanceOf) { if amount.is_zero() { - <::Currency as LockableCurrency>::remove_lock( - LOCK_IDENTIFIER, + <::Currency as MutateFreeze>::thaw( + &T::FreezeIdentifier::get(), &staker, ); } else { - <::Currency as LockableCurrency>::set_lock( - LOCK_IDENTIFIER, + <::Currency as MutateFreeze>::set_freeze( + &T::FreezeIdentifier::get(), staker, amount, - WithdrawReasons::all(), - ) + ); } } @@ -830,6 +1036,22 @@ .find(|l| l.id == LOCK_IDENTIFIER) } + /// Returns the balance freezed by the pallet for the staker. + /// + /// - `staker`: staker account. + pub fn get_freezed_balance(staker: &T::AccountId) -> Option> { + let res = <::Currency as InspectFreeze>::balance_frozen( + &T::FreezeIdentifier::get(), + staker, + ); + + if res == Zero::zero() { + None + } else { + Some(res) + } + } + /// Returns the total staked balance for the staker. /// /// - `staker`: staker account. @@ -926,7 +1148,7 @@ impl Pallet where - <::Currency as Currency>::Balance: Sum, + <::Currency as Inspect>::Balance: Sum, { /// Returns the amount reserved by the pending. /// If `staker` is `None`, returns the total pending. @@ -987,13 +1209,8 @@ // checks that we can do unstake in the block ensure!(!pendings.is_full(), Error::::PendingForBlockOverflow); - let mut total_stakes = 0u64; - let total_staked: BalanceOf = Staked::::drain_prefix((&staker_id,)) - .map(|(_, (amount, _))| { - total_stakes += 1; - amount - }) + .map(|(_, (amount, _))| amount) .sum(); if total_staked.is_zero() { --- a/pallets/common/src/lib.rs +++ b/pallets/common/src/lib.rs @@ -420,7 +420,6 @@ #[frame_support::pallet] pub mod pallet { - use core::marker::PhantomData; use super::*; use dispatch::CollectionDispatch; --- a/pallets/nonfungible/src/lib.rs +++ b/pallets/nonfungible/src/lib.rs @@ -151,7 +151,6 @@ use frame_support::{ Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key, traits::StorageVersion, }; - use frame_system::pallet_prelude::*; use up_data_structs::{CollectionId, TokenId}; use super::weights::WeightInfo; -- gitstuff