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::vec::Vec;
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::{Identity, storage_alias};
15use sp_domains::{ChainId, ChannelId};
16use sp_messenger::messages::{Channel as ChannelV1, ChannelState, Nonce};
17
18pub type VersionCheckedMigrateDomainsV1ToV2<T> = VersionedMigration<
19    1,
20    2,
21    VersionUncheckedMigrateV1ToV2<T>,
22    Pallet<T>,
23    <T as frame_system::Config>::DbWeight,
24>;
25
26pub struct VersionUncheckedMigrateV1ToV2<T>(PhantomData<T>);
27impl<T: Config> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateV1ToV2<T> {
28    fn on_runtime_upgrade() -> Weight {
29        migrate_channels::<T>()
30    }
31}
32
33/// Fee model to send a request and receive a response from another chain.
34#[derive(Default, Debug, Encode, Decode, Clone, Copy, Eq, PartialEq, TypeInfo)]
35pub struct FeeModel<Balance> {
36    /// Fee to relay message from one chain to another
37    pub relay_fee: Balance,
38}
39
40#[derive(Default, Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)]
41struct Channel<Balance, AccountId> {
42    /// Channel identifier.
43    pub channel_id: ChannelId,
44    /// State of the channel.
45    pub state: ChannelState,
46    /// Next inbox nonce.
47    pub next_inbox_nonce: Nonce,
48    /// Next outbox nonce.
49    pub next_outbox_nonce: Nonce,
50    /// Latest outbox message nonce for which response was received from dst_chain.
51    pub latest_response_received_message_nonce: Option<Nonce>,
52    /// Maximum outgoing non-delivered messages.
53    pub max_outgoing_messages: u32,
54    /// Fee model for this channel between the chains.
55    pub fee: FeeModel<Balance>,
56    /// Owner of the channel
57    /// Owner maybe None if the channel was initiated on the other chain.
58    pub maybe_owner: Option<AccountId>,
59    /// The amount of funds put on hold by the owner account for this channel
60    pub channel_reserve_fee: Balance,
61}
62
63impl<Balance, AccountId> From<Channel<Balance, AccountId>> for ChannelV1<Balance, AccountId> {
64    fn from(value: Channel<Balance, AccountId>) -> Self {
65        ChannelV1 {
66            channel_id: value.channel_id,
67            state: value.state,
68            next_inbox_nonce: value.next_inbox_nonce,
69            next_outbox_nonce: value.next_outbox_nonce,
70            latest_response_received_message_nonce: value.latest_response_received_message_nonce,
71            max_outgoing_messages: value.max_outgoing_messages,
72            maybe_owner: value.maybe_owner,
73            channel_reserve_fee: value.channel_reserve_fee,
74        }
75    }
76}
77
78pub(crate) mod migrate_channels {
79    use super::*;
80    use sp_messenger::messages::ChannelStateWithNonce;
81    use sp_runtime::traits::Get;
82
83    #[storage_alias]
84    pub(super) type Channels<T: Config> = StorageDoubleMap<
85        Pallet<T>,
86        Identity,
87        ChainId,
88        Identity,
89        ChannelId,
90        Channel<BalanceOf<T>, <T as frame_system::Config>::AccountId>,
91        OptionQuery,
92    >;
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_channels_and_states<T: Config>()
115    -> Vec<(ChainId, ChannelId, ChannelStateWithNonce)> {
116        let keys: Vec<(ChainId, ChannelId)> = ChannelStorageV1::<T>::iter_keys().collect();
117        keys.into_iter()
118            .filter_map(|(chain_id, channel_id)| {
119                get_channel::<T>(chain_id, channel_id).map(|channel| {
120                    let state = channel.state;
121                    let state_with_nonce = match state {
122                        ChannelState::Initiated => ChannelStateWithNonce::Initiated,
123                        ChannelState::Open => ChannelStateWithNonce::Open,
124                        ChannelState::Closed => ChannelStateWithNonce::Closed {
125                            next_outbox_nonce: channel.next_outbox_nonce,
126                            next_inbox_nonce: channel.next_inbox_nonce,
127                        },
128                    };
129
130                    (chain_id, channel_id, state_with_nonce)
131                })
132            })
133            .collect()
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use super::migrate_channels::Channels;
140    use super::*;
141    use crate::mock::chain_a::{Runtime, new_test_ext};
142    use frame_support::weights::RuntimeDbWeight;
143    use sp_runtime::traits::Get;
144
145    #[test]
146    fn test_channel_migration() {
147        let mut ext = new_test_ext();
148        let chain_id = ChainId::Consensus;
149        let channel_id = ChannelId::zero();
150        let channel = Channel {
151            channel_id,
152            state: ChannelState::Open,
153            next_inbox_nonce: Nonce::zero(),
154            next_outbox_nonce: Nonce::one(),
155            latest_response_received_message_nonce: Some(Nonce::from(100u32)),
156            max_outgoing_messages: 100,
157            fee: FeeModel { relay_fee: 100 },
158            maybe_owner: Some(100u64),
159            channel_reserve_fee: 200,
160        };
161
162        let channel_v1 = ChannelV1 {
163            channel_id,
164            state: ChannelState::Open,
165            next_inbox_nonce: Nonce::zero(),
166            next_outbox_nonce: Nonce::one(),
167            latest_response_received_message_nonce: Some(Nonce::from(100u32)),
168            max_outgoing_messages: 100,
169            maybe_owner: Some(100u64),
170            channel_reserve_fee: 200,
171        };
172
173        ext.execute_with(|| Channels::<Runtime>::insert(chain_id, channel_id, channel));
174
175        ext.commit_all().unwrap();
176
177        ext.execute_with(|| {
178            let weight = migrate_channels::<Runtime>();
179            let channel = ChannelStorageV1::<Runtime>::get(chain_id, channel_id).unwrap();
180            let db_weights: RuntimeDbWeight = <Runtime as frame_system::Config>::DbWeight::get();
181            assert_eq!(weight, db_weights.reads_writes(1, 1),);
182            assert_eq!(channel, channel_v1);
183        })
184    }
185}