|
| 1 | +//! Migration module for pallet-domains |
| 2 | +use crate::{Config, Pallet}; |
| 3 | +use frame_support::migrations::VersionedMigration; |
| 4 | +use frame_support::traits::UncheckedOnRuntimeUpgrade; |
| 5 | +use frame_support::weights::Weight; |
| 6 | + |
| 7 | +pub type VersionCheckedMigrateDomainsV1ToV2<T> = VersionedMigration< |
| 8 | + 1, |
| 9 | + 2, |
| 10 | + VersionUncheckedMigrateV1ToV2<T>, |
| 11 | + Pallet<T>, |
| 12 | + <T as frame_system::Config>::DbWeight, |
| 13 | +>; |
| 14 | + |
| 15 | +pub struct VersionUncheckedMigrateV1ToV2<T>(sp_std::marker::PhantomData<T>); |
| 16 | +impl<T: Config> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateV1ToV2<T> { |
| 17 | + fn on_runtime_upgrade() -> Weight { |
| 18 | + operator_structure_migration::migrate_operator_structure::<T>() |
| 19 | + } |
| 20 | +} |
| 21 | + |
| 22 | +mod operator_structure_migration { |
| 23 | + use crate::pallet::Operators as OperatorsV2; |
| 24 | + use crate::staking::{Operator as OperatorV2, OperatorStatus}; |
| 25 | + use crate::{BalanceOf, Config, DomainBlockNumberFor, Pallet}; |
| 26 | + use codec::{Decode, Encode}; |
| 27 | + use frame_support::pallet_prelude::{OptionQuery, TypeInfo, Weight}; |
| 28 | + use frame_support::{storage_alias, Identity}; |
| 29 | + use sp_core::Get; |
| 30 | + use sp_domains::{DomainId, OperatorId, OperatorPublicKey}; |
| 31 | + use sp_runtime::Percent; |
| 32 | + |
| 33 | + #[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)] |
| 34 | + pub struct Operator<Balance, Share, DomainBlockNumber> { |
| 35 | + pub signing_key: OperatorPublicKey, |
| 36 | + pub current_domain_id: DomainId, |
| 37 | + pub next_domain_id: DomainId, |
| 38 | + pub minimum_nominator_stake: Balance, |
| 39 | + pub nomination_tax: Percent, |
| 40 | + /// Total active stake of combined nominators under this operator. |
| 41 | + pub current_total_stake: Balance, |
| 42 | + /// Total rewards this operator received this current epoch. |
| 43 | + pub current_epoch_rewards: Balance, |
| 44 | + /// Total shares of all the nominators under this operator. |
| 45 | + pub current_total_shares: Share, |
| 46 | + /// The status of the operator, it may be stale due to the `OperatorStatus::PendingSlash` is |
| 47 | + /// not assigned to this field directly, thus MUST use the `status()` method to query the status |
| 48 | + /// instead. |
| 49 | + pub(super) partial_status: OperatorStatus<DomainBlockNumber>, |
| 50 | + /// Total deposits during the previous epoch |
| 51 | + pub deposits_in_epoch: Balance, |
| 52 | + /// Total withdrew shares during the previous epoch |
| 53 | + pub withdrawals_in_epoch: Share, |
| 54 | + /// Total balance deposited to the bundle storage fund |
| 55 | + pub total_storage_fee_deposit: Balance, |
| 56 | + } |
| 57 | + |
| 58 | + #[storage_alias] |
| 59 | + pub type Operators<T: Config> = StorageMap< |
| 60 | + Pallet<T>, |
| 61 | + Identity, |
| 62 | + OperatorId, |
| 63 | + Operator<BalanceOf<T>, <T as Config>::Share, DomainBlockNumberFor<T>>, |
| 64 | + OptionQuery, |
| 65 | + >; |
| 66 | + |
| 67 | + pub(super) fn migrate_operator_structure<T: Config>() -> Weight { |
| 68 | + // On Taurus, the operator 0-8 are registered before the runtime upgrade that brings the new |
| 69 | + // structure, for operator (if any) registered after that runtime upgrade it should be in new |
| 70 | + // structure already, thus the migration should only handle operator 0-8 |
| 71 | + let affected_operator = 8; |
| 72 | + let mut operator_count = 0; |
| 73 | + for operator_id in 0..=affected_operator { |
| 74 | + if let Some(operator) = Operators::<T>::take(operator_id) { |
| 75 | + OperatorsV2::<T>::set( |
| 76 | + operator_id, |
| 77 | + Some(OperatorV2 { |
| 78 | + signing_key: operator.signing_key, |
| 79 | + current_domain_id: operator.current_domain_id, |
| 80 | + next_domain_id: operator.next_domain_id, |
| 81 | + minimum_nominator_stake: operator.minimum_nominator_stake, |
| 82 | + nomination_tax: operator.nomination_tax, |
| 83 | + current_total_stake: operator.current_total_stake, |
| 84 | + current_total_shares: operator.current_total_shares, |
| 85 | + partial_status: operator.partial_status, |
| 86 | + deposits_in_epoch: operator.deposits_in_epoch, |
| 87 | + withdrawals_in_epoch: operator.withdrawals_in_epoch, |
| 88 | + total_storage_fee_deposit: operator.total_storage_fee_deposit, |
| 89 | + }), |
| 90 | + ); |
| 91 | + operator_count += 1; |
| 92 | + } |
| 93 | + } |
| 94 | + |
| 95 | + // 1 read and 1 write per old operator |
| 96 | + // 1 write per new operator |
| 97 | + T::DbWeight::get().reads_writes(operator_count, operator_count * 2) |
| 98 | + } |
| 99 | +} |
| 100 | + |
| 101 | +#[cfg(test)] |
| 102 | +mod tests { |
| 103 | + use super::operator_structure_migration::{migrate_operator_structure, Operator, Operators}; |
| 104 | + use crate::pallet::Operators as OperatorsV2; |
| 105 | + use crate::staking::{Operator as OperatorV2, OperatorStatus}; |
| 106 | + use crate::tests::{new_test_ext, Test}; |
| 107 | + use crate::Config; |
| 108 | + use sp_core::crypto::Ss58Codec; |
| 109 | + use sp_domains::OperatorPublicKey; |
| 110 | + |
| 111 | + #[test] |
| 112 | + fn test_operator_structure_migration() { |
| 113 | + let mut ext = new_test_ext(); |
| 114 | + let operator_id = 0; |
| 115 | + let operator = Operator { |
| 116 | + signing_key: OperatorPublicKey::from_ss58check( |
| 117 | + "5Gv1Uopoqo1k7125oDtFSCmxH4DzuCiBU7HBKu2bF1GZFsEb", |
| 118 | + ) |
| 119 | + .unwrap(), |
| 120 | + current_domain_id: 0u32.into(), |
| 121 | + next_domain_id: 0u32.into(), |
| 122 | + minimum_nominator_stake: <Test as Config>::MinNominatorStake::get(), |
| 123 | + nomination_tax: Default::default(), |
| 124 | + current_total_stake: 1u32.into(), |
| 125 | + current_epoch_rewards: 2u32.into(), |
| 126 | + current_total_shares: 3u32.into(), |
| 127 | + partial_status: OperatorStatus::Registered, |
| 128 | + deposits_in_epoch: 4u32.into(), |
| 129 | + withdrawals_in_epoch: 5u32.into(), |
| 130 | + total_storage_fee_deposit: 6u32.into(), |
| 131 | + }; |
| 132 | + |
| 133 | + ext.execute_with(|| Operators::<Test>::set(operator_id, Some(operator.clone()))); |
| 134 | + |
| 135 | + ext.commit_all().unwrap(); |
| 136 | + |
| 137 | + ext.execute_with(|| { |
| 138 | + let weights = migrate_operator_structure::<Test>(); |
| 139 | + assert_eq!( |
| 140 | + weights, |
| 141 | + <Test as frame_system::Config>::DbWeight::get().reads_writes(1, 2), |
| 142 | + ); |
| 143 | + assert_eq!( |
| 144 | + OperatorsV2::<Test>::get(operator_id), |
| 145 | + Some(OperatorV2 { |
| 146 | + signing_key: operator.signing_key, |
| 147 | + current_domain_id: operator.current_domain_id, |
| 148 | + next_domain_id: operator.next_domain_id, |
| 149 | + minimum_nominator_stake: operator.minimum_nominator_stake, |
| 150 | + nomination_tax: operator.nomination_tax, |
| 151 | + current_total_stake: operator.current_total_stake, |
| 152 | + current_total_shares: operator.current_total_shares, |
| 153 | + partial_status: operator.partial_status, |
| 154 | + deposits_in_epoch: operator.deposits_in_epoch, |
| 155 | + withdrawals_in_epoch: operator.withdrawals_in_epoch, |
| 156 | + total_storage_fee_deposit: operator.total_storage_fee_deposit, |
| 157 | + }) |
| 158 | + ); |
| 159 | + }); |
| 160 | + } |
| 161 | +} |
0 commit comments