diff --git a/substrate-node/pallets/pallet-smart-contract/src/migrations/mod.rs b/substrate-node/pallets/pallet-smart-contract/src/migrations/mod.rs index 6ec139373..bfd499d1e 100644 --- a/substrate-node/pallets/pallet-smart-contract/src/migrations/mod.rs +++ b/substrate-node/pallets/pallet-smart-contract/src/migrations/mod.rs @@ -1 +1,2 @@ -pub mod v6; \ No newline at end of file +pub mod v6; +pub mod v7; diff --git a/substrate-node/pallets/pallet-smart-contract/src/migrations/v7.rs b/substrate-node/pallets/pallet-smart-contract/src/migrations/v7.rs new file mode 100644 index 000000000..94f2504d1 --- /dev/null +++ b/substrate-node/pallets/pallet-smart-contract/src/migrations/v7.rs @@ -0,0 +1,139 @@ +use crate::*; +#[cfg(feature = "try-runtime")] +use codec::{Decode, Encode}; +use frame_support::{traits::OnRuntimeUpgrade, weights::Weight}; +use log::debug; +use sp_std::collections::btree_map::BTreeMap; +use sp_std::marker::PhantomData; + +#[cfg(feature = "try-runtime")] +use sp_std::vec::Vec; + +pub struct FixTwinLockedBalances(PhantomData); + +impl OnRuntimeUpgrade for FixTwinLockedBalances { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, &'static str> { + debug!("current pallet version: {:?}", PalletVersion::::get()); + assert!(PalletVersion::::get() >= types::StorageVersion::V6); + + debug!("👥 Smart Contract pallet to V7 passes PRE migrate checks ✅",); + Ok(vec![]) + } + + fn on_runtime_upgrade() -> Weight { + if PalletVersion::::get() == types::StorageVersion::V6 { + migrate_to_version_7::() + } else { + debug!(" >>> Unused Smart Contract pallet V7 migration"); + Weight::zero() + } + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(pre_contracts_count: Vec) -> Result<(), &'static str> { + debug!("current pallet version: {:?}", PalletVersion::::get()); + assert!(PalletVersion::::get() >= types::StorageVersion::V7); + + debug!( + "👥 Smart Contract pallet to {:?} passes POST migrate checks ✅", + PalletVersion::::get() + ); + + Ok(()) + } +} + +pub fn migrate_to_version_7() -> frame_support::weights::Weight { + debug!( + " >>> Starting contract pallet migration, pallet version: {:?}", + PalletVersion::::get() + ); + + let mut read_writes = 0; + + let mut twin_contract_locked_balances: BTreeMap> = BTreeMap::new(); + + for (ctr_id, l) in ContractLock::::iter() { + let ctr = Contracts::::get(ctr_id); + read_writes += 1; + match ctr { + Some(contract) => { + // l.amount_locked + twin_contract_locked_balances + .entry(contract.twin_id) + .and_modify(|v| *v += l.amount_locked) + .or_insert(BalanceOf::::saturated_from(0 as u128)); + } + None => (), + } + } + + for (t, total_contract_locked_balance) in twin_contract_locked_balances { + let twin = pallet_tfgrid::Twins::::get(t); + read_writes += 1; + match twin { + Some(twin) => { + let total_lock_balances = get_locked_balance::(&twin.account_id); + + if total_lock_balances != total_contract_locked_balance { + debug!( + "total locked balance on twin {} account: {:?}", + t, total_lock_balances + ); + debug!( + "should have locked only: {:?}", + total_contract_locked_balance + ); + + // get the total balance of the twin - minimum existence requirement + let total_balance = ::Currency::total_balance(&twin.account_id) + - ::Currency::minimum_balance(); + + // lock only an amount up to the total balance + // this will make sure the locked balance will not exceed the total balance on the twin's account + let amount_that_we_can_lock = total_balance.min(total_contract_locked_balance); + debug!("we can lock up to: {:?}", amount_that_we_can_lock); + + // Unlock all balance & relock real locked amount + ::Currency::remove_lock(GRID_LOCK_ID, &twin.account_id); + ::Currency::set_lock( + GRID_LOCK_ID, + &twin.account_id, + amount_that_we_can_lock, + WithdrawReasons::all(), + ); + read_writes += 2; + } + } + None => { + debug!("twin {} not found", t); + } + } + } + + // Update pallet storage version + PalletVersion::::set(types::StorageVersion::V7); + debug!(" <<< Storage version upgraded"); + + // Return the weight consumed by the migration. + T::DbWeight::get().reads(read_writes + 1) +} + +fn get_usable_balance(account_id: &T::AccountId) -> BalanceOf { + let balance = pallet_balances::pallet::Pallet::::usable_balance(account_id); + let b = balance.saturated_into::(); + BalanceOf::::saturated_from(b) +} + +pub fn get_locked_balance(account_id: &T::AccountId) -> BalanceOf { + let usable_balance = get_usable_balance::(account_id); + + let free_balance = ::Currency::free_balance(account_id); + + let locked_balance = free_balance.checked_sub(&usable_balance); + match locked_balance { + Some(balance) => balance, + None => BalanceOf::::saturated_from(0 as u128), + } +} diff --git a/substrate-node/pallets/pallet-smart-contract/src/types.rs b/substrate-node/pallets/pallet-smart-contract/src/types.rs index e7f2fb351..cfe59bb9c 100644 --- a/substrate-node/pallets/pallet-smart-contract/src/types.rs +++ b/substrate-node/pallets/pallet-smart-contract/src/types.rs @@ -18,6 +18,7 @@ pub enum StorageVersion { V4, V5, V6, + V7, } impl Default for StorageVersion { diff --git a/substrate-node/runtime/src/lib.rs b/substrate-node/runtime/src/lib.rs index 97037f92d..f673bb6e9 100644 --- a/substrate-node/runtime/src/lib.rs +++ b/substrate-node/runtime/src/lib.rs @@ -762,9 +762,10 @@ pub type Executive = frame_executive::Executive< Migrations, >; -// All migrations executed on runtime upgrade as a nested tuple of types implementing -// `OnRuntimeUpgrade`. -type Migrations = pallet_tfgrid::migrations::v14::FixFarmingPoliciesMap; +type Migrations = ( + pallet_smart_contract::migrations::v7::FixTwinLockedBalances, + pallet_tfgrid::migrations::v14::FixFarmingPoliciesMap, +); // follows Substrate's non destructive way of eliminating otherwise required // repetion: https://github.com/paritytech/substrate/pull/10592