Skip to content

Commit

Permalink
task: v9 mb migration for bonus-status
Browse files Browse the repository at this point in the history
  • Loading branch information
ipapandinas committed Jan 13, 2025
1 parent 6e6c56b commit df12ac8
Show file tree
Hide file tree
Showing 9 changed files with 314 additions and 9 deletions.
38 changes: 38 additions & 0 deletions pallets/dapp-staking/src/benchmarking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1191,6 +1191,44 @@ mod benchmarks {
);
}

/// Benchmark a single step of v9 mbm migration (for bonus_status).
#[benchmark]
fn mbm_step_v9_bonus_status() {
let alice: T::AccountId = account("alice", 0, 1);
let smart_contract = T::BenchmarkHelper::get_smart_contract(1);

crate::migration::v8::StakerInfo::<T>::set(
&alice,
&smart_contract,
Some(crate::migration::v8::SingularStakingInfo {
previous_staked: Default::default(),
staked: Default::default(),
loyal_staker: true,
}),
);

let mut meter = WeightMeter::new();

#[block]
{
crate::migration::v9::LazyMigrationBonusStatus::<T, weights::SubstrateWeight<T>>::step(
None, &mut meter,
)
.unwrap();
}

let expected_staker_info = SingularStakingInfoFor::<T> {
previous_staked: Default::default(),
staked: Default::default(),
bonus_status: BonusStatus::SafeMovesRemaining(0),
};

assert!(match StakerInfo::<T>::get(&alice, &smart_contract) {
Some(staker_info) => staker_info.equals(&expected_staker_info),
_ => false,
});
}

impl_benchmark_test_suite!(
Pallet,
crate::benchmarking::tests::new_test_ext(),
Expand Down
4 changes: 2 additions & 2 deletions pallets/dapp-staking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ pub mod pallet {
use super::*;

/// The current storage version.
pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(8);
pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(9);

#[pallet::pallet]
#[pallet::storage_version(STORAGE_VERSION)]
Expand Down Expand Up @@ -1534,7 +1534,7 @@ pub mod pallet {
Self::internal_claim_bonus_reward_for(account, smart_contract)
}

/// Transfers stake between two smart contracts, ensuring period alignment, bonus status preservation if elegible,
/// Transfers stake between two smart contracts, ensuring period alignment, bonus status preservation if elegible,
/// and adherence to staking limits. Updates all relevant storage and emits a `StakeMoved` event.
#[pallet::call_index(21)]
#[pallet::weight(T::WeightInfo::move_stake())]
Expand Down
123 changes: 122 additions & 1 deletion pallets/dapp-staking/src/migration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,133 @@ pub mod versioned_migrations {
>;
}

pub mod v9 {
use super::*;

// The loyal staker flag is replaced by 'BonusStatus'
pub struct LazyMigrationBonusStatus<T, W: WeightInfo>(PhantomData<(T, W)>);

impl<T: Config, W: WeightInfo> SteppedMigration for LazyMigrationBonusStatus<T, W> {
type Cursor = (<T as frame_system::Config>::AccountId, T::SmartContract);
// 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: 8,
version_to: 9,
}
}

fn step(
mut cursor: Option<Self::Cursor>,
meter: &mut WeightMeter,
) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
let on_chain_version = Pallet::<T>::on_chain_storage_version();
if on_chain_version != 9 {
return Ok(None);
}

let required = W::mbm_step_v9_bonus_status();

// 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;

loop {
if meter.try_consume(required).is_err() {
break;
}

let mut iter = if let Some(last_key_pair) = cursor {
// If a cursor is provided, start iterating from the stored value
// corresponding to the last key pair 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.
v8::StakerInfo::<T>::iter_from(v8::StakerInfo::<T>::hashed_key_for(
last_key_pair.0,
last_key_pair.1,
))
} else {
// If no cursor is provided, start iterating from the beginning.
v8::StakerInfo::<T>::iter()
};

if let Some((account, smart_contract, old_staking_info)) = iter.next() {
// inc count
count.saturating_inc();

let bonus_status = if old_staking_info.loyal_staker {
BonusStatusFor::<T>::SafeMovesRemaining(0)
} else {
BonusStatusFor::<T>::BonusForfeited
};

let new_staking_info = SingularStakingInfoFor::<T> {
previous_staked: old_staking_info.previous_staked,
staked: old_staking_info.staked,
bonus_status,
};

// Override StakerInfo
StakerInfo::<T>::insert(&account, &smart_contract, new_staking_info);

// inc migrated
migrated.saturating_inc();

// Return the processed key pair as the new cursor.
cursor = Some((account, smart_contract))
} 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)
}
}
}

// TierThreshold as percentage of the total issuance
mod v8 {
pub mod v8 {
use super::*;
use crate::migration::v7::TierParameters as TierParametersV7;
use crate::migration::v7::TiersConfiguration as TiersConfigurationV7;

/// Information about how much a particular staker staked on a particular smart contract.
#[derive(
Encode, Decode, MaxEncodedLen, Copy, Clone, Debug, PartialEq, Eq, TypeInfo, Default,
)]
pub struct SingularStakingInfo {
/// Amount staked before, if anything.
pub(crate) previous_staked: StakeAmount,
/// Staked amount
pub(crate) staked: StakeAmount,
/// Indicates whether a staker is a loyal staker or not.
pub(crate) loyal_staker: bool,
}

/// v8 type for [`crate::StakerInfo`]
#[storage_alias]
pub type StakerInfo<T: Config> = StorageDoubleMap<
Pallet<T>,
Blake2_128Concat,
<T as frame_system::Config>::AccountId,
Blake2_128Concat,
<T as Config>::SmartContract,
SingularStakingInfo,
OptionQuery,
>;

pub struct VersionMigrateV7ToV8<T, TierThresholds, ThresholdVariationPercentage>(
PhantomData<(T, TierThresholds, ThresholdVariationPercentage)>,
);
Expand Down
90 changes: 89 additions & 1 deletion pallets/dapp-staking/src/test/migrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,14 @@
#![cfg(all(test, not(feature = "runtime-benchmarks")))]

use crate::test::mock::*;
use crate::{AccountLedger, CurrentEraInfo, EraInfo, Ledger, UnlockingChunk};
use crate::{
AccountLedger, BonusStatus, CurrentEraInfo, EraInfo, Ledger, SingularStakingInfoFor,
StakerInfo, UnlockingChunk,
};
use frame_support::traits::OnRuntimeUpgrade;

use astar_primitives::dapp_staking::SmartContractHandle;

#[test]
fn lazy_migrations() {
ExtBuilder::default().build_and_execute(|| {
Expand Down Expand Up @@ -83,3 +88,86 @@ fn lazy_migrations() {
);
})
}

#[test]
fn lazy_migrations_bonus_status() {
ExtBuilder::default().build_and_execute(|| {
let account_1 = 1;
let account_2 = 2;
let contract_1 = MockSmartContract::wasm(1 as AccountId);
let contract_2 = MockSmartContract::wasm(2 as AccountId);
let contract_3 = MockSmartContract::wasm(3 as AccountId);

crate::migration::v8::StakerInfo::<Test>::set(
&account_1,
&contract_1,
Some(crate::migration::v8::SingularStakingInfo {
previous_staked: Default::default(),
staked: Default::default(),
loyal_staker: true,
}),
);
crate::migration::v8::StakerInfo::<Test>::set(
&account_1,
&contract_2,
Some(crate::migration::v8::SingularStakingInfo {
previous_staked: Default::default(),
staked: Default::default(),
loyal_staker: false,
}),
);
crate::migration::v8::StakerInfo::<Test>::set(
&account_2,
&contract_1,
Some(crate::migration::v8::SingularStakingInfo {
previous_staked: Default::default(),
staked: Default::default(),
loyal_staker: false,
}),
);
crate::migration::v8::StakerInfo::<Test>::set(
&account_2,
&contract_3,
Some(crate::migration::v8::SingularStakingInfo {
previous_staked: Default::default(),
staked: Default::default(),
loyal_staker: true,
}),
);

// go to block before migration
run_to_block(9);

// onboard MBMs
AllPalletsWithSystem::on_runtime_upgrade();
run_to_block(10);

let expected_staker_info_with_bonus = SingularStakingInfoFor::<Test> {
previous_staked: Default::default(),
staked: Default::default(),
bonus_status: BonusStatus::SafeMovesRemaining(0),
};
let expected_staker_info_without_bonus = SingularStakingInfoFor::<Test> {
previous_staked: Default::default(),
staked: Default::default(),
bonus_status: BonusStatus::BonusForfeited,
};

assert!(match StakerInfo::<Test>::get(&account_1, &contract_1) {
Some(staker_info) => staker_info.equals(&expected_staker_info_with_bonus),
_ => false,
});
assert!(match StakerInfo::<Test>::get(&account_1, &contract_2) {
Some(staker_info) => staker_info.equals(&expected_staker_info_without_bonus),
_ => false,
});
assert!(match StakerInfo::<Test>::get(&account_2, &contract_1) {
Some(staker_info) => staker_info.equals(&expected_staker_info_without_bonus),
_ => false,
});
assert!(match StakerInfo::<Test>::get(&account_2, &contract_3) {
Some(staker_info) => staker_info.equals(&expected_staker_info_with_bonus),
_ => false,
});
})
}
6 changes: 4 additions & 2 deletions pallets/dapp-staking/src/test/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,10 @@ parameter_types! {
#[derive_impl(pallet_migrations::config_preludes::TestDefaultConfig)]
impl pallet_migrations::Config for Test {
#[cfg(not(feature = "runtime-benchmarks"))]
type Migrations =
(crate::migration::LazyMigration<Test, crate::weights::SubstrateWeight<Test>>,);
type Migrations = (
crate::migration::LazyMigration<Test, crate::weights::SubstrateWeight<Test>>,
crate::migration::v9::LazyMigrationBonusStatus<Test, crate::weights::SubstrateWeight<Test>>,
);
#[cfg(feature = "runtime-benchmarks")]
type Migrations = pallet_migrations::mock_helpers::MockedMigrations;
type MigrationStatusHandler = ();
Expand Down
23 changes: 23 additions & 0 deletions pallets/dapp-staking/src/weights.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ pub trait WeightInfo {
fn dapp_tier_assignment(x: u32, ) -> Weight;
fn on_idle_cleanup() -> Weight;
fn step() -> Weight;
fn mbm_step_v9_bonus_status() -> Weight;
}

/// Weights for pallet_dapp_staking using the Substrate node and recommended hardware.
Expand Down Expand Up @@ -511,6 +512,17 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
/// Storage: `DappStaking::StakerInfo` (r:2 w:1)
/// Proof: `DappStaking::StakerInfo` (`max_values`: None, `max_size`: Some(179), added: 2654, mode: `MaxEncodedLen`)
fn mbm_step_v9_bonus_status() -> Weight {
// Proof Size summary in bytes:
// Measured: `150`
// Estimated: `6298`
// Minimum execution time: 15_000_000 picoseconds.
Weight::from_parts(15_000_000, 6298)
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
}

// For backwards compatibility and tests
Expand Down Expand Up @@ -946,4 +958,15 @@ impl WeightInfo for () {
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
/// Storage: `DappStaking::StakerInfo` (r:2 w:1)
/// Proof: `DappStaking::StakerInfo` (`max_values`: None, `max_size`: Some(179), added: 2654, mode: `MaxEncodedLen`)
fn mbm_step_v9_bonus_status() -> Weight {
// Proof Size summary in bytes:
// Measured: `150`
// Estimated: `6298`
// Minimum execution time: 15_000_000 picoseconds.
Weight::from_parts(15_000_000, 6298)
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
}
13 changes: 12 additions & 1 deletion runtime/astar/src/weights/pallet_dapp_staking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
// --output=./benchmark-results/astar-dev/dapp_staking_weights.rs
// --template=./scripts/templates/weight-template.hbs

// TODO: Dummy values for move_stake, do proper benchmark using gha
// TODO: Dummy values for move_stake & mbm_step_v9_bonus_status: do proper benchmark using gha

#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
Expand Down Expand Up @@ -495,4 +495,15 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
/// Storage: `DappStaking::StakerInfo` (r:2 w:1)
/// Proof: `DappStaking::StakerInfo` (`max_values`: None, `max_size`: Some(179), added: 2654, mode: `MaxEncodedLen`)
fn mbm_step_v9_bonus_status() -> Weight {
// Proof Size summary in bytes:
// Measured: `150`
// Estimated: `6298`
// Minimum execution time: 15_000_000 picoseconds.
Weight::from_parts(15_000_000, 6298)
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
}
Loading

0 comments on commit df12ac8

Please sign in to comment.