pallet_messenger/migrations/
v1_to_v2.rs

1//! Migration module for pallet-messenger
2
3#[cfg(not(feature = "std"))]
4extern crate alloc;
5use crate::migrations::v1_to_v2::migrate_channels::migrate_channels;
6use crate::{BalanceOf, Channels as ChannelStorageV1, Config, Pallet};
7#[cfg(not(feature = "std"))]
8use alloc::collections::BTreeSet;
9use core::marker::PhantomData;
10use frame_support::migrations::VersionedMigration;
11use frame_support::pallet_prelude::{Decode, Encode, OptionQuery, TypeInfo};
12use frame_support::traits::UncheckedOnRuntimeUpgrade;
13use frame_support::weights::Weight;
14use frame_support::{storage_alias, Identity};
15use sp_domains::{ChainId, ChannelId};
16use sp_messenger::messages::{Channel as ChannelV1, ChannelState, Nonce};
17#[cfg(feature = "std")]
18use std::collections::BTreeSet;
19
20pub type VersionCheckedMigrateDomainsV1ToV2<T> = VersionedMigration<
21    1,
22    2,
23    VersionUncheckedMigrateV1ToV2<T>,
24    Pallet<T>,
25    <T as frame_system::Config>::DbWeight,
26>;
27
28pub struct VersionUncheckedMigrateV1ToV2<T>(PhantomData<T>);
29impl<T: Config> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateV1ToV2<T> {
30    fn on_runtime_upgrade() -> Weight {
31        migrate_channels::<T>()
32    }
33}
34
35/// Fee model to send a request and receive a response from another chain.
36#[derive(Default, Debug, Encode, Decode, Clone, Copy, Eq, PartialEq, TypeInfo)]
37pub struct FeeModel<Balance> {
38    /// Fee to relay message from one chain to another
39    pub relay_fee: Balance,
40}
41
42#[derive(Default, Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
43struct Channel<Balance, AccountId> {
44    /// Channel identifier.
45    pub channel_id: ChannelId,
46    /// State of the channel.
47    pub state: ChannelState,
48    /// Next inbox nonce.
49    pub next_inbox_nonce: Nonce,
50    /// Next outbox nonce.
51    pub next_outbox_nonce: Nonce,
52    /// Latest outbox message nonce for which response was received from dst_chain.
53    pub latest_response_received_message_nonce: Option<Nonce>,
54    /// Maximum outgoing non-delivered messages.
55    pub max_outgoing_messages: u32,
56    /// Fee model for this channel between the chains.
57    pub fee: FeeModel<Balance>,
58    /// Owner of the channel
59    /// Owner maybe None if the channel was initiated on the other chain.
60    pub maybe_owner: Option<AccountId>,
61    /// The amount of funds put on hold by the owner account for this channel
62    pub channel_reserve_fee: Balance,
63}
64
65impl<Balance, AccountId> From<Channel<Balance, AccountId>> for ChannelV1<Balance, AccountId> {
66    fn from(value: Channel<Balance, AccountId>) -> Self {
67        ChannelV1 {
68            channel_id: value.channel_id,
69            state: value.state,
70            next_inbox_nonce: value.next_inbox_nonce,
71            next_outbox_nonce: value.next_outbox_nonce,
72            latest_response_received_message_nonce: value.latest_response_received_message_nonce,
73            max_outgoing_messages: value.max_outgoing_messages,
74            maybe_owner: value.maybe_owner,
75            channel_reserve_fee: value.channel_reserve_fee,
76        }
77    }
78}
79
80pub(crate) mod migrate_channels {
81    use super::*;
82    use sp_runtime::traits::Get;
83
84    #[storage_alias]
85    pub(super) type Channels<T: Config> = StorageDoubleMap<
86        Pallet<T>,
87        Identity,
88        ChainId,
89        Identity,
90        ChannelId,
91        Channel<BalanceOf<T>, <T as frame_system::Config>::AccountId>,
92        OptionQuery,
93    >;
94    pub(super) fn migrate_channels<T: Config>() -> Weight {
95        let mut count = 0;
96        Channels::<T>::drain().for_each(|(chain_id, channel_id, channel)| {
97            let channel_v1: ChannelV1<BalanceOf<T>, T::AccountId> = channel.into();
98            ChannelStorageV1::<T>::insert(chain_id, channel_id, channel_v1);
99            count += 1;
100        });
101
102        T::DbWeight::get().reads_writes(count, count)
103    }
104
105    pub(crate) fn get_channel<T: Config>(
106        chain_id: ChainId,
107        channel_id: ChannelId,
108    ) -> Option<ChannelV1<BalanceOf<T>, T::AccountId>> {
109        ChannelStorageV1::<T>::get(chain_id, channel_id).or_else(|| {
110            Channels::<T>::get(chain_id, channel_id).map(|old_channel| old_channel.into())
111        })
112    }
113
114    pub(crate) fn get_open_channels<T: Config>() -> BTreeSet<(ChainId, ChannelId)> {
115        let keys: BTreeSet<(ChainId, ChannelId)> = ChannelStorageV1::<T>::iter_keys().collect();
116        keys.into_iter()
117            .filter(|(chain_id, channel_id)| {
118                get_channel::<T>(*chain_id, *channel_id)
119                    .map(|channel| channel.state != ChannelState::Closed)
120                    .unwrap_or_default()
121            })
122            .collect()
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::migrate_channels::Channels;
129    use super::*;
130    use crate::mock::chain_a::{new_test_ext, Runtime};
131    use frame_support::weights::RuntimeDbWeight;
132    use sp_runtime::traits::Get;
133
134    #[test]
135    fn test_channel_migration() {
136        let mut ext = new_test_ext();
137        let chain_id = ChainId::Consensus;
138        let channel_id = ChannelId::zero();
139        let channel = Channel {
140            channel_id,
141            state: ChannelState::Open,
142            next_inbox_nonce: Nonce::zero(),
143            next_outbox_nonce: Nonce::one(),
144            latest_response_received_message_nonce: Some(Nonce::from(100u32)),
145            max_outgoing_messages: 100,
146            fee: FeeModel { relay_fee: 100 },
147            maybe_owner: Some(100u64),
148            channel_reserve_fee: 200,
149        };
150
151        let channel_v1 = ChannelV1 {
152            channel_id,
153            state: ChannelState::Open,
154            next_inbox_nonce: Nonce::zero(),
155            next_outbox_nonce: Nonce::one(),
156            latest_response_received_message_nonce: Some(Nonce::from(100u32)),
157            max_outgoing_messages: 100,
158            maybe_owner: Some(100u64),
159            channel_reserve_fee: 200,
160        };
161
162        ext.execute_with(|| Channels::<Runtime>::insert(chain_id, channel_id, channel));
163
164        ext.commit_all().unwrap();
165
166        ext.execute_with(|| {
167            let weight = migrate_channels::<Runtime>();
168            let channel = ChannelStorageV1::<Runtime>::get(chain_id, channel_id).unwrap();
169            let db_weights: RuntimeDbWeight = <Runtime as frame_system::Config>::DbWeight::get();
170            assert_eq!(weight, db_weights.reads_writes(1, 1),);
171            assert_eq!(channel, channel_v1);
172        })
173    }
174}