Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add storage migration for Operator struct change and bump runtime version #3342

Merged
merged 2 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion crates/pallet-domains/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ mod tests;
pub mod block_tree;
mod bundle_storage_fund;
pub mod domain_registry;
pub mod migration;
pub mod runtime_registry;
mod staking;
mod staking_epoch;
Expand Down Expand Up @@ -160,7 +161,7 @@ pub type BlockTreeNodeFor<T> = crate::block_tree::BlockTreeNode<
>;

/// The current storage version.
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
const STORAGE_VERSION: StorageVersion = StorageVersion::new(2);

/// The number of bundle of a particular domain to be included in the block is probabilistic
/// and based on the consensus chain slot probability and domain bundle slot probability, usually
Expand Down
161 changes: 161 additions & 0 deletions crates/pallet-domains/src/migration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
//! Migration module for pallet-domains
use crate::{Config, Pallet};
use frame_support::migrations::VersionedMigration;
use frame_support::traits::UncheckedOnRuntimeUpgrade;
use frame_support::weights::Weight;

pub type VersionCheckedMigrateDomainsV1ToV2<T> = VersionedMigration<
1,
2,
VersionUncheckedMigrateV1ToV2<T>,
Pallet<T>,
<T as frame_system::Config>::DbWeight,
>;

pub struct VersionUncheckedMigrateV1ToV2<T>(sp_std::marker::PhantomData<T>);
impl<T: Config> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateV1ToV2<T> {
fn on_runtime_upgrade() -> Weight {
operator_structure_migration::migrate_operator_structure::<T>()
}
}

mod operator_structure_migration {
use crate::pallet::Operators as OperatorsV2;
use crate::staking::{Operator as OperatorV2, OperatorStatus};
use crate::{BalanceOf, Config, DomainBlockNumberFor, Pallet};
use codec::{Decode, Encode};
use frame_support::pallet_prelude::{OptionQuery, TypeInfo, Weight};
use frame_support::{storage_alias, Identity};
use sp_core::Get;
use sp_domains::{DomainId, OperatorId, OperatorPublicKey};
use sp_runtime::Percent;

#[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)]
pub struct Operator<Balance, Share, DomainBlockNumber> {
pub signing_key: OperatorPublicKey,
pub current_domain_id: DomainId,
pub next_domain_id: DomainId,
pub minimum_nominator_stake: Balance,
pub nomination_tax: Percent,
/// Total active stake of combined nominators under this operator.
pub current_total_stake: Balance,
/// Total rewards this operator received this current epoch.
pub current_epoch_rewards: Balance,
/// Total shares of all the nominators under this operator.
pub current_total_shares: Share,
/// The status of the operator, it may be stale due to the `OperatorStatus::PendingSlash` is
/// not assigned to this field directly, thus MUST use the `status()` method to query the status
/// instead.
pub(super) partial_status: OperatorStatus<DomainBlockNumber>,
/// Total deposits during the previous epoch
pub deposits_in_epoch: Balance,
/// Total withdrew shares during the previous epoch
pub withdrawals_in_epoch: Share,
/// Total balance deposited to the bundle storage fund
pub total_storage_fee_deposit: Balance,
}

#[storage_alias]
pub type Operators<T: Config> = StorageMap<
Pallet<T>,
Identity,
OperatorId,
Operator<BalanceOf<T>, <T as Config>::Share, DomainBlockNumberFor<T>>,
OptionQuery,
>;

pub(super) fn migrate_operator_structure<T: Config>() -> Weight {
// On Taurus, the operator 0-8 are registered before the runtime upgrade that brings the new
// structure, for operator (if any) registered after that runtime upgrade it should be in new
// structure already, thus the migration should only handle operator 0-8
let affected_operator = 8;
let mut operator_count = 0;
for operator_id in 0..=affected_operator {
if let Some(operator) = Operators::<T>::take(operator_id) {
OperatorsV2::<T>::set(
operator_id,
Some(OperatorV2 {
signing_key: operator.signing_key,
current_domain_id: operator.current_domain_id,
next_domain_id: operator.next_domain_id,
minimum_nominator_stake: operator.minimum_nominator_stake,
nomination_tax: operator.nomination_tax,
current_total_stake: operator.current_total_stake,
current_total_shares: operator.current_total_shares,
partial_status: operator.partial_status,
deposits_in_epoch: operator.deposits_in_epoch,
withdrawals_in_epoch: operator.withdrawals_in_epoch,
total_storage_fee_deposit: operator.total_storage_fee_deposit,
}),
);
operator_count += 1;
}
}

// 1 read and 1 write per old operator
// 1 write per new operator
T::DbWeight::get().reads_writes(operator_count, operator_count * 2)
}
}

#[cfg(test)]
mod tests {
use super::operator_structure_migration::{migrate_operator_structure, Operator, Operators};
use crate::pallet::Operators as OperatorsV2;
use crate::staking::{Operator as OperatorV2, OperatorStatus};
use crate::tests::{new_test_ext, Test};
use crate::Config;
use sp_core::crypto::Ss58Codec;
use sp_domains::OperatorPublicKey;

#[test]
fn test_operator_structure_migration() {
let mut ext = new_test_ext();
let operator_id = 0;
let operator = Operator {
signing_key: OperatorPublicKey::from_ss58check(
"5Gv1Uopoqo1k7125oDtFSCmxH4DzuCiBU7HBKu2bF1GZFsEb",
)
.unwrap(),
current_domain_id: 0u32.into(),
next_domain_id: 0u32.into(),
minimum_nominator_stake: <Test as Config>::MinNominatorStake::get(),
nomination_tax: Default::default(),
current_total_stake: 1u32.into(),
current_epoch_rewards: 2u32.into(),
current_total_shares: 3u32.into(),
partial_status: OperatorStatus::Registered,
deposits_in_epoch: 4u32.into(),
withdrawals_in_epoch: 5u32.into(),
total_storage_fee_deposit: 6u32.into(),
};

ext.execute_with(|| Operators::<Test>::set(operator_id, Some(operator.clone())));

ext.commit_all().unwrap();

ext.execute_with(|| {
let weights = migrate_operator_structure::<Test>();
assert_eq!(
weights,
<Test as frame_system::Config>::DbWeight::get().reads_writes(1, 2),
);
assert_eq!(
OperatorsV2::<Test>::get(operator_id),
Some(OperatorV2 {
signing_key: operator.signing_key,
current_domain_id: operator.current_domain_id,
next_domain_id: operator.next_domain_id,
minimum_nominator_stake: operator.minimum_nominator_stake,
nomination_tax: operator.nomination_tax,
current_total_stake: operator.current_total_stake,
current_total_shares: operator.current_total_shares,
partial_status: operator.partial_status,
deposits_in_epoch: operator.deposits_in_epoch,
withdrawals_in_epoch: operator.withdrawals_in_epoch,
total_storage_fee_deposit: operator.total_storage_fee_deposit,
})
);
});
}
}
3 changes: 2 additions & 1 deletion crates/pallet-domains/src/staking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@ pub struct Operator<Balance, Share, DomainBlockNumber> {
/// The status of the operator, it may be stale due to the `OperatorStatus::PendingSlash` is
/// not assigned to this field directly, thus MUST use the `status()` method to query the status
/// instead.
partial_status: OperatorStatus<DomainBlockNumber>,
/// TODO: export `partial_status` for migration usage, remove `pub(crate)`once migration are done
pub(crate) partial_status: OperatorStatus<DomainBlockNumber>,
/// Total deposits during the previous epoch
pub deposits_in_epoch: Balance,
/// Total withdrew shares during the previous epoch
Expand Down
4 changes: 3 additions & 1 deletion crates/subspace-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: Cow::Borrowed("subspace"),
impl_name: Cow::Borrowed("subspace"),
authoring_version: 0,
spec_version: 10,
spec_version: 11,
impl_version: 0,
apis: RUNTIME_API_VERSIONS,
transaction_version: 0,
Expand Down Expand Up @@ -978,6 +978,8 @@ pub type Executive = frame_executive::Executive<
frame_system::ChainContext<Runtime>,
Runtime,
AllPalletsWithSystem,
// TODO: remove once the migrations are done
pallet_domains::migration::VersionCheckedMigrateDomainsV1ToV2<Runtime>,
>;

fn extract_segment_headers(ext: &UncheckedExtrinsic) -> Option<Vec<SegmentHeader>> {
Expand Down
Loading