code/pallets/subtensor/src/migrations/migrate_fix_rao_alpha_out_accounting.rs

migrate_fix_rao_alpha_out_accounting.rs

143 lines · 5,979 bytes · 71136ad109RawGitHub
use super::*;
use frame_support::{traits::Get, weights::Weight};
use frame_system::pallet_prelude::BlockNumberFor;
use scale_info::prelude::string::String;
use sp_runtime::traits::Zero;
use subtensor_runtime_common::{AlphaBalance, NetUid};

pub(crate) const MIGRATION_NAME: &[u8] = b"migrate_fix_rao_alpha_out_accounting";

/// Fixed mainnet corrections generated by an offline historical block scan.
///
/// Netuid 0 repairs root dividends that were credited directly to root stake
/// without increasing `SubnetAlphaOut(0)`. The other rows repair the RAO launch
/// bug fixed by PR #1321, where the full local dividend and its root portion were
/// both credited while `SubnetAlphaOut` recorded only the intended issuance.
///
/// Recycled netuids are intentionally absent: their affected historical subnet
/// generation was dissolved, so applying its correction to the current generation
/// would corrupt an unrelated asset. Tests verify these embedded constants against
/// the checked-in reconstruction data.
/// `(netuid, expected_generation_registered_at, alpha_out_correction)`.
/// Root has no registration row, so its expected storage value is zero.
pub(crate) const ALPHA_OUT_CORRECTIONS: &[(u16, u64, u64)] = &[
    (0, 0, 728_652_620_877_147),
    (1, 1_497_824, 16_841_481_627_450),
    (2, 2_734_060, 16_657_607_997_081),
    (3, 4_165_565, 16_612_384_835_337),
    (4, 1_411_451, 16_657_531_459_163),
    (5, 2_491_604, 16_603_538_275_444),
    (6, 3_219_949, 16_670_388_331_537),
    (7, 2_627_691, 16_665_154_249_543),
    (8, 1_477_264, 16_641_858_745_916),
    (9, 1_489_797, 16_602_494_598_480),
    (10, 2_869_647, 16_648_982_598_377),
    (11, 2_918_568, 16_614_884_693_920),
    (12, 2_256_433, 16_609_506_226_265),
    (13, 1_907_637, 16_593_293_670_926),
    (14, 4_848_444, 16_654_855_289_608),
    (17, 2_840_556, 16_629_648_001_041),
    (18, 1_604_679, 16_609_953_922_039),
    (19, 1_956_072, 16_648_375_285_150),
    (20, 1_970_929, 16_627_174_584_058),
    (21, 3_156_578, 16_664_619_652_248),
    (22, 2_009_702, 16_657_589_459_778),
    (23, 2_063_528, 16_650_031_992_562),
    (24, 2_538_424, 16_627_624_371_902),
    (25, 2_998_801, 16_612_520_257_668),
    (27, 1_727_132, 16_611_935_757_901),
    (28, 4_878_363, 16_634_666_296_663),
    (29, 3_379_782, 16_521_483_756_032),
    (30, 3_250_216, 16_511_202_677_182),
    (32, 2_515_294, 16_641_878_275_936),
    (33, 2_943_950, 16_566_858_991_992),
    (34, 3_493_948, 16_618_182_021_497),
    (35, 3_037_158, 16_572_673_502_097),
    (37, 3_212_175, 16_630_032_241_363),
    (39, 3_280_104, 16_611_788_077_785),
    (41, 3_394_182, 16_565_029_906_044),
    (42, 3_613_591, 16_609_212_950_301),
    (43, 3_408_582, 16_555_071_176_088),
    (44, 3_550_319, 16_577_716_745_472),
    (45, 3_633_154, 16_622_137_987_566),
    (46, 3_919_107, 16_613_515_003_811),
    (48, 3_856_677, 16_584_343_114_727),
    (50, 4_763_204, 16_600_577_252_963),
    (51, 3_966_206, 16_560_623_847_708),
    (52, 3_989_825, 16_608_149_936_500),
    (53, 4_203_869, 16_566_475_199_783),
    (54, 4_742_549, 16_597_835_699_556),
    (55, 4_703_386, 16_621_722_394_828),
    (56, 4_312_927, 16_582_983_440_389),
    (59, 4_401_833, 16_937_046_522_687),
    (60, 4_796_992, 16_979_801_039_432),
    (61, 4_457_976, 16_957_520_199_920),
    (62, 4_474_225, 16_958_464_626_467),
    (63, 4_885_578, 16_977_368_055_945),
    (64, 4_531_295, 16_921_598_714_453),
    (65, 4_950_813, 4_932_684_752_021),
    (66, 4_958_013, 2_003_518_827_049),
];

/// Repairs two stabilized mainnet `SubnetAlphaOut` undercounts using offline
/// reconstructed constants. The migration deliberately does not query or replay
/// historical chain state.
pub fn migrate_fix_rao_alpha_out_accounting<T: Config>() -> Weight {
    let migration_name = MIGRATION_NAME.to_vec();
    let mut weight = T::DbWeight::get().reads(1);

    if HasMigrationRun::<T>::get(&migration_name) {
        log::info!(
            "Migration '{}' already executed - skipping",
            String::from_utf8_lossy(MIGRATION_NAME)
        );
        return weight;
    }

    // These amounts describe mainnet history and must never be applied to
    // testnet, devnet, or a local chain.
    let genesis_hash = frame_system::Pallet::<T>::block_hash(BlockNumberFor::<T>::zero());
    weight.saturating_accrue(T::DbWeight::get().reads(1));
    let mainnet_genesis =
        hex_literal::hex!("2f0555cc76fc2840a25a6ea3b9637146806f1f44b090c175ffde2a7e5ab36c03");
    let mut applied_corrections = 0u64;

    if genesis_hash.as_ref() == mainnet_genesis {
        for &(netuid, expected_registered_at, correction) in ALPHA_OUT_CORRECTIONS {
            let netuid = NetUid::from(netuid);
            let registered_at = NetworkRegisteredAt::<T>::get(netuid);
            weight.saturating_accrue(T::DbWeight::get().reads(1));

            if registered_at != expected_registered_at {
                log::error!(
                    "Skipping SubnetAlphaOut correction for netuid {:?}: registered_at={}, expected={}",
                    netuid,
                    registered_at,
                    expected_registered_at,
                );
                continue;
            }

            SubnetAlphaOut::<T>::mutate(netuid, |alpha_out| {
                *alpha_out = alpha_out.saturating_add(AlphaBalance::from(correction));
            });
            weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
            applied_corrections = applied_corrections.saturating_add(1);
        }
    } else {
        log::info!(
            "Migration '{}' skipped outside mainnet",
            String::from_utf8_lossy(MIGRATION_NAME)
        );
    }

    HasMigrationRun::<T>::insert(&migration_name, true);
    weight.saturating_accrue(T::DbWeight::get().writes(1));

    log::info!(
        "Migration '{}' completed with {} SubnetAlphaOut corrections",
        String::from_utf8_lossy(MIGRATION_NAME),
        applied_corrections
    );
    weight
}