chore: ♻️ Remove executed runtime migrations (#318)

## Summary

Removes old runtime migrations that have already been executed on
Stagenet and Testnet environments, reducing code complexity and
maintenance burden.

## Changes

### Migration Cleanup
- **Removed `evm_alias::EvmAliasMigration`** (~532 lines)
- Multi-block migration that renamed the Frontier EVM pallet alias from
`Evm` to `EVM`
  - Migrated AccountCodes, AccountCodesMetadata, and AccountStorages
  
- **Removed `evm_chain_id::EvmChainIdMigration`**
- Single-step migration that updated stored EVM chain IDs to match new
configuration
  - Applied to testnet (55931) and stagenet (55932)

### Runtime Updates
- **Simplified `MultiBlockMigrationList`** to empty tuple `()` in
`runtime/common/src/migrations.rs`
- **Updated all runtime configs** to use simplified migration list:
  - `runtime/mainnet/src/configs/mod.rs`
  - `runtime/stagenet/src/configs/mod.rs`
  - `runtime/testnet/src/configs/mod.rs`
- Removed `Runtime` type parameter from migration configurations

### What Remains
- `pallet_migrations` infrastructure stays in place for future
migrations
- Migration test file (`mainnet/tests/migrations.rs`) preserved for
testing pallet administrative functions
- Configuration types and constants (cursor/identifier lengths,
handlers)

## Impact

- **Code reduction**: 532 lines removed
- **No functional change**: These migrations have already executed
successfully
- **Future-ready**: Migration infrastructure remains for new migrations
when needed

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
This commit is contained in:
Steve Degosserie 2025-11-25 18:44:06 +01:00 committed by GitHub
parent 72c3457183
commit 92d3c42f79
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 40 additions and 532 deletions

View file

@ -18,6 +18,42 @@
//!
//! The types and constants defined here keep the pallet configuration consistent between
//! networks while leaving each runtime free to decide which migrations should actually run.
//!
//! ## Migration History
//!
//! This section documents migrations that have been executed and subsequently removed from the
//! codebase. The migration framework tracks completed migrations on-chain, preventing re-execution.
//!
//! ### Executed Migrations (Code Removed)
//!
//! #### 1. EVM Pallet Alias Migration
//! - **Migration ID**: `datahaven-evm-mbm` (version 0 → 1)
//! - **Type**: Multi-block stepped migration
//! - **Original PR**: [#213](https://github.com/datahaven-xyz/datahaven/pull/213)
//! - **Intent**: Fixed `eth_getCode` and other Ethereum RPC calls by renaming the pallet_evm
//! FRAME alias from `Evm` to `EVM`. Frontier's `StorageOverrideHandler` hardcodes the storage
//! prefix as `twox_128("EVM")`, but our runtimes used `Evm`, causing all contract bytecode
//! lookups to fail. This migration realigned the storage prefix with Frontier's expectations.
//! - **Storage Migrated**:
//! - `Evm::AccountCodes` → `EVM::AccountCodes`
//! - `Evm::AccountCodesMetadata` → `EVM::AccountCodesMetadata`
//! - `Evm::AccountStorages` → `EVM::AccountStorages`
//! - **Execution**: Successfully executed on Testnet and Stagenet (39 keys migrated, <0.1% block weight)
//! - **Removed**: 2025-01 ([PR #318](https://github.com/datahaven-xyz/datahaven/pull/318))
//!
//! #### 2. EVM Chain ID Migration
//! - **Migration ID**: `dh-evm-chain-id-v1` (version 0 → 1)
//! - **Type**: Single-step migration
//! - **Original PR**: [#280](https://github.com/datahaven-xyz/datahaven/pull/280)
//! - **Intent**: Updated chain IDs for all three DataHaven environments to their final assigned
//! values. The stored EVM chain ID in `pallet_evm_chain_id::ChainId` was migrated to match
//! the configured constants after chain ID assignments were finalized.
//! - **Networks**:
//! - Mainnet: Chain ID 55930 (no migration needed, set at genesis)
//! - Testnet: Migrated to chain ID 55931
//! - Stagenet: Migrated to chain ID 55932
//! - **Execution**: Successfully executed on Testnet and Stagenet
//! - **Removed**: 2025-01 ([PR #318](https://github.com/datahaven-xyz/datahaven/pull/318))
use frame_support::pallet_prelude::*;
@ -36,7 +72,7 @@ pub type MigrationIdentifierMaxLen = ConstU32<MIGRATION_IDENTIFIER_MAX_LEN>;
/// The tuple starts empty and can be extended with concrete migrations over time. Keeping it in a
/// shared module reduces duplication once we coordinate migrations across networks.
#[cfg(not(feature = "runtime-benchmarks"))]
pub type MultiBlockMigrationList<T> = (evm_alias::EvmAliasMigration<T>,);
pub type MultiBlockMigrationList = ();
/// During benchmarking we switch to the pallet-provided mocked migrations to guarantee success.
#[cfg(feature = "runtime-benchmarks")]
@ -58,519 +94,3 @@ pub type FailedMigrationHandler<SafeMode> =
SafeMode,
frame_support::migrations::FreezeChainOnFailedMigration,
>;
/// Multi-block migration for updating the EVM chain ID to the new value.
pub mod evm_chain_id {
use core::marker::PhantomData;
use frame_support::{
migrations::{MigrationId, SteppedMigration, SteppedMigrationError},
pallet_prelude::*,
weights::WeightMeter,
};
#[cfg(feature = "try-runtime")]
use codec::Encode;
/// Multi-block migration that updates the stored EVM chain ID to match the new configuration.
pub struct EvmChainIdMigration<T, const NEW_CHAIN_ID: u64>(PhantomData<T>);
impl<T, const NEW_CHAIN_ID: u64> SteppedMigration for EvmChainIdMigration<T, NEW_CHAIN_ID>
where
T: pallet_evm_chain_id::Config,
{
type Cursor = ();
type Identifier = MigrationId<20>;
fn id() -> Self::Identifier {
MigrationId {
pallet_id: *b"dh-evm-chain-id-v1 ",
version_from: 0,
version_to: 1,
}
}
fn step(
cursor: Option<Self::Cursor>,
meter: &mut WeightMeter,
) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
// This migration completes in a single step
if cursor.is_some() {
return Ok(None);
}
let required = T::DbWeight::get().reads_writes(1, 1);
if meter.try_consume(required).is_err() {
return Err(SteppedMigrationError::InsufficientWeight { required });
}
log::info!(
"🔄 [EVM Chain ID Migration] Updating chain ID to {}",
NEW_CHAIN_ID
);
// Update the chain ID storage
pallet_evm_chain_id::ChainId::<T>::put(NEW_CHAIN_ID);
log::info!(
"✅ [EVM Chain ID Migration] Successfully updated chain ID to {}",
NEW_CHAIN_ID
);
Ok(None)
}
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
let old_chain_id = pallet_evm_chain_id::ChainId::<T>::get();
log::info!(
"📋 [EVM Chain ID Migration] Current chain ID: {}",
old_chain_id
);
Ok(old_chain_id.encode())
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
use codec::Decode;
let old_chain_id =
u64::decode(&mut &state[..]).map_err(|_| "Failed to decode old chain ID")?;
let new_chain_id = pallet_evm_chain_id::ChainId::<T>::get();
log::info!(
"🔍 [EVM Chain ID Migration] Chain ID updated from {} to {}",
old_chain_id,
new_chain_id
);
if new_chain_id != NEW_CHAIN_ID {
return Err(sp_runtime::TryRuntimeError::Other(
"Chain ID was not updated correctly",
));
}
Ok(())
}
}
}
/// Multi-block migration for renaming the EVM pallet alias.
pub mod evm_alias {
use core::marker::PhantomData;
use frame_support::{
migrations::{MigrationId, SteppedMigration, SteppedMigrationError},
pallet_prelude::*,
weights::WeightMeter,
BoundedVec, StorageHasher,
};
use sp_io::storage;
use sp_std::{convert::TryFrom, vec::Vec};
#[cfg(feature = "try-runtime")]
use sp_std::collections::btree_map::BTreeMap;
/// Multi-block migration that renames the Frontier EVM pallet alias from `Evm` to `EVM`.
pub struct EvmAliasMigration<T>(PhantomData<T>);
impl<T> SteppedMigration for EvmAliasMigration<T>
where
T: pallet_evm::Config,
{
type Cursor = BoundedVec<u8, ConstU32<128>>;
type Identifier = MigrationId<17>;
fn id() -> Self::Identifier {
MigrationId {
pallet_id: *b"datahaven-evm-mbm",
version_from: 0,
version_to: 1,
}
}
fn step(
cursor: Option<Self::Cursor>,
meter: &mut WeightMeter,
) -> Result<Option<Self::Cursor>, SteppedMigrationError> {
if cursor.is_none() {
log::info!(
"🚀 [EVM Migration] Starting pallet alias migration from 'Evm' to 'EVM'"
);
}
let old_prefix = Twox128::hash(b"Evm");
let new_prefix = Twox128::hash(b"EVM");
let mut current_key: Vec<u8> = cursor
.map(Into::into)
.unwrap_or_else(|| old_prefix.to_vec());
let mut processed = 0u32;
let required = T::DbWeight::get().reads_writes(1, 2);
loop {
let next_key = match storage::next_key(&current_key) {
Some(next) if next.starts_with(&old_prefix) => next,
_ => {
log::info!(
"✅ [EVM Migration] Completed! Processed {} keys in this step",
processed
);
return Ok(None);
}
};
if meter.try_consume(required).is_err() {
if processed == 0 {
log::warn!(
"⚠️ [EVM Migration] Insufficient weight for even one key migration"
);
return Err(SteppedMigrationError::InsufficientWeight { required });
}
log::info!(
"⏸️ [EVM Migration] Pausing after migrating {} keys (weight limit reached)",
processed
);
return BoundedVec::try_from(current_key)
.map(Some)
.map_err(|_| SteppedMigrationError::Failed);
}
if let Some(value) = storage::get(&next_key) {
let mut new_key = Vec::with_capacity(next_key.len());
new_key.extend_from_slice(&new_prefix);
new_key.extend_from_slice(&next_key[16..]);
storage::set(&new_key, &value);
}
storage::clear(&next_key);
processed = processed.saturating_add(1);
current_key = next_key;
}
}
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
use codec::Encode;
let storage_prefix = |item: &[u8]| {
let mut key = [0u8; 32];
key[0..16].copy_from_slice(&Twox128::hash(b"Evm"));
key[16..32].copy_from_slice(&Twox128::hash(item));
key
};
let mut counts = BTreeMap::new();
for name in [
b"AccountCodes" as &[u8],
b"AccountCodesMetadata",
b"AccountStorages",
] {
let count = count_keys(&storage_prefix(name));
counts.insert(name.to_vec(), count.encode());
}
Ok(counts.encode())
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
use codec::Decode;
let snapshot: BTreeMap<Vec<u8>, Vec<u8>> =
Decode::decode(&mut &state[..]).expect("Failed to decode snapshot");
let old_storage = |item: &[u8]| {
let mut key = [0u8; 32];
key[0..16].copy_from_slice(&Twox128::hash(b"Evm"));
key[16..32].copy_from_slice(&Twox128::hash(item));
key
};
let new_storage = |item: &[u8]| {
let mut key = [0u8; 32];
key[0..16].copy_from_slice(&Twox128::hash(b"EVM"));
key[16..32].copy_from_slice(&Twox128::hash(item));
key
};
for name in [
b"AccountCodes" as &[u8],
b"AccountCodesMetadata",
b"AccountStorages",
] {
let old_count = count_keys(&old_storage(name));
assert_eq!(old_count, 0, "Old Evm prefix still present after migration");
let expected = snapshot
.get(name)
.and_then(|v| u32::decode(&mut &v[..]).ok())
.unwrap_or(0);
let actual = count_keys(&new_storage(name));
assert_eq!(expected, actual, "Migrated entry count mismatch");
}
Ok(())
}
}
/// Helper function to count storage keys with a given prefix.
#[cfg(any(feature = "try-runtime", test))]
pub(crate) fn count_keys(prefix: &[u8]) -> u32 {
let mut count = 0u32;
let mut cursor = prefix.to_vec();
loop {
match storage::next_key(&cursor) {
Some(next) if next.starts_with(prefix) => {
count = count.saturating_add(1);
cursor = next;
}
_ => break,
}
}
count
}
}
#[cfg(test)]
mod tests {
use super::evm_alias::{count_keys, EvmAliasMigration};
use codec::Encode;
use frame_support::{
derive_impl,
migrations::SteppedMigration,
parameter_types,
weights::{constants::RocksDbWeight, Weight, WeightMeter},
StorageHasher, Twox128,
};
use pallet_evm::AccountCodes;
use sp_core::{H160, H256, U256};
use sp_io::storage;
use sp_runtime::BuildStorage;
use sp_std::vec;
frame_support::construct_runtime!(
pub enum TestRuntime {
System: frame_system::{Pallet, Call, Config<T>, Storage, Event<T>},
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
Timestamp: pallet_timestamp::{Pallet, Call, Storage},
EVM: pallet_evm::{Pallet, Call, Storage, Config<T>, Event<T>},
EvmChainId: pallet_evm_chain_id::{Pallet, Storage},
}
);
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub BlockWeights: frame_system::limits::BlockWeights =
frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1024, 0));
}
#[derive_impl(frame_system::config_preludes::SolochainDefaultConfig as frame_system::DefaultConfig)]
impl frame_system::Config for TestRuntime {
type Nonce = u64;
type Block = frame_system::mocking::MockBlock<Self>;
type BlockHashCount = BlockHashCount;
type AccountData = pallet_balances::AccountData<u64>;
}
parameter_types! {
pub const ExistentialDeposit: u64 = 0;
}
#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
impl pallet_balances::Config for TestRuntime {
type RuntimeHoldReason = ();
type Balance = u64;
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = System;
}
#[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)]
impl pallet_timestamp::Config for TestRuntime {}
parameter_types! {
pub MockPrecompiles: MockPrecompileSet = MockPrecompileSet;
}
#[derive_impl(pallet_evm::config_preludes::TestDefaultConfig)]
impl pallet_evm::Config for TestRuntime {
type AccountProvider = pallet_evm::FrameSystemAccountProvider<Self>;
type FeeCalculator = FixedGasPrice;
type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping<Self>;
type Currency = Balances;
type Runner = pallet_evm::runner::stack::Runner<Self>;
type Timestamp = Timestamp;
type PrecompilesType = MockPrecompileSet;
type PrecompilesValue = MockPrecompiles;
}
impl pallet_evm_chain_id::Config for TestRuntime {}
pub struct FixedGasPrice;
impl pallet_evm::FeeCalculator for FixedGasPrice {
fn min_gas_price() -> (U256, Weight) {
(1_000_000_000u128.into(), Weight::from_parts(1_000, 0))
}
}
pub struct MockPrecompileSet;
impl pallet_evm::PrecompileSet for MockPrecompileSet {
fn execute(
&self,
_handle: &mut impl pallet_evm::PrecompileHandle,
) -> Option<pallet_evm::PrecompileResult> {
None
}
fn is_precompile(&self, _address: H160, _gas: u64) -> pallet_evm::IsPrecompileResult {
pallet_evm::IsPrecompileResult::Answer {
is_precompile: false,
extra_cost: 0,
}
}
}
fn old_storage_prefix(item: &[u8]) -> [u8; 32] {
let mut key = [0u8; 32];
key[0..16].copy_from_slice(&Twox128::hash(b"Evm"));
key[16..32].copy_from_slice(&Twox128::hash(item));
key
}
fn raw_key(storage_name: &[u8], address: H160, index: Option<H256>) -> Vec<u8> {
let mut key = old_storage_prefix(storage_name).to_vec();
key.extend_from_slice(&sp_io::hashing::blake2_128(&address.encode()));
key.extend_from_slice(address.as_bytes());
if let Some(idx) = index {
key.extend_from_slice(&sp_io::hashing::blake2_128(&idx.encode()));
key.extend_from_slice(idx.as_bytes());
}
key
}
#[test]
fn multi_block_evm_alias_migration_moves_all_entries() {
let mut storage = frame_system::GenesisConfig::<TestRuntime>::default()
.build_storage()
.unwrap();
pallet_balances::GenesisConfig::<TestRuntime>::default()
.assimilate_storage(&mut storage)
.unwrap();
let mut ext = sp_io::TestExternalities::new(storage);
ext.execute_with(|| {
let addresses: Vec<H160> = (1u64..=3).map(H160::from_low_u64_be).collect();
for (idx, &address) in addresses.iter().enumerate() {
storage::set(
&raw_key(b"AccountCodes", address, None),
&vec![idx as u8; 3].encode(),
);
storage::set(
&raw_key(b"AccountCodesMetadata", address, None),
&(42u64 + idx as u64, H256::repeat_byte(idx as u8)).encode(),
);
storage::set(
&raw_key(
b"AccountStorages",
address,
Some(H256::repeat_byte(0xAA + idx as u8)),
),
&H256::repeat_byte(idx as u8).encode(),
);
}
let mut cursor = None;
loop {
let mut meter = WeightMeter::with_limit(RocksDbWeight::get().reads_writes(1, 2));
match EvmAliasMigration::<TestRuntime>::step(cursor, &mut meter) {
Ok(None) => break,
Ok(Some(next)) => cursor = Some(next),
Err(err) => panic!("migration failed: {:?}", err),
}
}
for address in addresses {
assert!(!AccountCodes::<TestRuntime>::get(address).is_empty());
}
assert_eq!(count_keys(&old_storage_prefix(b"AccountCodes")[..]), 0);
assert_eq!(
count_keys(&old_storage_prefix(b"AccountCodesMetadata")[..]),
0
);
assert_eq!(count_keys(&old_storage_prefix(b"AccountStorages")[..]), 0);
});
}
#[test]
fn evm_chain_id_migration_updates_storage() {
use super::evm_chain_id::EvmChainIdMigration;
let mut storage = frame_system::GenesisConfig::<TestRuntime>::default()
.build_storage()
.unwrap();
pallet_balances::GenesisConfig::<TestRuntime>::default()
.assimilate_storage(&mut storage)
.unwrap();
let mut ext = sp_io::TestExternalities::new(storage);
ext.execute_with(|| {
// Set an old chain ID value
const OLD_CHAIN_ID: u64 = 12345;
const NEW_CHAIN_ID: u64 = 55931;
pallet_evm_chain_id::ChainId::<TestRuntime>::put(OLD_CHAIN_ID);
assert_eq!(
pallet_evm_chain_id::ChainId::<TestRuntime>::get(),
OLD_CHAIN_ID
);
// Run the migration
let mut meter = WeightMeter::with_limit(Weight::MAX);
let result = EvmChainIdMigration::<TestRuntime, NEW_CHAIN_ID>::step(None, &mut meter);
// Verify migration succeeded and completed in one step
assert!(result.is_ok());
assert_eq!(result.unwrap(), None);
// Verify the chain ID was updated
assert_eq!(
pallet_evm_chain_id::ChainId::<TestRuntime>::get(),
NEW_CHAIN_ID
);
});
}
#[test]
fn evm_chain_id_migration_is_idempotent() {
use super::evm_chain_id::EvmChainIdMigration;
let mut storage = frame_system::GenesisConfig::<TestRuntime>::default()
.build_storage()
.unwrap();
pallet_balances::GenesisConfig::<TestRuntime>::default()
.assimilate_storage(&mut storage)
.unwrap();
let mut ext = sp_io::TestExternalities::new(storage);
ext.execute_with(|| {
const NEW_CHAIN_ID: u64 = 55932;
// Run the migration twice
let mut meter = WeightMeter::with_limit(Weight::MAX);
let result1 = EvmChainIdMigration::<TestRuntime, NEW_CHAIN_ID>::step(None, &mut meter);
let mut meter = WeightMeter::with_limit(Weight::MAX);
let result2 = EvmChainIdMigration::<TestRuntime, NEW_CHAIN_ID>::step(None, &mut meter);
// Both should succeed
assert!(result1.is_ok());
assert!(result2.is_ok());
// Chain ID should be set correctly
assert_eq!(
pallet_evm_chain_id::ChainId::<TestRuntime>::get(),
NEW_CHAIN_ID
);
});
}
}

View file

@ -848,7 +848,7 @@ impl pallet_parameters::Config for Runtime {
impl pallet_migrations::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
#[cfg(not(feature = "runtime-benchmarks"))]
type Migrations = datahaven_runtime_common::migrations::MultiBlockMigrationList<Runtime>;
type Migrations = datahaven_runtime_common::migrations::MultiBlockMigrationList;
#[cfg(feature = "runtime-benchmarks")]
type Migrations = datahaven_runtime_common::migrations::MultiBlockMigrationList;
type CursorMaxLen = MigrationCursorMaxLen;

View file

@ -845,13 +845,7 @@ impl pallet_parameters::Config for Runtime {
impl pallet_migrations::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
#[cfg(not(feature = "runtime-benchmarks"))]
type Migrations = (
datahaven_runtime_common::migrations::MultiBlockMigrationList<Runtime>,
datahaven_runtime_common::migrations::evm_chain_id::EvmChainIdMigration<
Runtime,
EVM_CHAIN_ID,
>,
);
type Migrations = datahaven_runtime_common::migrations::MultiBlockMigrationList;
#[cfg(feature = "runtime-benchmarks")]
type Migrations = datahaven_runtime_common::migrations::MultiBlockMigrationList;
type CursorMaxLen = MigrationCursorMaxLen;

View file

@ -848,13 +848,7 @@ impl pallet_parameters::Config for Runtime {
impl pallet_migrations::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
#[cfg(not(feature = "runtime-benchmarks"))]
type Migrations = (
datahaven_runtime_common::migrations::MultiBlockMigrationList<Runtime>,
datahaven_runtime_common::migrations::evm_chain_id::EvmChainIdMigration<
Runtime,
EVM_CHAIN_ID,
>,
);
type Migrations = datahaven_runtime_common::migrations::MultiBlockMigrationList;
#[cfg(feature = "runtime-benchmarks")]
type Migrations = datahaven_runtime_common::migrations::MultiBlockMigrationList;
type CursorMaxLen = MigrationCursorMaxLen;