diff --git a/crates/subspace-runtime/src/lib.rs b/crates/subspace-runtime/src/lib.rs index dd655e6c10..4ce11c1c11 100644 --- a/crates/subspace-runtime/src/lib.rs +++ b/crates/subspace-runtime/src/lib.rs @@ -967,6 +967,8 @@ pub type Executive = frame_executive::Executive< frame_system::ChainContext, Runtime, AllPalletsWithSystem, + // TODO: remove once migrations are done + pallet_messenger::migrations::VersionCheckedMigrateDomainsV0ToV1, >; fn extract_segment_headers(ext: &UncheckedExtrinsic) -> Option> { diff --git a/domains/pallets/messenger/src/lib.rs b/domains/pallets/messenger/src/lib.rs index 0f59336a14..4d6d41cf30 100644 --- a/domains/pallets/messenger/src/lib.rs +++ b/domains/pallets/messenger/src/lib.rs @@ -24,6 +24,7 @@ mod benchmarking; mod fees; mod messages; +pub mod migrations; #[cfg(test)] mod mock; #[cfg(test)] @@ -34,6 +35,7 @@ pub mod weights; extern crate alloc; use codec::{Decode, Encode}; +use frame_support::pallet_prelude::StorageVersion; use frame_support::traits::fungible::{Inspect, InspectHold}; use frame_system::pallet_prelude::BlockNumberFor; pub use pallet::*; @@ -107,13 +109,16 @@ pub trait HoldIdentifier { fn messenger_channel() -> FungibleHoldId; } +/// The current storage version. +const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + #[frame_support::pallet] mod pallet { use crate::weights::WeightInfo; use crate::{ BalanceOf, ChainAllowlistUpdate, Channel, ChannelId, ChannelState, CloseChannelBy, FeeModel, HoldIdentifier, Nonce, OutboxMessageResult, StateRootOf, ValidatedRelayMessage, - U256, + STORAGE_VERSION, U256, }; #[cfg(not(feature = "std"))] use alloc::boxed::Box; @@ -196,6 +201,7 @@ mod pallet { /// Pallet messenger used to communicate between chains and other blockchains. #[pallet::pallet] #[pallet::without_storage_info] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); /// Stores the next channel id for a foreign chain. diff --git a/domains/pallets/messenger/src/migrations.rs b/domains/pallets/messenger/src/migrations.rs new file mode 100644 index 0000000000..59a3de4e6d --- /dev/null +++ b/domains/pallets/messenger/src/migrations.rs @@ -0,0 +1,203 @@ +//! Migration module for pallet-messenger +#[cfg(not(feature = "std"))] +extern crate alloc; +use crate::{Config, Pallet}; +#[cfg(not(feature = "std"))] +use alloc::collections::BTreeMap; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; +use frame_support::migrations::VersionedMigration; +use frame_support::traits::UncheckedOnRuntimeUpgrade; +use frame_support::weights::Weight; +use sp_core::sp_std; +#[cfg(feature = "std")] +use std::collections::BTreeMap; +#[cfg(feature = "std")] +use std::vec::Vec; + +pub type VersionCheckedMigrateDomainsV0ToV1 = VersionedMigration< + 0, + 1, + VersionUncheckedMigrateV0ToV1, + Pallet, + ::DbWeight, +>; + +pub struct VersionUncheckedMigrateV0ToV1(sp_std::marker::PhantomData); +impl UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateV0ToV1 { + fn on_runtime_upgrade() -> Weight { + messenger_migration::migrate_messenger_storages::() + } +} + +mod messenger_migration { + use super::{BTreeMap, Vec}; + use crate::{ + BalanceOf, Config, InboxResponses as InboxResponsesNew, Outbox as OutboxNew, + OutboxMessageCount, Pallet, + }; + use frame_support::pallet_prelude::OptionQuery; + use frame_support::weights::Weight; + use frame_support::{storage_alias, Identity}; + use sp_core::Get; + use sp_domains::{ChainId, ChannelId}; + use sp_messenger::messages::{Message, Nonce}; + + #[storage_alias] + pub(super) type InboxResponses = CountedStorageMap< + Pallet, + Identity, + (ChainId, ChannelId, Nonce), + Message>, + OptionQuery, + >; + + #[storage_alias] + pub(super) type Outbox = CountedStorageMap< + Pallet, + Identity, + (ChainId, ChannelId, Nonce), + Message>, + OptionQuery, + >; + + pub(super) fn migrate_messenger_storages() -> Weight { + let mut reads = 0; + let mut writes = 0; + let inbox_responses = InboxResponses::::drain().collect::>(); + inbox_responses.into_iter().for_each(|(key, msg)| { + // we do one read from the old storage + reads += 1; + + // we do one write to old storage and one write to new storage + writes += 2; + + InboxResponsesNew::::insert(key, msg); + }); + + let outbox = Outbox::::drain().collect::>(); + let mut outbox_count = BTreeMap::new(); + outbox.into_iter().for_each(|(key, msg)| { + // we do one read from the old storage + reads += 1; + + // we do one write to old storage and one write to new storage + writes += 2; + + // total outbox count + outbox_count + .entry((key.0, key.1)) + .and_modify(|count| *count += 1) + .or_insert(1); + + OutboxNew::::insert(key, msg); + }); + + outbox_count.into_iter().for_each(|(key, count)| { + // we do one write to the outbox message count + writes += 1; + OutboxMessageCount::::insert(key, count); + }); + + T::DbWeight::get().reads_writes(reads, writes) + } +} + +#[cfg(test)] +mod tests { + use crate::migrations::messenger_migration::{ + migrate_messenger_storages, InboxResponses, Outbox, + }; + use crate::mock::chain_a::{new_test_ext, Runtime, SelfChainId}; + use crate::{InboxResponses as InboxResponsesNew, Outbox as OutboxNew, OutboxMessageCount}; + use frame_support::weights::RuntimeDbWeight; + use sp_core::Get; + use sp_domains::{ChainId, ChannelId}; + use sp_messenger::endpoint::{Endpoint, EndpointRequest}; + use sp_messenger::messages::{Message, Nonce, Payload, RequestResponse, VersionedPayload}; + + #[test] + fn test_messenger_storage_migration() { + let mut ext = new_test_ext(); + let msg = Message { + src_chain_id: ChainId::Consensus, + dst_chain_id: SelfChainId::get(), + channel_id: Default::default(), + nonce: Default::default(), + payload: VersionedPayload::V0(Payload::Endpoint(RequestResponse::Request( + EndpointRequest { + src_endpoint: Endpoint::Id(0), + dst_endpoint: Endpoint::Id(0), + payload: vec![], + }, + ))), + last_delivered_message_response_nonce: None, + }; + ext.execute_with(|| { + // one inbox response + InboxResponses::::insert( + (ChainId::Consensus, ChannelId::zero(), Nonce::zero()), + msg.clone(), + ); + + // outbox responses + Outbox::::insert( + (ChainId::Consensus, ChannelId::zero(), Nonce::zero()), + msg.clone(), + ); + Outbox::::insert( + (ChainId::Consensus, ChannelId::zero(), Nonce::one()), + msg.clone(), + ); + Outbox::::insert( + (ChainId::Consensus, ChannelId::one(), Nonce::zero()), + msg.clone(), + ); + }); + + ext.commit_all().unwrap(); + + ext.execute_with(|| { + let weights = migrate_messenger_storages::(); + // 1 read and 2 writes for inbox response + // 3 reads and 6 writes for outbox + // 2 writes for Outbox message count + let db_weights: RuntimeDbWeight = ::DbWeight::get(); + assert_eq!(weights, db_weights.reads_writes(4, 10),); + + assert_eq!( + InboxResponsesNew::::get(( + ChainId::Consensus, + ChannelId::zero(), + Nonce::zero() + )), + Some(msg.clone()) + ); + + assert_eq!( + OutboxNew::::get((ChainId::Consensus, ChannelId::zero(), Nonce::zero())), + Some(msg.clone()) + ); + + assert_eq!( + OutboxNew::::get((ChainId::Consensus, ChannelId::zero(), Nonce::one())), + Some(msg.clone()) + ); + + assert_eq!( + OutboxNew::::get((ChainId::Consensus, ChannelId::one(), Nonce::zero())), + Some(msg.clone()) + ); + + assert_eq!( + OutboxMessageCount::::get((ChainId::Consensus, ChannelId::zero())), + 2 + ); + + assert_eq!( + OutboxMessageCount::::get((ChainId::Consensus, ChannelId::one())), + 1 + ); + }); + } +} diff --git a/domains/runtime/auto-id/src/lib.rs b/domains/runtime/auto-id/src/lib.rs index ba45fc839e..d3992e35c4 100644 --- a/domains/runtime/auto-id/src/lib.rs +++ b/domains/runtime/auto-id/src/lib.rs @@ -106,6 +106,8 @@ pub type Executive = domain_pallet_executive::Executive< frame_system::ChainContext, Runtime, AllPalletsWithSystem, + // TODO: remove once migrations are done + pallet_messenger::migrations::VersionCheckedMigrateDomainsV0ToV1, >; impl_opaque_keys! { diff --git a/domains/runtime/evm/src/lib.rs b/domains/runtime/evm/src/lib.rs index f240de0013..0c9dbde59f 100644 --- a/domains/runtime/evm/src/lib.rs +++ b/domains/runtime/evm/src/lib.rs @@ -143,6 +143,8 @@ pub type Executive = domain_pallet_executive::Executive< frame_system::ChainContext, Runtime, AllPalletsWithSystem, + // TODO: remove once migrations are done + pallet_messenger::migrations::VersionCheckedMigrateDomainsV0ToV1, >; impl fp_self_contained::SelfContainedCall for RuntimeCall {