diff --git a/Cargo.lock b/Cargo.lock index 48b7a8a57..277f1f1a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5579,6 +5579,7 @@ dependencies = [ "pallet-membership", "pallet-price-aggregator", "pallet-proxy", + "pallet-session", "pallet-timestamp", "pallet-unified-accounts", "pallet-utility", @@ -5587,6 +5588,7 @@ dependencies = [ "sha3", "shibuya-runtime", "shiden-runtime", + "sp-consensus-aura", "sp-core", "sp-io", "sp-runtime", @@ -8485,6 +8487,7 @@ dependencies = [ "log", "num-traits", "pallet-balances", + "pallet-migrations", "parity-scale-codec", "scale-info", "serde", @@ -14481,6 +14484,7 @@ dependencies = [ "staging-xcm-builder", "staging-xcm-executor", "substrate-wasm-builder", + "vesting-mbm", "xcm-fee-payment-runtime-api", ] @@ -17030,6 +17034,27 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vesting-mbm" +version = "1.0.0" +dependencies = [ + "astar-primitives", + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-balances", + "pallet-migrations", + "pallet-vesting", + "parity-scale-codec", + "scale-info", + "serde_json", + "sp-arithmetic", + "sp-io", + "sp-runtime", + "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.11.0)", +] + [[package]] name = "void" version = "1.0.2" diff --git a/Cargo.toml b/Cargo.toml index e413f0d65..2109c7d42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -297,6 +297,7 @@ astar-xcm-benchmarks = { path = "./pallets/astar-xcm-benchmarks", default-featur pallet-static-price-provider = { path = "./pallets/static-price-provider", default-features = false } pallet-price-aggregator = { path = "./pallets/price-aggregator", default-features = false } pallet-collective-proxy = { path = "./pallets/collective-proxy", default-features = false } +vesting-mbm = { path = "./pallets/vesting-mbm", default-features = false } dapp-staking-runtime-api = { path = "./pallets/dapp-staking/rpc/runtime-api", default-features = false } diff --git a/pallets/dapp-staking/Cargo.toml b/pallets/dapp-staking/Cargo.toml index 61d9ff225..bd8cdd709 100644 --- a/pallets/dapp-staking/Cargo.toml +++ b/pallets/dapp-staking/Cargo.toml @@ -29,6 +29,7 @@ frame-benchmarking = { workspace = true, optional = true } [dev-dependencies] pallet-balances = { workspace = true } +pallet-migrations = { workspace = true } [features] default = ["std"] diff --git a/pallets/dapp-staking/src/benchmarking/mod.rs b/pallets/dapp-staking/src/benchmarking/mod.rs index 0399a8d46..c8ec3ddef 100644 --- a/pallets/dapp-staking/src/benchmarking/mod.rs +++ b/pallets/dapp-staking/src/benchmarking/mod.rs @@ -21,7 +21,7 @@ use super::{Pallet as DappStaking, *}; use astar_primitives::Balance; use frame_benchmarking::v2::*; -use frame_support::assert_ok; +use frame_support::{assert_ok, migrations::SteppedMigration, weights::WeightMeter}; use frame_system::{Pallet as System, RawOrigin}; use sp_std::prelude::*; @@ -1070,6 +1070,73 @@ mod benchmarks { ); } + /// Benchmark a single step of mbm migration. + #[benchmark] + fn step() { + let alice: T::AccountId = account("alice", 0, 1); + + Ledger::::set( + &alice, + AccountLedger { + locked: 1000, + unlocking: vec![ + UnlockingChunk { + amount: 100, + unlock_block: 5, + }, + UnlockingChunk { + amount: 100, + unlock_block: 20, + }, + ] + .try_into() + .unwrap(), + staked: Default::default(), + staked_future: None, + contract_stake_count: 0, + }, + ); + CurrentEraInfo::::put(EraInfo { + total_locked: 1000, + unlocking: 200, + current_stake_amount: Default::default(), + next_stake_amount: Default::default(), + }); + + System::::set_block_number(10u32.into()); + let mut meter = WeightMeter::new(); + + #[block] + { + crate::migration::LazyMigration::>::step( + None, &mut meter, + ) + .unwrap(); + } + + assert_eq!( + Ledger::::get(&alice), + AccountLedger { + locked: 1000, + unlocking: vec![ + UnlockingChunk { + amount: 100, + unlock_block: 5, // already unlocked + }, + UnlockingChunk { + amount: 100, + unlock_block: 30, // double remaining blocks + }, + ] + .try_into() + .unwrap(), + staked: Default::default(), + staked_future: None, + contract_stake_count: 0, + } + ); + } + impl_benchmark_test_suite!( Pallet, crate::benchmarking::tests::new_test_ext(), diff --git a/pallets/dapp-staking/src/migration.rs b/pallets/dapp-staking/src/migration.rs index 35e27ccfb..79fa7e858 100644 --- a/pallets/dapp-staking/src/migration.rs +++ b/pallets/dapp-staking/src/migration.rs @@ -17,7 +17,12 @@ // along with Astar. If not, see . use super::*; -use frame_support::{storage_alias, traits::UncheckedOnRuntimeUpgrade}; +use frame_support::{ + migrations::{MigrationId, SteppedMigration, SteppedMigrationError}, + storage_alias, + traits::{OnRuntimeUpgrade, UncheckedOnRuntimeUpgrade}, + weights::WeightMeter, +}; #[cfg(feature = "try-runtime")] use sp_std::vec::Vec; @@ -412,3 +417,111 @@ pub mod v6 { pub period: PeriodNumber, } } + +const PALLET_MIGRATIONS_ID: &[u8; 16] = b"dapp-staking-mbm"; + +pub struct LazyMigration(PhantomData<(T, W)>); + +impl SteppedMigration for LazyMigration { + type Cursor = ::AccountId; + // Without the explicit length here the construction of the ID would not be infallible. + type Identifier = MigrationId<16>; + + /// The identifier of this migration. Which should be globally unique. + fn id() -> Self::Identifier { + MigrationId { + pallet_id: *PALLET_MIGRATIONS_ID, + version_from: 0, + version_to: 1, + } + } + + fn step( + mut cursor: Option, + meter: &mut WeightMeter, + ) -> Result, SteppedMigrationError> { + let required = W::step(); + // If there is not enough weight for a single step, return an error. This case can be + // problematic if it is the first migration that ran in this block. But there is nothing + // that we can do about it here. + if meter.remaining().any_lt(required) { + return Err(SteppedMigrationError::InsufficientWeight { required }); + } + + let mut count = 0u32; + let mut migrated = 0u32; + let current_block_number = + frame_system::Pallet::::block_number().saturated_into::(); + + // We loop here to do as much progress as possible per step. + loop { + if meter.try_consume(required).is_err() { + break; + } + + let mut iter = if let Some(last_key) = cursor { + // If a cursor is provided, start iterating from the stored value + // corresponding to the last key processed in the previous step. + // Note that this only works if the old and the new map use the same way to hash + // storage keys. + Ledger::::iter_from(Ledger::::hashed_key_for(last_key)) + } else { + // If no cursor is provided, start iterating from the beginning. + Ledger::::iter() + }; + + // If there's a next item in the iterator, perform the migration. + if let Some((ref last_key, mut ledger)) = iter.next() { + // inc count + count.saturating_inc(); + + if ledger.unlocking.is_empty() { + // no unlocking for this account, nothing to update + // Return the processed key as the new cursor. + cursor = Some(last_key.clone()); + continue; + } + for chunk in ledger.unlocking.iter_mut() { + if current_block_number >= chunk.unlock_block { + continue; // chunk already unlocked + } + let remaining_blocks = chunk.unlock_block.saturating_sub(current_block_number); + chunk.unlock_block.saturating_accrue(remaining_blocks); + } + + // Override ledger + Ledger::::insert(last_key, ledger); + + // inc migrated + migrated.saturating_inc(); + + // Return the processed key as the new cursor. + cursor = Some(last_key.clone()) + } else { + // Signal that the migration is complete (no more items to process). + cursor = None; + break; + } + } + log::info!(target: LOG_TARGET, "🚚 iterated {count} entries, migrated {migrated}"); + Ok(cursor) + } +} + +/// Double the remaining block for next era start +pub struct AdjustEraMigration(PhantomData); + +impl OnRuntimeUpgrade for AdjustEraMigration { + fn on_runtime_upgrade() -> Weight { + log::info!("🚚 migrated to async backing, adjust next era start"); + ActiveProtocolState::::mutate_exists(|maybe| { + if let Some(state) = maybe { + let current_block_number = + frame_system::Pallet::::block_number().saturated_into::(); + let remaining = state.next_era_start.saturating_sub(current_block_number); + state.next_era_start.saturating_accrue(remaining); + } + }); + T::DbWeight::get().reads_writes(1, 1) + } +} diff --git a/pallets/dapp-staking/src/test/migrations.rs b/pallets/dapp-staking/src/test/migrations.rs new file mode 100644 index 000000000..d1e1109ac --- /dev/null +++ b/pallets/dapp-staking/src/test/migrations.rs @@ -0,0 +1,85 @@ +// This file is part of Astar. + +// Copyright (C) Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Astar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Astar. If not, see . + +#![cfg(all(test, not(feature = "runtime-benchmarks")))] + +use crate::test::mock::*; +use crate::{AccountLedger, CurrentEraInfo, EraInfo, Ledger, UnlockingChunk}; +use frame_support::traits::OnRuntimeUpgrade; + +#[test] +fn lazy_migrations() { + ExtBuilder::default().build_and_execute(|| { + Ledger::::set( + &1, + AccountLedger { + locked: 1000, + unlocking: vec![ + UnlockingChunk { + amount: 100, + unlock_block: 5, + }, + UnlockingChunk { + amount: 100, + unlock_block: 20, + }, + ] + .try_into() + .unwrap(), + staked: Default::default(), + staked_future: None, + contract_stake_count: 0, + }, + ); + CurrentEraInfo::::put(EraInfo { + total_locked: 1000, + unlocking: 200, + current_stake_amount: Default::default(), + next_stake_amount: Default::default(), + }); + + // go to block before migration + run_to_block(9); + + // onboard MBMs + AllPalletsWithSystem::on_runtime_upgrade(); + run_to_block(10); + + assert_eq!( + Ledger::::get(&1), + AccountLedger { + locked: 1000, + unlocking: vec![ + UnlockingChunk { + amount: 100, + unlock_block: 5, // already unlocked + }, + UnlockingChunk { + amount: 100, + unlock_block: 30, // double remaining blocks + }, + ] + .try_into() + .unwrap(), + staked: Default::default(), + staked_future: None, + contract_stake_count: 0, + } + ); + }) +} diff --git a/pallets/dapp-staking/src/test/mock.rs b/pallets/dapp-staking/src/test/mock.rs index 8fc568c95..1a1b1532c 100644 --- a/pallets/dapp-staking/src/test/mock.rs +++ b/pallets/dapp-staking/src/test/mock.rs @@ -23,7 +23,9 @@ use crate::{ }; use frame_support::{ - construct_runtime, ord_parameter_types, parameter_types, + construct_runtime, derive_impl, + migrations::MultiStepMigrator, + ord_parameter_types, parameter_types, traits::{fungible::Mutate as FunMutate, ConstBool, ConstU128, ConstU32, EitherOfDiverse}, weights::Weight, }; @@ -54,6 +56,7 @@ construct_runtime!( System: frame_system, Balances: pallet_balances, DappStaking: pallet_dapp_staking, + MultiBlockMigrations: pallet_migrations, } ); @@ -89,7 +92,7 @@ impl frame_system::Config for Test { type MaxConsumers = frame_support::traits::ConstU32<16>; type RuntimeTask = RuntimeTask; type SingleBlockMigrations = (); - type MultiBlockMigrator = (); + type MultiBlockMigrator = MultiBlockMigrations; type PreInherents = (); type PostInherents = (); type PostTransactions = (); @@ -111,6 +114,21 @@ impl pallet_balances::Config for Test { type WeightInfo = (); } +parameter_types! { + pub const MaxServiceWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000); +} + +#[derive_impl(pallet_migrations::config_preludes::TestDefaultConfig)] +impl pallet_migrations::Config for Test { + #[cfg(not(feature = "runtime-benchmarks"))] + type Migrations = + (crate::migration::LazyMigration>,); + #[cfg(feature = "runtime-benchmarks")] + type Migrations = pallet_migrations::mock_helpers::MockedMigrations; + type MigrationStatusHandler = (); + type MaxServiceWeight = MaxServiceWeight; +} + pub struct DummyPriceProvider; impl PriceProvider for DummyPriceProvider { fn average_price() -> FixedU128 { @@ -397,6 +415,9 @@ pub(crate) fn run_to_block(n: BlockNumber) { System::set_block_number(System::block_number() + 1); // This is performed outside of dapps staking but we expect it before on_initialize + // Done by Executive: + ::MultiBlockMigrator::step(); + let pre_snapshot = MemorySnapshot::new(); DappStaking::on_initialize(System::block_number()); assert_block_bump(&pre_snapshot); diff --git a/pallets/dapp-staking/src/test/mod.rs b/pallets/dapp-staking/src/test/mod.rs index f5baecd0a..7c891a6b2 100644 --- a/pallets/dapp-staking/src/test/mod.rs +++ b/pallets/dapp-staking/src/test/mod.rs @@ -16,6 +16,7 @@ // You should have received a copy of the GNU General Public License // along with Astar. If not, see . +mod migrations; pub(crate) mod mock; mod testing_utils; mod tests; diff --git a/pallets/dapp-staking/src/weights.rs b/pallets/dapp-staking/src/weights.rs index f7c46df53..31859fc35 100644 --- a/pallets/dapp-staking/src/weights.rs +++ b/pallets/dapp-staking/src/weights.rs @@ -73,6 +73,7 @@ pub trait WeightInfo { fn on_initialize_build_and_earn_to_build_and_earn() -> Weight; fn dapp_tier_assignment(x: u32, ) -> Weight; fn on_idle_cleanup() -> Weight; + fn step() -> Weight; } /// Weights for pallet_dapp_staking using the Substrate node and recommended hardware. @@ -477,6 +478,17 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } + /// Storage: `DappStaking::Ledger` (r:2 w:1) + /// Proof: `DappStaking::Ledger` (`max_values`: None, `max_size`: Some(310), added: 2785, mode: `MaxEncodedLen`) + fn step() -> Weight { + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `6560` + // Minimum execution time: 10_060_000 picoseconds. + Weight::from_parts(10_314_000, 6560) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } } // For backwards compatibility and tests @@ -880,4 +892,15 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } + /// Storage: `DappStaking::Ledger` (r:2 w:1) + /// Proof: `DappStaking::Ledger` (`max_values`: None, `max_size`: Some(310), added: 2785, mode: `MaxEncodedLen`) + fn step() -> Weight { + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `6560` + // Minimum execution time: 10_060_000 picoseconds. + Weight::from_parts(10_314_000, 6560) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } } diff --git a/pallets/inflation/src/lib.rs b/pallets/inflation/src/lib.rs index 25fa4bd50..c9baf754a 100644 --- a/pallets/inflation/src/lib.rs +++ b/pallets/inflation/src/lib.rs @@ -126,6 +126,7 @@ pub use weights::WeightInfo; #[cfg(any(feature = "runtime-benchmarks"))] pub mod benchmarking; +pub mod migration; #[cfg(test)] mod mock; #[cfg(test)] diff --git a/pallets/inflation/src/migration.rs b/pallets/inflation/src/migration.rs new file mode 100644 index 000000000..100b75a16 --- /dev/null +++ b/pallets/inflation/src/migration.rs @@ -0,0 +1,39 @@ +// This file is part of Astar. + +// Copyright (C) Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Astar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Astar. If not, see . + +use super::*; +use frame_support::pallet_prelude::Weight; +use frame_support::traits::OnRuntimeUpgrade; + +/// Half block reward for collators and treasury +pub struct AdjustBlockRewardMigration(core::marker::PhantomData); + +impl OnRuntimeUpgrade for AdjustBlockRewardMigration { + fn on_runtime_upgrade() -> Weight { + log::info!("🚚 migrated to async backing, adjust reward per block"); + ActiveInflationConfig::::mutate_exists(|maybe| { + if let Some(config) = maybe { + config.collator_reward_per_block = + config.collator_reward_per_block.saturating_div(2); + config.treasury_reward_per_block = + config.treasury_reward_per_block.saturating_div(2); + } + }); + T::DbWeight::get().reads_writes(1, 1) + } +} diff --git a/pallets/vesting-mbm/Cargo.toml b/pallets/vesting-mbm/Cargo.toml new file mode 100644 index 000000000..a990fa01d --- /dev/null +++ b/pallets/vesting-mbm/Cargo.toml @@ -0,0 +1,61 @@ +[package] +name = "vesting-mbm" +version = "1.0.0" +license = "GPL-3.0-or-later" +description = "Vesting multi block migration to support async backing" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +astar-primitives = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +log = { workspace = true } +pallet-vesting = { workspace = true } +parity-scale-codec = { workspace = true } +scale-info = { workspace = true } +sp-arithmetic = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +[dev-dependencies] +pallet-balances = { workspace = true } +pallet-migrations = { workspace = true } +serde_json = { workspace = true } + +[features] +default = ["std"] +std = [ + "sp-std/std", + "sp-io/std", + "log/std", + "scale-info/std", + "parity-scale-codec/std", + "frame-support/std", + "frame-system/std", + "frame-benchmarking?/std", + "sp-arithmetic/std", + "sp-runtime/std", + "astar-primitives/std", + "pallet-vesting/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "astar-primitives/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-vesting/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] + +try-runtime = [ + "frame-support/try-runtime", + "astar-primitives/try-runtime", + "frame-system/try-runtime", + "sp-runtime/try-runtime", + "pallet-vesting/try-runtime", +] diff --git a/pallets/vesting-mbm/src/benchmarks.rs b/pallets/vesting-mbm/src/benchmarks.rs new file mode 100644 index 000000000..c0d401bcb --- /dev/null +++ b/pallets/vesting-mbm/src/benchmarks.rs @@ -0,0 +1,73 @@ +// This file is part of Astar. + +// Copyright (C) Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Astar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Astar. If not, see . + +#![cfg(feature = "runtime-benchmarks")] + +use crate::{Config, Pallet}; +use frame_benchmarking::v2::*; +use frame_support::{ + assert_ok, migrations::SteppedMigration, traits::Currency, weights::WeightMeter, +}; +use pallet_vesting::VestingInfo; +use sp_runtime::traits::StaticLookup; +use sp_std::vec; + +#[benchmarks] +mod benches { + use super::*; + + /// Benchmark a single step of vesting migration. + #[benchmark] + fn step(x: Linear<1u32, { ::MAX_VESTING_SCHEDULES }>) { + let alice: T::AccountId = account("alice", 0, 1); + let bob: T::AccountId = account("bob", 0, 2); + + for _ in 0..x { + let _ = T::Currency::make_free_balance_be(&alice, 1_000_000u32.into()); + assert_ok!(pallet_vesting::Pallet::::vested_transfer( + frame_system::RawOrigin::Signed(alice.clone()).into(), + T::Lookup::unlookup(bob.clone()), + VestingInfo::new(1_000_000u32.into(), 10u32.into(), 0u32.into()), + )); + } + + let mut meter = WeightMeter::new(); + + #[block] + { + crate::LazyMigration::>::step(None, &mut meter) + .unwrap(); + } + + let mut expected = vec![]; + for _ in 0..x { + expected.push(VestingInfo::new( + 999_990u32.into(), + 5u32.into(), + 1u32.into(), + )); + } + + assert_eq!( + pallet_vesting::Vesting::::get(&bob).unwrap().to_vec(), + expected + ); + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Runtime); +} diff --git a/pallets/vesting-mbm/src/lib.rs b/pallets/vesting-mbm/src/lib.rs new file mode 100644 index 000000000..e2e02b313 --- /dev/null +++ b/pallets/vesting-mbm/src/lib.rs @@ -0,0 +1,143 @@ +// This file is part of Astar. + +// Copyright (C) Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Astar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Astar. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::{ + migrations::{MigrationId, SteppedMigration, SteppedMigrationError}, + weights::WeightMeter, +}; +use pallet_vesting::{Vesting, VestingInfo}; +use sp_arithmetic::traits::{SaturatedConversion, Saturating}; +use sp_runtime::{traits::BlockNumberProvider, Percent}; + +pub use pallet::*; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarks; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + +pub mod weights; + +const LOG_TARGET: &str = "mbm::vesting"; +const PALLET_MIGRATIONS_ID: &[u8; 18] = b"pallet-vesting-mbm"; + +pub struct LazyMigration(core::marker::PhantomData<(T, W)>); + +impl SteppedMigration for LazyMigration { + type Cursor = ::AccountId; + // Without the explicit length here the construction of the ID would not be infallible. + type Identifier = MigrationId<18>; + + /// The identifier of this migration. Which should be globally unique. + fn id() -> Self::Identifier { + MigrationId { + pallet_id: *PALLET_MIGRATIONS_ID, + version_from: 0, + version_to: 1, + } + } + + fn step( + mut cursor: Option, + meter: &mut WeightMeter, + ) -> Result, SteppedMigrationError> { + let required = W::step(T::MAX_VESTING_SCHEDULES); + // If there is not enough weight for a single step, return an error. This case can be + // problematic if it is the first migration that ran in this block. But there is nothing + // that we can do about it here. + if meter.remaining().any_lt(required) { + return Err(SteppedMigrationError::InsufficientWeight { required }); + } + + let mut count = 0u32; + let para_block_number = frame_system::Pallet::::block_number(); + let current_block_number = T::BlockNumberProvider::current_block_number(); + + // We loop here to do as much progress as possible per step. + loop { + // stop when remaining weight is lower than step max weight + if meter.remaining().any_lt(required) { + break; + } + + let mut iter = if let Some(last_key) = cursor { + // If a cursor is provided, start iterating from the stored value + // corresponding to the last key processed in the previous step. + // Note that this only works if the old and the new map use the same way to hash + // storage keys. + Vesting::::iter_from(Vesting::::hashed_key_for(last_key)) + } else { + // If no cursor is provided, start iterating from the beginning. + Vesting::::iter() + }; + + // If there's a next item in the iterator, perform the migration. + if let Some((ref last_key, mut schedules)) = iter.next() { + for schedule in schedules.iter_mut() { + // remaining locked balance + let locked = schedule.locked_at::(para_block_number); + // reduce unlock `per_block` into half + let per_block = Percent::from_percent(50) * schedule.per_block(); + // remaining blocks to start vesting if vesting hasn't started yet + // remaining blocks will be doubled + let remaining_blocks = schedule + .starting_block() + .saturating_sub(para_block_number) + .saturating_mul(2u32.into()); + let start_block = current_block_number.saturating_add(remaining_blocks); + + *schedule = VestingInfo::new(locked, per_block, start_block); + } + + // consume the exact weight + meter.consume(W::step(schedules.len().saturated_into())); + + // Override vesting schedules + Vesting::::insert(last_key, schedules); + + // inc counter + count.saturating_inc(); + + // Return the processed key as the new cursor. + cursor = Some(last_key.clone()) + } else { + // Signal that the migration is complete (no more items to process). + cursor = None; + break; + } + } + log::debug!(target: LOG_TARGET, "migrated {count:?} entries"); + Ok(cursor) + } +} + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::pallet] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config + pallet_vesting::Config {} +} diff --git a/pallets/vesting-mbm/src/mock.rs b/pallets/vesting-mbm/src/mock.rs new file mode 100644 index 000000000..f562c71e5 --- /dev/null +++ b/pallets/vesting-mbm/src/mock.rs @@ -0,0 +1,140 @@ +// This file is part of Astar. + +// Copyright (C) Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Astar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Astar. If not, see . + +#![cfg(test)] + +use frame_support::{ + construct_runtime, derive_impl, + migrations::MultiStepMigrator, + pallet_prelude::*, + parameter_types, + traits::{OnFinalize, OnInitialize, WithdrawReasons}, +}; +use sp_runtime::{traits::ConvertInto, BuildStorage}; + +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub struct Runtime { + System: frame_system, + Balances: pallet_balances, + Vesting: pallet_vesting, + MultiBlockMigrations: pallet_migrations, + Pallet: crate, + } +); + +impl crate::Config for Runtime {} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Runtime { + type AccountData = pallet_balances::AccountData; + type Block = Block; + type MultiBlockMigrator = MultiBlockMigrations; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Runtime { + type AccountStore = System; + type ExistentialDeposit = ExistentialDeposit; +} + +parameter_types! { + pub const MaxServiceWeight: Weight = Weight::from_parts(1_000_000_000, 1_000_000); +} + +#[derive_impl(pallet_migrations::config_preludes::TestDefaultConfig)] +impl pallet_migrations::Config for Runtime { + #[cfg(not(feature = "runtime-benchmarks"))] + type Migrations = (crate::LazyMigration>,); + #[cfg(feature = "runtime-benchmarks")] + type Migrations = pallet_migrations::mock_helpers::MockedMigrations; + type MigrationStatusHandler = (); + type MaxServiceWeight = MaxServiceWeight; +} + +parameter_types! { + pub const MinVestedTransfer: u64 = 256 * 2; + pub UnvestedFundsAllowedWithdrawReasons: WithdrawReasons = + WithdrawReasons::except(WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE); + pub static ExistentialDeposit: u64 = 1; +} + +impl pallet_vesting::Config for Runtime { + type BlockNumberToBalance = ConvertInto; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + const MAX_VESTING_SCHEDULES: u32 = 3; + type MinVestedTransfer = MinVestedTransfer; + type WeightInfo = (); + type UnvestedFundsAllowedWithdrawReasons = UnvestedFundsAllowedWithdrawReasons; + type BlockNumberProvider = System; +} + +#[derive(Default)] +pub struct ExtBuilder; + +pub(crate) const ALICE: u64 = 1; +pub(crate) const BOB: u64 = 2; +pub(crate) const CHARLIE: u64 = 3; +pub(crate) const DAVE: u64 = 4; + +impl ExtBuilder { + pub fn build(self) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![(ALICE, 100), (BOB, 100), (CHARLIE, 1000), (DAVE, 800)], + } + .assimilate_storage(&mut t) + .unwrap(); + + let vesting = vec![ + // who, start_at, length, liquid + (ALICE, 0, 10, 0), + (BOB, 10, 10, 0), + (CHARLIE, 20, 10, 0), + (DAVE, 5, 20, 400), + ]; + + pallet_vesting::GenesisConfig:: { vesting } + .assimilate_storage(&mut t) + .unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext + } +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + ExtBuilder::default().build() +} + +#[allow(dead_code)] +pub fn run_to_block(n: u64) { + assert!(System::block_number() < n); + while System::block_number() < n { + let b = System::block_number(); + AllPalletsWithSystem::on_finalize(b); + // Done by Executive: + ::MultiBlockMigrator::step(); + System::set_block_number(b + 1); + AllPalletsWithSystem::on_initialize(b + 1); + } +} diff --git a/pallets/vesting-mbm/src/tests.rs b/pallets/vesting-mbm/src/tests.rs new file mode 100644 index 000000000..052c18b69 --- /dev/null +++ b/pallets/vesting-mbm/src/tests.rs @@ -0,0 +1,125 @@ +// This file is part of Astar. + +// Copyright (C) Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Astar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Astar. If not, see . + +#![cfg(all(test, not(feature = "runtime-benchmarks")))] + +use crate::mock::{ + new_test_ext, run_to_block, AllPalletsWithSystem, Balances, RuntimeOrigin, Vesting, ALICE, BOB, + CHARLIE, DAVE, +}; +use frame_support::{ + assert_ok, + traits::{OnRuntimeUpgrade, VestingSchedule}, +}; +use pallet_balances::{BalanceLock, Reasons}; +use pallet_vesting::VestingInfo; + +#[test] +fn migrate_to_new_block_time() { + new_test_ext().execute_with(|| { + assert_eq!(Vesting::vesting_balance(&ALICE), Some(90)); // 10 vested at block 1, 10 per block + assert_eq!(Vesting::vesting_balance(&BOB), Some(100)); + assert_eq!(Vesting::vesting_balance(&CHARLIE), Some(1000)); + assert_eq!(Vesting::vesting_balance(&DAVE), Some(400)); + + run_to_block(10); + + assert_eq!(Vesting::vesting_balance(&ALICE), Some(0)); // Alice fully vested at block 10 + assert_eq!(Vesting::vesting_balance(&BOB), Some(100)); // starts now, 10 per block + assert_eq!(Vesting::vesting_balance(&CHARLIE), Some(1000)); // starts at 20 + assert_eq!(Vesting::vesting_balance(&DAVE), Some(300)); // starts vesting at block 5, 20 per block + + assert_eq!( + Vesting::vesting(&BOB).unwrap().to_vec(), + vec![VestingInfo::new(100, 10, 10)] + ); + assert_eq!( + Vesting::vesting(&CHARLIE).unwrap().to_vec(), + vec![VestingInfo::new(1000, 100, 20)] + ); + assert_eq!( + Vesting::vesting(&DAVE).unwrap().to_vec(), + vec![VestingInfo::new(400, 20, 5)] + ); + + // onboard MBMs + AllPalletsWithSystem::on_runtime_upgrade(); + run_to_block(11); + + assert_eq!(Vesting::vesting_balance(&ALICE), Some(0)); // Alice remains the same + assert_eq!(Vesting::vesting_balance(&BOB), Some(95)); // 5 unlocked + assert_eq!(Vesting::vesting_balance(&CHARLIE), Some(1000)); // starts at 30 now, doubled remaining blocks + assert_eq!(Vesting::vesting_balance(&DAVE), Some(290)); // 10 unlocked + + assert_eq!( + Vesting::vesting(&BOB).unwrap().to_vec(), + vec![VestingInfo::new(100, 5, 10)] + ); + assert_eq!( + Vesting::vesting(&CHARLIE).unwrap().to_vec(), + vec![VestingInfo::new(1000, 50, 30)] + ); + assert_eq!( + Vesting::vesting(&DAVE).unwrap().to_vec(), + vec![VestingInfo::new(300, 10, 10)] + ); + + // lock is updated when `PalletVesting::vest` is called + assert_eq!( + Balances::locks(&BOB).to_vec(), + vec![BalanceLock { + id: *b"vesting ", + amount: 100, + reasons: Reasons::Misc, + }] + ); + // call vest to unlock vested funds + assert_ok!(Vesting::vest(RuntimeOrigin::signed(BOB))); + assert_eq!( + Balances::locks(&BOB).to_vec(), + vec![BalanceLock { + id: *b"vesting ", + amount: 95, + reasons: Reasons::Misc, + }] + ); + + run_to_block(29); + assert_eq!(Vesting::vesting_balance(&BOB), Some(5)); + // Bob will fully vest at 30 + run_to_block(30); + assert_eq!(Vesting::vesting_balance(&BOB), Some(0)); + // call vest to unlock vested funds + assert_ok!(Vesting::vest(RuntimeOrigin::signed(BOB))); + assert_eq!(Balances::locks(&BOB).to_vec(), vec![]); + + run_to_block(39); + assert_eq!(Vesting::vesting_balance(&CHARLIE), Some(550)); // started vesting at 30 + assert_eq!(Vesting::vesting_balance(&DAVE), Some(10)); + + // Dave will fully vest at 40 + run_to_block(40); + assert_eq!(Vesting::vesting_balance(&DAVE), Some(0)); + + run_to_block(49); + assert_eq!(Vesting::vesting_balance(&CHARLIE), Some(50)); + // Charlie will fully vest at 50, (50 per block starting at block 30) + run_to_block(50); + assert_eq!(Vesting::vesting_balance(&CHARLIE), Some(0)); + }); +} diff --git a/pallets/vesting-mbm/src/weights.rs b/pallets/vesting-mbm/src/weights.rs new file mode 100644 index 000000000..52940c768 --- /dev/null +++ b/pallets/vesting-mbm/src/weights.rs @@ -0,0 +1,89 @@ + +// This file is part of Astar. + +// Copyright (C) Stake Technologies Pte.Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later + +// Astar is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Astar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Astar. If not, see . + +//! Autogenerated weights for vesting_mbm +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-08-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `gh-runner-01-ovh`, CPU: `Intel(R) Xeon(R) E-2236 CPU @ 3.40GHz` +//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("shibuya-dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/astar-collator +// benchmark +// pallet +// --chain=shibuya-dev +// --steps=50 +// --repeat=20 +// --pallet=vesting_mbm +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./benchmark-results/shibuya-dev/mbm_weights.rs +// --template=./scripts/templates/weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for vesting_mbm. +pub trait WeightInfo { + fn step(x: u32, ) -> Weight; +} + +/// Weights for vesting_mbm using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `Vesting::Vesting` (r:2 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// The range of component `x` is `[1, 28]`. + fn step(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `316 + x * (36 ±0)` + // Estimated: `8054` + // Minimum execution time: 13_079_000 picoseconds. + Weight::from_parts(13_513_131, 8054) + // Standard Error: 723 + .saturating_add(Weight::from_parts(102_389, 0).saturating_mul(x.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: `Vesting::Vesting` (r:2 w:1) + /// Proof: `Vesting::Vesting` (`max_values`: None, `max_size`: Some(1057), added: 3532, mode: `MaxEncodedLen`) + /// The range of component `x` is `[1, 28]`. + fn step(x: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `316 + x * (36 ±0)` + // Estimated: `8054` + // Minimum execution time: 13_079_000 picoseconds. + Weight::from_parts(13_513_131, 8054) + // Standard Error: 723 + .saturating_add(Weight::from_parts(102_389, 0).saturating_mul(x.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} diff --git a/runtime/astar/src/weights/pallet_dapp_staking.rs b/runtime/astar/src/weights/pallet_dapp_staking.rs index 6bb3456bb..29b26bbff 100644 --- a/runtime/astar/src/weights/pallet_dapp_staking.rs +++ b/runtime/astar/src/weights/pallet_dapp_staking.rs @@ -467,4 +467,15 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } + /// Storage: `DappStaking::Ledger` (r:2 w:1) + /// Proof: `DappStaking::Ledger` (`max_values`: None, `max_size`: Some(310), added: 2785, mode: `MaxEncodedLen`) + fn step() -> Weight { + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `6560` + // Minimum execution time: 10_060_000 picoseconds. + Weight::from_parts(10_314_000, 6560) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } } diff --git a/runtime/shibuya/Cargo.toml b/runtime/shibuya/Cargo.toml index 967349c82..1baec6901 100644 --- a/runtime/shibuya/Cargo.toml +++ b/runtime/shibuya/Cargo.toml @@ -78,6 +78,7 @@ pallet-transaction-payment-rpc-runtime-api = { workspace = true } pallet-treasury = { workspace = true } pallet-utility = { workspace = true } pallet-vesting = { workspace = true } +vesting-mbm = { workspace = true } # cumulus dependencies cumulus-pallet-aura-ext = { workspace = true } @@ -264,6 +265,7 @@ std = [ "pallet-xcm-benchmarks?/std", "precompile-utils/std", "scale-info/std", + "vesting-mbm/std", ] runtime-benchmarks = [ "astar-xcm-benchmarks/runtime-benchmarks", @@ -321,6 +323,7 @@ runtime-benchmarks = [ "cumulus-primitives-core/runtime-benchmarks", "parachains-common/runtime-benchmarks", "orml-oracle/runtime-benchmarks", + "vesting-mbm/runtime-benchmarks", ] try-runtime = [ "fp-self-contained/try-runtime", @@ -377,6 +380,7 @@ try-runtime = [ "pallet-chain-extension-assets/try-runtime", "polkadot-runtime-common/try-runtime", "sp-runtime/try-runtime", + "vesting-mbm/try-runtime", ] evm-tracing = [ "moonbeam-evm-tracer", diff --git a/runtime/shibuya/src/lib.rs b/runtime/shibuya/src/lib.rs index 0c4ff317d..cd05f7511 100644 --- a/runtime/shibuya/src/lib.rs +++ b/runtime/shibuya/src/lib.rs @@ -147,7 +147,7 @@ pub const fn contracts_deposit(items: u32, bytes: u32) -> Balance { } /// Change this to adjust the block time. -pub const MILLISECS_PER_BLOCK: u64 = 12000; +pub const MILLISECS_PER_BLOCK: u64 = 6000; pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; // Time is measured by number of blocks. @@ -157,7 +157,7 @@ pub const DAYS: BlockNumber = HOURS * 24; /// Maximum number of blocks simultaneously accepted by the Runtime, not yet included into the /// relay chain. -pub const UNINCLUDED_SEGMENT_CAPACITY: u32 = 1; +pub const UNINCLUDED_SEGMENT_CAPACITY: u32 = 3; /// How many parachain blocks are processed by the relay chain per parent. Limits the number of /// blocks authored per slot. pub const BLOCK_PROCESSING_VELOCITY: u32 = 1; @@ -232,9 +232,9 @@ const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10); /// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used /// by Operational extrinsics. const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); -/// We allow for 0.5 seconds of compute with a 6 second average block time. +/// We allow for 2 seconds of compute with a 6 second average block time. const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( - WEIGHT_REF_TIME_PER_SECOND.saturating_div(2), + WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), polkadot_primitives::MAX_POV_SIZE as u64, ); @@ -332,15 +332,10 @@ impl frame_system::Config for Runtime { type PostTransactions = (); } -parameter_types! { - pub const MinimumPeriod: u64 = MILLISECS_PER_BLOCK / 2; -} - impl pallet_timestamp::Config for Runtime { - /// A timestamp: milliseconds since the unix epoch. type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = MinimumPeriod; + type OnTimestampSet = Aura; + type MinimumPeriod = ConstU64<0>; type WeightInfo = pallet_timestamp::weights::SubstrateWeight; } @@ -572,8 +567,7 @@ impl pallet_aura::Config for Runtime { type DisabledValidators = (); type MaxAuthorities = ConstU32<250>; type SlotDuration = ConstU64; - // Set to `true` once async backing is enabled. - type AllowMultipleBlocksPerSlot = ConstBool; + type AllowMultipleBlocksPerSlot = ConstBool; } impl cumulus_pallet_aura_ext::Config for Runtime {} @@ -714,6 +708,9 @@ impl pallet_vesting::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type BlockNumberToBalance = ConvertInto; + #[cfg(feature = "runtime-benchmarks")] + type MinVestedTransfer = ConstU128<1>; + #[cfg(not(feature = "runtime-benchmarks"))] type MinVestedTransfer = MinVestedTransfer; type WeightInfo = pallet_vesting::weights::SubstrateWeight; type UnvestedFundsAllowedWithdrawReasons = UnvestedFundsAllowedWithdrawReasons; @@ -926,8 +923,8 @@ parameter_types! { /// max_gas_limit = max_tx_ref_time / WEIGHT_PER_GAS = max_pov_size * gas_limit_pov_size_ratio /// gas_limit_pov_size_ratio = ceil((max_tx_ref_time / WEIGHT_PER_GAS) / max_pov_size) /// - /// Equals 4 for values used by Shibuya runtime. - pub const GasLimitPovSizeRatio: u64 = 4; + /// Equals 16 for values used by Shibuya runtime. + pub const GasLimitPovSizeRatio: u64 = 16; } impl pallet_evm::Config for Runtime { @@ -1187,7 +1184,7 @@ parameter_types! { // Of course it's not true for Shibuya, but SBY is worthless, a test token. pub const NativeCurrencyId: CurrencyId = CurrencyId::ASTR; // Aggregate values for one day. - pub const AggregationDuration: BlockNumber = 7200; + pub const AggregationDuration: BlockNumber = DAYS; } impl pallet_price_aggregator::Config for Runtime { @@ -1521,7 +1518,13 @@ parameter_types! { impl pallet_migrations::Config for Runtime { type RuntimeEvent = RuntimeEvent; #[cfg(not(feature = "runtime-benchmarks"))] - type Migrations = (); + type Migrations = ( + pallet_dapp_staking::migration::LazyMigration< + Runtime, + pallet_dapp_staking::weights::SubstrateWeight, + >, + vesting_mbm::LazyMigration>, + ); // Benchmarks need mocked migrations to guarantee that they succeed. #[cfg(feature = "runtime-benchmarks")] type Migrations = pallet_migrations::mock_helpers::MockedMigrations; @@ -1533,6 +1536,9 @@ impl pallet_migrations::Config for Runtime { type WeightInfo = pallet_migrations::weights::SubstrateWeight; } +#[cfg(feature = "runtime-benchmarks")] +impl vesting_mbm::Config for Runtime {} + construct_runtime!( pub struct Runtime { @@ -1599,6 +1605,9 @@ construct_runtime!( CollectiveProxy: pallet_collective_proxy = 109, MultiBlockMigrations: pallet_migrations = 120, + + #[cfg(feature = "runtime-benchmarks")] + VestingMBM: vesting_mbm = 250, } ); @@ -1637,33 +1646,6 @@ pub type Executive = frame_executive::Executive< Migrations, >; -parameter_types! { - // Threshold amount variation allowed for this migration - 150% - pub const ThresholdVariationPercentage: u32 = 150; - // percentages below are calculated based on a total issuance at the time when dApp staking v3 was launched (147M) - pub const TierThresholds: [TierThreshold; 4] = [ - TierThreshold::DynamicPercentage { - percentage: Perbill::from_parts(20_000), // 0.0020% - minimum_required_percentage: Perbill::from_parts(17_000), // 0.0017% - }, - TierThreshold::DynamicPercentage { - percentage: Perbill::from_parts(13_000), // 0.0013% - minimum_required_percentage: Perbill::from_parts(10_000), // 0.0010% - }, - TierThreshold::DynamicPercentage { - percentage: Perbill::from_parts(5_400), // 0.00054% - minimum_required_percentage: Perbill::from_parts(3_400), // 0.00034% - }, - TierThreshold::FixedPercentage { - required_percentage: Perbill::from_parts(1_400), // 0.00014% - }, - ]; -} - -parameter_types! { - pub const DmpQueuePalletName: &'static str = "DmpQueue"; -} - /// All migrations that will run on the next runtime upgrade. /// /// __NOTE:__ THE ORDER IS IMPORTANT. @@ -1671,17 +1653,8 @@ pub type Migrations = (Unreleased, Permanent); /// Unreleased migrations. Add new ones here: pub type Unreleased = ( - // dApp-staking dyn tier threshold migrations - pallet_dapp_staking::migration::versioned_migrations::V7ToV8< - Runtime, - TierThresholds, - ThresholdVariationPercentage, - >, - frame_support::migrations::RemovePallet< - DmpQueuePalletName, - ::DbWeight, - >, - pallet_contracts::Migration, + pallet_dapp_staking::migration::AdjustEraMigration, + pallet_inflation::migration::AdjustBlockRewardMigration, ); /// Migrations/checks that do not need to be versioned and can run on every upgrade. @@ -1776,6 +1749,7 @@ mod benches { [pallet_price_aggregator, PriceAggregator] [pallet_collective_proxy, CollectiveProxy] [orml_oracle, Oracle] + [vesting_mbm, VestingMBM] ); } diff --git a/runtime/shibuya/src/weights/pallet_dapp_staking.rs b/runtime/shibuya/src/weights/pallet_dapp_staking.rs index 1b2808c35..e10cd8d18 100644 --- a/runtime/shibuya/src/weights/pallet_dapp_staking.rs +++ b/runtime/shibuya/src/weights/pallet_dapp_staking.rs @@ -467,4 +467,15 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } + /// Storage: `DappStaking::Ledger` (r:2 w:1) + /// Proof: `DappStaking::Ledger` (`max_values`: None, `max_size`: Some(310), added: 2785, mode: `MaxEncodedLen`) + fn step() -> Weight { + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `6560` + // Minimum execution time: 10_060_000 picoseconds. + Weight::from_parts(10_314_000, 6560) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } } diff --git a/runtime/shiden/src/weights/pallet_dapp_staking.rs b/runtime/shiden/src/weights/pallet_dapp_staking.rs index 9ccd24fab..169284360 100644 --- a/runtime/shiden/src/weights/pallet_dapp_staking.rs +++ b/runtime/shiden/src/weights/pallet_dapp_staking.rs @@ -467,4 +467,15 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } + /// Storage: `DappStaking::Ledger` (r:2 w:1) + /// Proof: `DappStaking::Ledger` (`max_values`: None, `max_size`: Some(310), added: 2785, mode: `MaxEncodedLen`) + fn step() -> Weight { + // Proof Size summary in bytes: + // Measured: `76` + // Estimated: `6560` + // Minimum execution time: 10_060_000 picoseconds. + Weight::from_parts(10_314_000, 6560) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } } diff --git a/tests/integration/Cargo.toml b/tests/integration/Cargo.toml index 19e075fc5..2df214f42 100644 --- a/tests/integration/Cargo.toml +++ b/tests/integration/Cargo.toml @@ -34,8 +34,10 @@ pallet-contracts-uapi = { workspace = true } pallet-democracy = { workspace = true } pallet-membership = { workspace = true } pallet-proxy = { workspace = true } +pallet-session = { workspace = true } pallet-timestamp = { workspace = true } pallet-utility = { workspace = true } +sp-consensus-aura = { workspace = true } sp-core = { workspace = true } sp-io = { workspace = true } sp-runtime = { workspace = true } @@ -121,6 +123,8 @@ std = [ "unified-accounts-chain-extension-types/std", "xcm/std", "xcm-fee-payment-runtime-api/std", + "pallet-session/std", + "sp-consensus-aura/std", ] shibuya = ["shibuya-runtime"] shiden = ["shiden-runtime"] diff --git a/tests/integration/src/setup.rs b/tests/integration/src/setup.rs index 7fcf40649..de2adf322 100644 --- a/tests/integration/src/setup.rs +++ b/tests/integration/src/setup.rs @@ -24,20 +24,20 @@ pub use frame_support::{ weights::Weight, }; use parity_scale_codec::Encode; -pub use sp_core::{Get, H160}; -pub use sp_runtime::{AccountId32, MultiAddress}; +pub use sp_core::{sr25519, Get, H160}; +pub use sp_runtime::{AccountId32, Digest, DigestItem, MultiAddress}; use cumulus_primitives_core::{relay_chain::HeadData, PersistedValidationData}; use cumulus_primitives_parachain_inherent::ParachainInherentData; use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder; +use sp_consensus_aura::{Slot, SlotDuration, AURA_ENGINE_ID}; +#[cfg(feature = "shibuya")] +pub use astar_primitives::governance::{ + CommunityCouncilMembershipInst, MainCouncilMembershipInst, TechnicalCommitteeMembershipInst, +}; pub use astar_primitives::{ - governance::{ - CommunityCouncilMembershipInst, MainCouncilMembershipInst, OracleMembershipInst, - TechnicalCommitteeMembershipInst, - }, - oracle::Price, - BlockNumber, + genesis::GenesisAccount, governance::OracleMembershipInst, oracle::Price, BlockNumber, }; #[cfg(feature = "shibuya")] @@ -104,11 +104,13 @@ pub const INITIAL_AMOUNT: u128 = 100_000 * UNIT; pub const INIT_PRICE: Price = Price::from_rational(1, 10); pub type SystemError = frame_system::Error; +use cumulus_pallet_parachain_system::RelaychainDataProvider; pub use pallet_balances::Call as BalancesCall; pub use pallet_dapp_staking as DappStakingCall; pub use pallet_proxy::Event as ProxyEvent; pub use pallet_utility::{Call as UtilityCall, Event as UtilityEvent}; use parity_scale_codec::Decode; +use sp_runtime::traits::BlockNumberProvider; pub struct ExtBuilder { balances: Vec<(AccountId32, Balance)>, @@ -137,6 +139,16 @@ impl ExtBuilder { .assimilate_storage(&mut t) .unwrap(); + as BuildStorage>::assimilate_storage( + &pallet_aura::GenesisConfig:: { + authorities: vec![GenesisAccount::::from_seed("Alice") + .pub_key() + .into()], + }, + &mut t, + ) + .unwrap(); + // Setup initial oracle members as BuildStorage>::assimilate_storage( &pallet_membership::GenesisConfig:: { @@ -192,14 +204,22 @@ impl ExtBuilder { let mut ext = sp_io::TestExternalities::new(t); ext.execute_with(|| { - System::set_block_number(1); - - pallet_dapp_staking::Safeguard::::put(false); - // Ensure the initial state is set for the first block - AllPalletsWithSystem::on_initialize(1); + System::initialize( + &1, + &Default::default(), + &Digest { + logs: vec![DigestItem::PreRuntime( + AURA_ENGINE_ID, + Slot::from(1).encode(), + )], + }, + ); + AllPalletsWithoutSystem::on_initialize(1); set_timestamp(); set_validation_data(); + + pallet_dapp_staking::Safeguard::::put(false); }); ext } @@ -223,20 +243,23 @@ fn set_timestamp() { } fn set_validation_data() { - let block_number = System::block_number(); let para_id = ::SelfParaId::get(); let parent_head = HeadData(b"deadbeef".into()); let sproof_builder = RelayStateSproofBuilder { para_id, included_para_head: Some(parent_head.clone()), + current_slot: Slot::from_timestamp( + pallet_timestamp::Now::::get().into(), + SlotDuration::from_millis(6_000), + ), ..Default::default() }; let (relay_parent_storage_root, relay_chain_state) = sproof_builder.into_state_root_and_proof(); let para_inherent_data = ParachainInherentData { validation_data: PersistedValidationData { parent_head, - relay_parent_number: block_number, + relay_parent_number: RelaychainDataProvider::::current_block_number() + 1, relay_parent_storage_root, max_pov_size: 5_000_000, }, @@ -271,8 +294,18 @@ pub fn run_to_block(n: BlockNumber) { ); // initialize block - System::set_block_number(block_number + 1); - AllPalletsWithSystem::on_initialize(block_number + 1); + let slot = Slot::from_timestamp( + (pallet_timestamp::Now::::get() + SLOT_DURATION).into(), + SlotDuration::from_millis(SLOT_DURATION), + ); + System::initialize( + &(block_number + 1), + &Default::default(), + &Digest { + logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())], + }, + ); + AllPalletsWithoutSystem::on_initialize(block_number + 1); // apply inherent set_timestamp(); set_validation_data();