From c150245dd119a1cd63def954b87888dfbe6de17c Mon Sep 17 00:00:00 2001 From: Murtaza Alexandru Date: Wed, 21 Feb 2024 15:34:14 +0200 Subject: [PATCH] Gov V1 removal: Add manual migration unlock_democracy_funds (#2651) * add manual migration unlock_democracy_funds extrinsic * account for proof size and add LimitCannotBeZero error * account for intermediate nodes, add upper limit and no fee charge * add migration complete tracker * fix merge (tests) * cargo fmt * fix fmt --- Cargo.lock | 2 + pallets/moonbeam-lazy-migrations/Cargo.toml | 2 + pallets/moonbeam-lazy-migrations/src/lib.rs | 66 +++++++++++++++++- pallets/moonbeam-lazy-migrations/src/mock.rs | 70 +++++++++++++++++++- 4 files changed, 134 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3140f0a701..5a0059b98e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9577,7 +9577,9 @@ dependencies = [ "frame-system", "log", "pallet-balances", + "pallet-democracy", "pallet-evm", + "pallet-scheduler", "pallet-timestamp", "parity-scale-codec", "rlp", diff --git a/pallets/moonbeam-lazy-migrations/Cargo.toml b/pallets/moonbeam-lazy-migrations/Cargo.toml index 8589aee633..8725645bd4 100644 --- a/pallets/moonbeam-lazy-migrations/Cargo.toml +++ b/pallets/moonbeam-lazy-migrations/Cargo.toml @@ -16,6 +16,8 @@ scale-info = { workspace = true, features = ["derive"] } sp-core = { workspace = true } sp-io = { workspace = true } sp-runtime = { workspace = true } +pallet-democracy = { workspace = true } +pallet-scheduler = { workspace = true } sp-std = { workspace = true } # Frontier diff --git a/pallets/moonbeam-lazy-migrations/src/lib.rs b/pallets/moonbeam-lazy-migrations/src/lib.rs index 25a5a76247..c2db72695d 100644 --- a/pallets/moonbeam-lazy-migrations/src/lib.rs +++ b/pallets/moonbeam-lazy-migrations/src/lib.rs @@ -37,7 +37,9 @@ pub use pallet::*; pub mod pallet { use super::*; use frame_support::pallet_prelude::*; + use frame_support::traits::{LockIdentifier, LockableCurrency}; use frame_system::pallet_prelude::*; + use pallet_democracy::VotingOf; use sp_core::H160; pub const ARRAY_LIMIT: u32 = 1000; @@ -47,6 +49,15 @@ pub mod pallet { const MAX_LOCAL_ASSETS_STORAGE_ENTRY_SIZE: u64 = (/* biggest key on moonbeam */136) + (/* biggest value on moonbeam */142); + /// Copied from pallet-democracy + const DEMOCRACY_ID: LockIdentifier = *b"democrac"; + const MAX_DEMOCRACY_VOTINGOF_STORAGE_ENTRY_SIZE: u64 = + (/* biggest key on moonbeam */60) + (/* biggest value on moonbeam */1440); + const MAX_BALANCES_LOCKS_STORAGE_ENTRY_SIZE: u64 = + (/* biggest key on moonbeam */60) + (/* biggest value on moonbeam */26 * 3); + const MAX_UNLOCK_PROOF_PER_ACCOUNT: u64 = + MAX_BALANCES_LOCKS_STORAGE_ENTRY_SIZE + MAX_DEMOCRACY_VOTINGOF_STORAGE_ENTRY_SIZE; + /// Pallet for multi block migrations #[pallet::pallet] pub struct Pallet(PhantomData); @@ -55,13 +66,17 @@ pub mod pallet { /// If true, it means that LocalAssets storage has been removed. pub(crate) type LocalAssetsMigrationCompleted = StorageValue<_, bool, ValueQuery>; + #[pallet::storage] + /// If true, it means that Democracy funds have been unlocked. + pub(crate) type DemocracyLocksMigrationCompleted = StorageValue<_, bool, ValueQuery>; + #[pallet::storage] /// The total number of suicided contracts that were removed pub(crate) type SuicidedContractsRemoved = StorageValue<_, u32, ValueQuery>; /// Configuration trait of this pallet. #[pallet::config] - pub trait Config: frame_system::Config + pallet_evm::Config { + pub trait Config: frame_system::Config + pallet_evm::Config + pallet_democracy::Config { type WeightInfo: WeightInfo; } @@ -69,10 +84,14 @@ pub mod pallet { pub enum Error { /// There are no more storage entries to be removed AllStorageEntriesHaveBeenRemoved, - /// There must be at least one address - AddressesLengthCannotBeZero, /// The limit cannot be zero LimitCannotBeZero, + /// The limit for unlocking funds is too high + UnlockLimitTooHigh, + /// There are no more VotingOf entries to be removed and democracy funds to be unlocked + AllDemocracyFundsUnlocked, + /// There must be at least one address + AddressesLengthCannotBeZero, /// The contract is not corrupted (Still exist or properly suicided) ContractNotCorrupted, } @@ -183,5 +202,46 @@ pub mod pallet { } Ok(Pays::No.into()) } + + // TODO(alexandru): This extrinsic should be removed once Gov V1 is removed. + // Note: We don't need to unreserve any funds, as they are assumed to be already + // unreserved prior to this operation and the proposal submission disabled. + #[pallet::call_index(2)] + #[pallet::weight( + Weight::from_parts(0, + INTERMEDIATES_NODES_SIZE + MAX_UNLOCK_PROOF_PER_ACCOUNT * ::from(*limit)) + .saturating_add(::DbWeight::get() + .reads_writes((*limit + 1).into(), (*limit + 1).into()).saturating_mul(2)) + )] + pub fn unlock_democracy_funds( + origin: OriginFor, + limit: u32, + ) -> DispatchResultWithPostInfo { + ensure_signed(origin)?; + ensure!(limit != 0, Error::::LimitCannotBeZero); + ensure!(limit <= 50, Error::::UnlockLimitTooHigh); + + ensure!( + !DemocracyLocksMigrationCompleted::::get(), + Error::::AllDemocracyFundsUnlocked + ); + + // Unlock staked funds and remove the voting entry. This way we can keep track of what + // is left without extra cost. + let unlocked_accounts = VotingOf::::iter() + .drain() + .take(limit as usize) + .map(|(account, _)| { + ::Currency::remove_lock(DEMOCRACY_ID, &account) + }) + .count() as u32; + + if unlocked_accounts < limit { + DemocracyLocksMigrationCompleted::::set(true); + } + + log::info!("Unlocked {} accounts 🧹", unlocked_accounts); + Ok(Pays::No.into()) + } } } diff --git a/pallets/moonbeam-lazy-migrations/src/mock.rs b/pallets/moonbeam-lazy-migrations/src/mock.rs index 7688dd924d..6a9f47a805 100644 --- a/pallets/moonbeam-lazy-migrations/src/mock.rs +++ b/pallets/moonbeam-lazy-migrations/src/mock.rs @@ -19,12 +19,13 @@ use super::*; use crate as pallet_moonbeam_lazy_migrations; use frame_support::{ - construct_runtime, parameter_types, - traits::Everything, + construct_runtime, ord_parameter_types, parameter_types, + traits::{EqualPrivilegeOnly, Everything, SortedMembers}, weights::{constants::RocksDbWeight, Weight}, }; +use frame_system::{EnsureRoot, EnsureSigned}; use pallet_evm::{AddressMapping, EnsureAddressTruncated}; -use sp_core::{H160, H256, U256}; +use sp_core::{ConstU32, H160, H256, U256}; use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, AccountId32, BuildStorage, Perbill, @@ -42,6 +43,8 @@ construct_runtime!( Timestamp: pallet_timestamp, EVM: pallet_evm, LazyMigrations: pallet_moonbeam_lazy_migrations::{Pallet, Call}, + Democracy: pallet_democracy::{Pallet, Call, Storage, Config, Event}, + Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event}, } ); @@ -98,6 +101,67 @@ impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = (); } +impl pallet_scheduler::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type PalletsOrigin = OriginCaller; + type RuntimeCall = RuntimeCall; + type MaximumWeight = (); + type ScheduleOrigin = EnsureRoot; + type MaxScheduledPerBlock = ConstU32<100>; + type WeightInfo = (); + type OriginPrivilegeCmp = EqualPrivilegeOnly; + type Preimages = (); +} + +ord_parameter_types! { + pub const One: u64 = 1; + pub const Two: u64 = 2; + pub const Three: u64 = 3; + pub const Four: u64 = 4; + pub const Five: u64 = 5; + pub const Six: u64 = 6; +} +pub struct OneToFive; +impl SortedMembers for OneToFive { + fn sorted_members() -> Vec { + vec![1, 2, 3, 4, 5] + } + #[cfg(feature = "runtime-benchmarks")] + fn add(_m: &u64) {} +} +impl pallet_democracy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = pallet_balances::Pallet; + type EnactmentPeriod = (); + type LaunchPeriod = (); + type VotingPeriod = (); + type VoteLockingPeriod = (); + type FastTrackVotingPeriod = (); + type MinimumDeposit = (); + type MaxDeposits = (); + type MaxBlacklisted = (); + type SubmitOrigin = EnsureSigned; + type ExternalOrigin = EnsureRoot; + type ExternalMajorityOrigin = EnsureRoot; + type ExternalDefaultOrigin = EnsureRoot; + type FastTrackOrigin = EnsureRoot; + type CancellationOrigin = EnsureRoot; + type BlacklistOrigin = EnsureRoot; + type CancelProposalOrigin = EnsureRoot; + type VetoOrigin = EnsureSigned; + type CooloffPeriod = (); + type Slash = (); + type InstantOrigin = EnsureRoot; + type InstantAllowed = (); + type Scheduler = Scheduler; + type MaxVotes = (); + type PalletsOrigin = OriginCaller; + type WeightInfo = (); + type MaxProposals = (); + type Preimages = (); +} + parameter_types! { pub const MinimumPeriod: u64 = 6000 / 2; }