diff --git a/node/service/src/chain_spec/bifrost_kusama.rs b/node/service/src/chain_spec/bifrost_kusama.rs index 446ac36ea..7216b5f29 100644 --- a/node/service/src/chain_spec/bifrost_kusama.rs +++ b/node/service/src/chain_spec/bifrost_kusama.rs @@ -18,14 +18,13 @@ use crate::chain_spec::{get_account_id_from_seed, get_from_seed, RelayExtensions}; use bifrost_kusama_runtime::{ - AccountId, Balance, BalancesConfig, BlockNumber, InflationInfo, Range, SS58Prefix, - VestingConfig, + AccountId, Balance, BalancesConfig, BlockNumber, SS58Prefix, VestingConfig, }; use bifrost_primitives::{ BifrostKusamaChainId, CurrencyId, CurrencyId::*, TokenInfo, TokenSymbol::*, }; use bifrost_runtime_common::bridge_xcm_helper::DEFAULT_XCM_FEES_IK_PERSPECTIVE; -use bifrost_runtime_common::{constants::currency::DOLLARS, constants::time::HOURS, AuraId}; +use bifrost_runtime_common::{constants::currency::DOLLARS, AuraId}; use cumulus_primitives_core::ParaId; use frame_benchmarking::{account, whitelisted_caller}; use hex_literal::hex; @@ -34,7 +33,7 @@ use sc_service::ChainType; use serde::de::DeserializeOwned; use serde_json as json; use sp_core::{crypto::UncheckedInto, sr25519}; -use sp_runtime::{traits::Zero, Perbill}; +use sp_runtime::traits::Zero; use std::{ collections::BTreeMap, fs::{read_dir, File}, @@ -51,37 +50,6 @@ pub fn ENDOWMENT() -> u128 { 1_000_000 * DOLLARS } -const BLOCKS_PER_ROUND: u32 = 2 * HOURS; - -pub fn inflation_config() -> InflationInfo { - fn to_round_inflation(annual: Range) -> Range { - use bifrost_parachain_staking::inflation::{ - perbill_annual_to_perbill_round, BLOCKS_PER_YEAR, - }; - perbill_annual_to_perbill_round( - annual, - // rounds per year - BLOCKS_PER_YEAR / BLOCKS_PER_ROUND, - ) - } - let annual = Range { - min: Perbill::from_percent(4), - ideal: Perbill::from_percent(5), - max: Perbill::from_percent(5), - }; - InflationInfo { - // staking expectations - expect: Range { - min: 100_000 * DOLLARS, - ideal: 200_000 * DOLLARS, - max: 500_000 * DOLLARS, - }, - // annual inflation - annual, - round: to_round_inflation(annual), - } -} - fn bifrost_kusama_properties() -> Properties { let mut properties = Properties::new(); let mut token_symbol: Vec = vec![]; diff --git a/pallets/parachain-staking/src/migrations.rs b/pallets/parachain-staking/src/migrations.rs index fe6c996ff..9a0dee2ad 100644 --- a/pallets/parachain-staking/src/migrations.rs +++ b/pallets/parachain-staking/src/migrations.rs @@ -25,8 +25,8 @@ use frame_support::{ pallet, pallet_prelude::PhantomData, storage_alias, - traits::{Get, OnRuntimeUpgrade, ReservableCurrency}, - weights::Weight, + traits::{Get, LockableCurrency, OnRuntimeUpgrade, ReservableCurrency}, + weights::{RuntimeDbWeight, Weight}, Twox64Concat, }; #[cfg(feature = "try-runtime")] @@ -63,6 +63,103 @@ const COLLATOR_COMMISSION: Perbill = Perbill::from_percent(10); const PARACHAIN_BOND_RESERVE_PERCENT: Percent = Percent::from_percent(0); const BLOCKS_PER_ROUND: u32 = 2 * 300; +/// Remove participant account locks before the pallet storage is deleted from a runtime. +pub struct RemoveStakingParticipantLocks(PhantomData<(T, C, DbWeight)>); +impl OnRuntimeUpgrade for RemoveStakingParticipantLocks +where + T: frame_system::Config + pallet_balances::Config, + C: LockableCurrency, + DbWeight: Get, +{ + fn on_runtime_upgrade() -> Weight { + let mut candidates = 0u64; + let mut delegators = 0u64; + + for (account, _) in storage_key_iter::< + T::AccountId, + CandidateMetadata<>::Balance>, + Twox64Concat, + >(b"ParachainStaking", b"CandidateInfo") + { + C::remove_lock(COLLATOR_LOCK_ID, &account); + candidates = candidates.saturating_add(1); + } + + for (account, _) in storage_key_iter::< + T::AccountId, + Delegator>::Balance>, + Twox64Concat, + >(b"ParachainStaking", b"DelegatorState") + { + C::remove_lock(DELEGATOR_LOCK_ID, &account); + delegators = delegators.saturating_add(1); + } + + log::info!( + target: "parachain-staking-migration", + "Removed staking locks for {candidates} candidates and {delegators} delegators", + ); + + DbWeight::get().reads_writes( + candidates.saturating_add(delegators), + candidates.saturating_add(delegators), + ) + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, TryRuntimeError> { + let candidate_accounts: Vec = storage_key_iter::< + T::AccountId, + CandidateMetadata<>::Balance>, + Twox64Concat, + >(b"ParachainStaking", b"CandidateInfo") + .map(|(account, _)| account) + .collect(); + let delegator_accounts: Vec = storage_key_iter::< + T::AccountId, + Delegator>::Balance>, + Twox64Concat, + >(b"ParachainStaking", b"DelegatorState") + .map(|(account, _)| account) + .collect(); + + log::info!( + target: "parachain-staking-migration", + "pre_upgrade: {} candidate locks and {} delegator locks to remove", + candidate_accounts.len(), + delegator_accounts.len(), + ); + + Ok((candidate_accounts, delegator_accounts).encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), TryRuntimeError> { + let (candidate_accounts, delegator_accounts): (Vec, Vec) = + Decode::decode(&mut state.as_slice()).map_err(|_| { + TryRuntimeError::Other("failed to decode staking participant lock migration state") + })?; + + for account in candidate_accounts { + let locks = pallet_balances::Pallet::::locks(&account); + ensure!( + !locks.iter().any(|lock| lock.id == COLLATOR_LOCK_ID), + "candidate staking lock still present after migration" + ); + } + + for account in delegator_accounts { + let locks = pallet_balances::Pallet::::locks(&account); + ensure!( + !locks.iter().any(|lock| lock.id == DELEGATOR_LOCK_ID), + "delegator staking lock still present after migration" + ); + } + + Ok(()) + } +} + /// Migration to purge staking storage bloat for `Points` and `AtStake` storage items pub struct InitGenesisMigration(PhantomData); impl OnRuntimeUpgrade for InitGenesisMigration { diff --git a/runtime/bifrost-kusama/Cargo.toml b/runtime/bifrost-kusama/Cargo.toml index 1aae0fdb2..7c296e8c0 100644 --- a/runtime/bifrost-kusama/Cargo.toml +++ b/runtime/bifrost-kusama/Cargo.toml @@ -113,6 +113,7 @@ bifrost-stable-asset = { workspace = true } # Bifrost bifrost-asset-registry = { workspace = true } bifrost-p-k-bridge = { workspace = true } +bifrost-parachain-staking = { workspace = true } bifrost-currencies = { workspace = true } bifrost-farming = { workspace = true, features = ["kusama"] } bifrost-farming-rpc-runtime-api = { workspace = true } @@ -134,7 +135,6 @@ bifrost-vstoken-conversion = { workspace = true } bifrost-vtoken-minting = { workspace = true, features = ["kusama"] } bifrost-vtoken-voting = { workspace = true, features = [ "kusama" ] } bifrost-xcm-interface = { workspace = true } -bifrost-parachain-staking = { workspace = true } lend-market = { workspace = true } lend-market-rpc-runtime-api = { workspace = true } pallet-prices = { workspace = true } @@ -239,6 +239,7 @@ std = [ "bifrost-primitives/std", "bifrost-asset-registry/std", "bifrost-p-k-bridge/std", + "bifrost-parachain-staking/std", "bifrost-currencies/std", "bifrost-farming-rpc-runtime-api/std", "bifrost-farming/std", @@ -264,7 +265,6 @@ std = [ "pallet-prices/std", "leverage-staking/std", "bifrost-stable-asset/std", - "bifrost-parachain-staking/std", "bifrost-xcm-interface/std", "bifrost-channel-commission/std", "bifrost-vbnc-convert/std", @@ -308,7 +308,6 @@ runtime-benchmarks = [ "bifrost-vstoken-conversion/runtime-benchmarks", "bifrost-slp-v2/runtime-benchmarks", "bifrost-asset-registry/runtime-benchmarks", - "bifrost-parachain-staking/runtime-benchmarks", "sp-api/disable-logging", "bifrost-fee-share/runtime-benchmarks", "bifrost-slpx/runtime-benchmarks", @@ -339,7 +338,6 @@ try-runtime = [ "pallet-session/try-runtime", "pallet-aura/try-runtime", "cumulus-pallet-aura-ext/try-runtime", - "bifrost-parachain-staking/try-runtime", "pallet-collective/try-runtime", "pallet-conviction-voting/try-runtime", "pallet-membership/try-runtime", @@ -370,6 +368,7 @@ try-runtime = [ "bifrost-flexible-fee/try-runtime", "bifrost-salp/try-runtime", "bifrost-token-issuer/try-runtime", + "bifrost-parachain-staking/try-runtime", "bifrost-asset-registry/try-runtime", "bifrost-vtoken-minting/try-runtime", "bifrost-slp-v2/try-runtime", @@ -412,4 +411,4 @@ fast-runtime = [] force-debug = ["sp-debug-derive/force-debug"] # Enable sudo pallet. -sudo = ["pallet-sudo"] \ No newline at end of file +sudo = ["pallet-sudo"] diff --git a/runtime/bifrost-kusama/src/lib.rs b/runtime/bifrost-kusama/src/lib.rs index 06489c09e..df8dc5f66 100644 --- a/runtime/bifrost-kusama/src/lib.rs +++ b/runtime/bifrost-kusama/src/lib.rs @@ -34,9 +34,6 @@ use bifrost_primitives::{ AssetHubChainId, BLP_BNC_VBNC, BNC, KSM, KUSAMA_VBNC_ASSET_INDEX, KUSAMA_VBNC_LP_ASSET_INDEX, KUSD, LP_BNC_VBNC, VBNC, VKSM, }; -use core::convert::TryInto; -// A few exports that help ease life for downstream crates. -pub use bifrost_parachain_staking::{InflationInfo, Range}; use bifrost_primitives::{ BifrostCrowdloanId, BifrostVsbondAccount, BuybackPalletId, CommissionPalletId, FarmingBoostPalletId, FarmingGaugeRewardIssuerPalletId, FarmingKeeperPalletId, @@ -45,6 +42,7 @@ use bifrost_primitives::{ SlpEntrancePalletId, SlpExitPalletId, SlpxPalletId, SystemMakerPalletId, SystemStakingPalletId, TreasuryPalletId, VBNCConvertPalletId, VtokenVotingPalletId, }; +use core::convert::TryInto; use frame_support::traits::ExistenceRequirement; pub use frame_support::{ construct_runtime, match_types, parameter_types, @@ -113,7 +111,7 @@ use frame_support::{ fungible::HoldConsideration, tokens::{PayFromAccount, UnityAssetBalanceConversion}, Currency, EitherOf, EitherOfDiverse, Get, Imbalance, InsideBoth, LinearStoragePrice, - LockIdentifier, OnUnbalanced, + LockIdentifier, OnRuntimeUpgrade, OnUnbalanced, ReservableCurrency, }, weights::WeightToFee as _, }; @@ -830,79 +828,6 @@ impl parachain_info::Config for Runtime {} impl cumulus_pallet_aura_ext::Config for Runtime {} -parameter_types! { - /// Minimum round length is 2 minutes - pub const MinBlocksPerRound: u32 = 2 * MINUTES; - /// Rounds before the collator leaving the candidates request can be executed - pub const LeaveCandidatesDelay: u32 = 84; - /// Rounds before the candidate bond increase/decrease can be executed - pub const CandidateBondLessDelay: u32 = 84; - /// Rounds before the delegator exit can be executed - pub const LeaveDelegatorsDelay: u32 = 84; - /// Rounds before the delegator revocation can be executed - pub const RevokeDelegationDelay: u32 = 84; - /// Rounds before the delegator bond increase/decrease can be executed - pub const DelegationBondLessDelay: u32 = 84; - /// Rounds before the reward is paid - pub const RewardPaymentDelay: u32 = 2; - /// Minimum collators selected per round, default at genesis and minimum forever after - /// The collator incentives on Bifrost-Kusama will be discontinued. The number of active - /// collators will be set to 4, ensuring that all collators are nodes operated by Bifrost - /// itself. - pub const MinSelectedCandidates: u32 = prod_or_fast!(4,4); - /// Maximum top delegations per candidate - pub const MaxTopDelegationsPerCandidate: u32 = 300; - /// Maximum bottom delegations per candidate - pub const MaxBottomDelegationsPerCandidate: u32 = 50; - /// Maximum delegations per delegator - pub const MaxDelegationsPerDelegator: u32 = 100; - /// Minimum stake required to become a collator - pub MinCollatorStk: u128 = 5000 * BNCS; - /// Minimum stake required to be reserved to be a candidate - pub MinCandidateStk: u128 = 5000 * BNCS; - /// Minimum stake required to be reserved to be a delegator - pub MinDelegatorStk: u128 = 50 * BNCS; - pub AllowInflation: bool = false; - pub ToMigrateInvulnables: Vec = prod_or_fast!(vec![ - hex!["8cf80f0bafcd0a3d80ca61cb688e4400e275b39d3411b4299b47e712e9dab809"].into(), - hex!["40ac4effe39181731a8feb8a8ee0780e177bdd0d752b09c8fd71047e67189022"].into(), - hex!["624d6a004c72a1abcf93131e185515ebe1410e43a301fe1f25d20d8da345376e"].into(), - hex!["985d2738e512909c81289e6055e60a6824818964535ecfbf10e4d69017084756"].into(), - ],vec![ - hex!["d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"].into(), - hex!["8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48"].into(), - ]); - pub PaymentInRound: u128 = 180 * BNCS; - pub InitSeedStk: u128 = 5000 * BNCS; -} -impl bifrost_parachain_staking::Config for Runtime { - type Currency = Balances; - type MonetaryGovernanceOrigin = TechAdminOrRoot; - type MinBlocksPerRound = MinBlocksPerRound; - type LeaveCandidatesDelay = LeaveCandidatesDelay; - type CandidateBondLessDelay = CandidateBondLessDelay; - type LeaveDelegatorsDelay = LeaveDelegatorsDelay; - type RevokeDelegationDelay = RevokeDelegationDelay; - type DelegationBondLessDelay = DelegationBondLessDelay; - type RewardPaymentDelay = RewardPaymentDelay; - type MinSelectedCandidates = MinSelectedCandidates; - type MaxTopDelegationsPerCandidate = MaxTopDelegationsPerCandidate; - type MaxBottomDelegationsPerCandidate = MaxBottomDelegationsPerCandidate; - type MaxDelegationsPerDelegator = MaxDelegationsPerDelegator; - type MinCollatorStk = MinCollatorStk; - type MinCandidateStk = MinCandidateStk; - type MinDelegation = MinDelegatorStk; - type MinDelegatorStk = MinDelegatorStk; - type AllowInflation = AllowInflation; - type PaymentInRound = PaymentInRound; - type ToMigrateInvulnables = ToMigrateInvulnables; - type PalletId = ParachainStakingPalletId; - type InitSeedStk = InitSeedStk; - type OnCollatorPayout = (); - type OnNewRound = (); - type WeightInfo = bifrost_parachain_staking::weights::SubstrateWeight; -} - parameter_types! { pub const Period: u32 = 6 * HOURS; pub const Offset: u32 = 0; @@ -1801,7 +1726,6 @@ construct_runtime! { Session: pallet_session = 22, Aura: pallet_aura = 23, AuraExt: cumulus_pallet_aura_ext = 24, - ParachainStaking: bifrost_parachain_staking = 25, // Governance stuff #[cfg(feature = "sudo")] @@ -1914,6 +1838,141 @@ impl cumulus_pallet_xcmp_queue::migration::v5::V5Config for Runtime { /// upgrades in case governance decides to do so. THE ORDER IS IMPORTANT. pub type Migrations = migrations::Unreleased; +parameter_types! { + pub const ParachainStakingName: &'static str = "ParachainStaking"; +} + +pub struct TransferParachainStakingAccountToTreasury; +impl OnRuntimeUpgrade for TransferParachainStakingAccountToTreasury { + fn on_runtime_upgrade() -> Weight { + let parachain_staking_account: AccountId = + ParachainStakingPalletId::get().into_account_truncating(); + let treasury_account: AccountId = TreasuryPalletId::get().into_account_truncating(); + let free = >::free_balance(¶chain_staking_account); + let reserved = >::reserved_balance( + ¶chain_staking_account, + ); + + if !reserved.is_zero() { + log::warn!( + target: "runtime::parachain_staking_removal", + "ParachainStaking account has unexpected reserved balance: {reserved:?}", + ); + } + + if free.is_zero() { + log::info!( + target: "runtime::parachain_staking_removal", + "ParachainStaking account has no free balance to transfer", + ); + return RocksDbWeight::get().reads(2); + } + + match >::transfer( + ¶chain_staking_account, + &treasury_account, + free, + ExistenceRequirement::AllowDeath, + ) { + Ok(()) => log::info!( + target: "runtime::parachain_staking_removal", + "Transferred ParachainStaking account free balance to treasury: {free:?}", + ), + Err(error) => log::error!( + target: "runtime::parachain_staking_removal", + "Failed to transfer ParachainStaking account free balance to treasury: {error:?}", + ), + } + + RocksDbWeight::get().reads_writes(3, 2) + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + let parachain_staking_account: AccountId = + ParachainStakingPalletId::get().into_account_truncating(); + let treasury_account: AccountId = TreasuryPalletId::get().into_account_truncating(); + let staking_free = + >::free_balance(¶chain_staking_account); + let staking_reserved = >::reserved_balance( + ¶chain_staking_account, + ); + let treasury_free = >::free_balance(&treasury_account); + + log::info!( + target: "runtime::parachain_staking_removal", + "pre_upgrade: ParachainStaking free={staking_free:?}, reserved={staking_reserved:?}, treasury_free={treasury_free:?}", + ); + + Ok((staking_free, staking_reserved, treasury_free).encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + use frame_support::ensure; + + let (staking_free_before, staking_reserved_before, treasury_free_before): ( + Balance, + Balance, + Balance, + ) = Decode::decode(&mut state.as_slice()).map_err(|_| { + sp_runtime::TryRuntimeError::Other( + "failed to decode ParachainStaking treasury transfer migration state", + ) + })?; + + let parachain_staking_account: AccountId = + ParachainStakingPalletId::get().into_account_truncating(); + let treasury_account: AccountId = TreasuryPalletId::get().into_account_truncating(); + let staking_free_after = + >::free_balance(¶chain_staking_account); + let staking_reserved_after = >::reserved_balance( + ¶chain_staking_account, + ); + let treasury_free_after = + >::free_balance(&treasury_account); + + ensure!( + staking_reserved_before.is_zero(), + "ParachainStaking account had reserved balance before migration" + ); + ensure!( + staking_free_after.is_zero(), + "ParachainStaking account still has free balance after migration" + ); + ensure!( + staking_reserved_after.is_zero(), + "ParachainStaking account still has reserved balance after migration" + ); + ensure!( + treasury_free_after >= treasury_free_before.saturating_add(staking_free_before), + "treasury did not receive ParachainStaking account free balance" + ); + + Ok(()) + } +} + +pub struct EnsureParachainStakingRemoved; +impl OnRuntimeUpgrade for EnsureParachainStakingRemoved { + fn on_runtime_upgrade() -> Weight { + Weight::zero() + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + use frame_support::{ensure, storage::unhashed}; + + let pallet_prefix = sp_io::hashing::twox_128(ParachainStakingName::get().as_bytes()); + ensure!( + !unhashed::contains_prefixed_key(&pallet_prefix), + "ParachainStaking storage prefix still has keys after RemovePallet" + ); + + Ok(()) + } +} + /// The runtime migrations per release. pub mod migrations { use super::*; @@ -1922,6 +1981,14 @@ pub mod migrations { pub type Unreleased = ( // permanent migration, do not remove pallet_xcm::migration::MigrateToLatestXcmVersion, + TransferParachainStakingAccountToTreasury, + bifrost_parachain_staking::migrations::RemoveStakingParticipantLocks< + Runtime, + Balances, + RocksDbWeight, + >, + frame_support::migrations::RemovePallet, + EnsureParachainStakingRemoved, ); } diff --git a/runtime/bifrost-kusama/src/xcm_config.rs b/runtime/bifrost-kusama/src/xcm_config.rs index 474aa9cf3..8f35f3c4a 100644 --- a/runtime/bifrost-kusama/src/xcm_config.rs +++ b/runtime/bifrost-kusama/src/xcm_config.rs @@ -402,7 +402,6 @@ impl Contains for DustRemovalWhitelist { SystemMakerPalletId::get().into_account_truncating(), ZenklinkFeeAccount::get(), CommissionPalletId::get().into_account_truncating(), - ParachainStakingPalletId::get().into_account_truncating(), SystemStakingPalletId::get().into_account_truncating(), VBNCConvertPalletId::get().into_account_truncating(), ];