1
// Copyright (C) Moondance Labs Ltd.
2
// This file is part of Tanssi.
3

            
4
// Tanssi is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Tanssi is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Tanssi.  If not, see <http://www.gnu.org/licenses/>
16

            
17
use crate::xcm_config::UniversalLocation;
18
use crate::EthereumLocation;
19
use snowbridge_core::TokenIdOf;
20
use xcm::latest::Junction::GlobalConsensus;
21
use xcm::latest::Junctions::Here;
22
use xcm::latest::Junctions::X1;
23
use xcm::latest::NetworkId;
24
use xcm::latest::{Location, Reanchorable, ROCOCO_GENESIS_HASH};
25
use xcm_executor::traits::ConvertLocation;
26
use {
27
    crate::{
28
        tests::common::ExtBuilder, BeefyMmrLeaf, CollatorConfiguration, ExternalValidators,
29
        PalletInfo, Runtime, Session,
30
    },
31
    beefy_primitives::mmr::BeefyAuthoritySet,
32
    frame_support::{migration::clear_storage_prefix, storage::unhashed, traits::PalletInfo as _},
33
    pallet_migrations::Migration,
34
    parity_scale_codec::Encode,
35
    sp_arithmetic::Perbill,
36
    tanssi_runtime_common::migrations::{
37
        snowbridge_system_migration::DancelightLocation, BondedErasTimestampMigration,
38
        ExternalValidatorsInitialMigration, HostConfigurationV3,
39
        MigrateConfigurationAddFullRotationMode, MigrateEthSystemGenesisHashes,
40
        MigrateMMRLeafPallet,
41
    },
42
    xcm::v3::Weight,
43
};
44

            
45
#[test]
46
1
fn test_migration_mmr_leaf_pallet_renaming() {
47
1
    ExtBuilder::default().build().execute_with(|| {
48
1
        let migrate_mmr_leaf_pallet = MigrateMMRLeafPallet::<Runtime>(Default::default());
49
1
        let old_pallet_name = MigrateMMRLeafPallet::<Runtime>::old_pallet_name();
50
1
        let old_storage_1 = frame_support::storage::storage_prefix(
51
1
            old_pallet_name.as_bytes(),
52
1
            b"example_storage_1",
53
1
        );
54
1
        let new_storage_1 = frame_support::storage::storage_prefix(
55
1
            PalletInfo::name::<BeefyMmrLeaf>()
56
1
                .expect("BeefyMMRLeaf pallet must be part of the runtime")
57
1
                .as_bytes(),
58
1
            b"example_storage_1",
59
1
        );
60
1
        unhashed::put(&old_storage_1, &1u64);
61
1

            
62
1
        let beefy_authority_set: BeefyAuthoritySet<()> = BeefyAuthoritySet {
63
1
            len: 5,
64
1
            ..Default::default()
65
1
        };
66
1
        let old_storage_2 = frame_support::storage::storage_prefix(
67
1
            old_pallet_name.as_bytes(),
68
1
            b"example_storage_2",
69
1
        );
70
1
        let new_storage_2 = frame_support::storage::storage_prefix(
71
1
            PalletInfo::name::<BeefyMmrLeaf>()
72
1
                .expect("BeefyMMRLeaf pallet must be part of the runtime")
73
1
                .as_bytes(),
74
1
            b"example_storage_2",
75
1
        );
76
1
        unhashed::put(&old_storage_2, &beefy_authority_set);
77
1

            
78
1
        let used_weight = migrate_mmr_leaf_pallet.migrate(Weight::MAX);
79
1
        assert_eq!(used_weight, Weight::MAX);
80

            
81
1
        assert_eq!(unhashed::get::<u64>(&old_storage_1), None);
82
1
        assert_eq!(unhashed::get::<BeefyAuthoritySet<()>>(&old_storage_2), None);
83

            
84
1
        assert_eq!(unhashed::get::<u64>(&new_storage_1), Some(1u64));
85
1
        assert_eq!(
86
1
            unhashed::get::<BeefyAuthoritySet<()>>(&new_storage_2),
87
1
            Some(BeefyAuthoritySet {
88
1
                len: 5,
89
1
                ..Default::default()
90
1
            })
91
1
        );
92
1
    });
93
1
}
94

            
95
#[test]
96
1
fn test_migration_external_validators_pallet() {
97
1
    ExtBuilder::default().build().execute_with(|| {
98
1
        let migrate_external_validators =
99
1
            ExternalValidatorsInitialMigration::<Runtime>(Default::default());
100
1
        let old_pallet_name = b"ValidatorManager";
101
1

            
102
1
        // Kill storage of ExternalValidators pallet, because this migration will initialize this pallet
103
1
        let _ = clear_storage_prefix(b"ExternalValidators", b"", b"", None, None);
104
1

            
105
1
        // Simulate adding data to the old pallet storage
106
1
        // The value is not used for anything, we only care that it is removed by the migration.
107
1
        let old_storage_key =
108
1
            frame_support::storage::storage_prefix(old_pallet_name, b"ValidatorsToAdd");
109
1
        let expected_validators: Vec<u64> = vec![5, 6];
110
1
        unhashed::put(&old_storage_key, &expected_validators);
111
1

            
112
1
        // Run migration
113
1
        let _used_weight = migrate_external_validators.migrate(Weight::MAX);
114
1

            
115
1
        // Assert that ValidatorManager pallet prefix is empty after migration
116
1
        let old_pallet_key = frame_support::storage::storage_prefix(old_pallet_name, b"");
117
1
        let old_storage_exists = unhashed::contains_prefixed_key(&old_pallet_key);
118
1
        assert!(
119
1
            !old_storage_exists,
120
            "Old pallet storage should be cleared after migration"
121
        );
122

            
123
        // Assert that ExternalValidators has the validators from ValidatorManager
124
1
        let migrated_validators = ExternalValidators::validators();
125
1
        let empty = vec![];
126
1
        assert_ne!(
127
            migrated_validators, empty,
128
            "ExternalValidators should not be empty after migration"
129
        );
130

            
131
        // ExternalValidators should be equal to validators from Session::queued_keys
132
2
        let expected_validators: Vec<_> = Session::queued_keys().into_iter().map(|x| x.0).collect();
133
1
        assert_eq!(migrated_validators, expected_validators);
134
1
    });
135
1
}
136

            
137
#[test]
138
1
fn test_migration_config_add_full_rotation_mode() {
139
1
    ExtBuilder::default().build().execute_with(|| {
140
        const CONFIGURATION_ACTIVE_CONFIG_KEY: &[u8] =
141
            &hex_literal::hex!("86e86c1d728ee2b18f76dd0e04d96cdbb4b49d95320d9021994c850f25b8e385");
142
        const CONFIGURATION_PENDING_CONFIGS_KEY: &[u8] =
143
            &hex_literal::hex!("86e86c1d728ee2b18f76dd0e04d96cdb53b4123b2e186e07fb7bad5dda5f55c0");
144

            
145
        // Modify active config
146
1
        frame_support::storage::unhashed::put_raw(
147
1
            CONFIGURATION_ACTIVE_CONFIG_KEY,
148
1
            &HostConfigurationV3 {
149
1
                max_collators: 5,
150
1
                min_orchestrator_collators: 2,
151
1
                max_orchestrator_collators: 1,
152
1
                collators_per_container: 3,
153
1
                full_rotation_period: 4,
154
1
                collators_per_parathread: 2,
155
1
                parathreads_per_collator: 1,
156
1
                target_container_chain_fullness: Perbill::from_percent(45),
157
1
                max_parachain_cores_percentage: Some(Perbill::from_percent(75)),
158
1
            }
159
1
            .encode(),
160
1
        );
161
1
        // Modify pending configs
162
1
        frame_support::storage::unhashed::put_raw(
163
1
            CONFIGURATION_PENDING_CONFIGS_KEY,
164
1
            &vec![
165
1
                (
166
1
                    1234u32,
167
1
                    HostConfigurationV3 {
168
1
                        max_collators: 1,
169
1
                        min_orchestrator_collators: 4,
170
1
                        max_orchestrator_collators: 45,
171
1
                        collators_per_container: 5,
172
1
                        full_rotation_period: 1,
173
1
                        collators_per_parathread: 1,
174
1
                        parathreads_per_collator: 1,
175
1
                        target_container_chain_fullness: Perbill::from_percent(65),
176
1
                        max_parachain_cores_percentage: Some(Perbill::from_percent(75)),
177
1
                    },
178
1
                ),
179
1
                (
180
1
                    5678u32,
181
1
                    HostConfigurationV3 {
182
1
                        max_collators: 1,
183
1
                        min_orchestrator_collators: 4,
184
1
                        max_orchestrator_collators: 45,
185
1
                        collators_per_container: 5,
186
1
                        full_rotation_period: 1,
187
1
                        collators_per_parathread: 1,
188
1
                        parathreads_per_collator: 1,
189
1
                        target_container_chain_fullness: Perbill::from_percent(65),
190
1
                        max_parachain_cores_percentage: Some(Perbill::from_percent(75)),
191
1
                    },
192
1
                ),
193
1
            ]
194
1
            .encode(),
195
1
        );
196
1

            
197
1
        let migration = MigrateConfigurationAddFullRotationMode::<Runtime>(Default::default());
198
1
        migration.migrate(Default::default());
199
1

            
200
1
        let expected_active = pallet_configuration::HostConfiguration {
201
1
            max_collators: 5,
202
1
            min_orchestrator_collators: 2,
203
1
            max_orchestrator_collators: 1,
204
1
            collators_per_container: 3,
205
1
            full_rotation_period: 4,
206
1
            collators_per_parathread: 2,
207
1
            parathreads_per_collator: 1,
208
1
            target_container_chain_fullness: Perbill::from_percent(45),
209
1
            max_parachain_cores_percentage: Some(Perbill::from_percent(75)),
210
1
            ..Default::default()
211
1
        };
212
1
        assert_eq!(CollatorConfiguration::config(), expected_active);
213

            
214
1
        let expected_pending = vec![
215
1
            (
216
1
                1234u32,
217
1
                pallet_configuration::HostConfiguration {
218
1
                    max_collators: 1,
219
1
                    min_orchestrator_collators: 4,
220
1
                    max_orchestrator_collators: 45,
221
1
                    collators_per_container: 5,
222
1
                    full_rotation_period: 1,
223
1
                    collators_per_parathread: 1,
224
1
                    parathreads_per_collator: 1,
225
1
                    target_container_chain_fullness: Perbill::from_percent(65),
226
1
                    max_parachain_cores_percentage: Some(Perbill::from_percent(75)),
227
1
                    ..Default::default()
228
1
                },
229
1
            ),
230
1
            (
231
1
                5678u32,
232
1
                pallet_configuration::HostConfiguration {
233
1
                    max_collators: 1,
234
1
                    min_orchestrator_collators: 4,
235
1
                    max_orchestrator_collators: 45,
236
1
                    collators_per_container: 5,
237
1
                    full_rotation_period: 1,
238
1
                    collators_per_parathread: 1,
239
1
                    parathreads_per_collator: 1,
240
1
                    target_container_chain_fullness: Perbill::from_percent(65),
241
1
                    max_parachain_cores_percentage: Some(Perbill::from_percent(75)),
242
1
                    ..Default::default()
243
1
                },
244
1
            ),
245
1
        ];
246
1
        assert_eq!(CollatorConfiguration::pending_configs(), expected_pending);
247
1
    });
248
1
}
249

            
250
#[test]
251
1
fn test_add_timestamp_to_bonded_eras_migration() {
252
1
    ExtBuilder::default().build().execute_with(|| {
253
1
        let bonded_eras_key =
254
1
            pallet_external_validator_slashes::BondedEras::<Runtime>::hashed_key();
255
1

            
256
1
        let previous_value: Vec<(sp_staking::EraIndex, sp_staking::SessionIndex)> =
257
1
            vec![(1, 1), (2, 2), (3, 3)];
258
1

            
259
1
        // Modify storage to pevious value
260
1
        frame_support::storage::unhashed::put_raw(&bonded_eras_key, &previous_value.encode());
261
1

            
262
1
        let migration = BondedErasTimestampMigration::<Runtime>(Default::default());
263
1
        migration.migrate(Default::default());
264
1

            
265
1
        let expected_bonded_eras_after: Vec<(sp_staking::EraIndex, sp_staking::SessionIndex, u64)> =
266
1
            previous_value
267
1
                .iter()
268
3
                .map(|(era, session)| (*era, *session, 0u64))
269
1
                .collect();
270
1
        assert_eq!(
271
1
            pallet_external_validator_slashes::BondedEras::<Runtime>::get(),
272
1
            expected_bonded_eras_after
273
1
        );
274
1
    });
275
1
}
276

            
277
#[test]
278
1
fn test_genesis_hashes_migration() {
279
1
    ExtBuilder::default().build().execute_with(|| {
280
        // Raw values copied from stagelight runtime 1100
281
        const FOREIGN_TO_NATIVE_ID_KEY: &[u8] =
282
            &hex_literal::hex!("ccee781f0b9380204db9882d1b1c771d53e99bc228247291bd1d0d34fa7f53993faff06e7c800c84bd5d1f5ea566d14962e8f33b7fb0e7e2d2276564061a2f3c7bcb612e733b8bf5733ea16cee0ecba6");
283
        const FOREIGN_TO_NATIVE_ID_VALUE: &[u8] =
284
            &hex_literal::hex!("010109006408de7737c59c238890533af25896a2c20608d8b380bb01029acb392781063e");
285
        const NATIVE_TO_FOREIGN_ID_KEY: &[u8] =
286
            &hex_literal::hex!("ccee781f0b9380204db9882d1b1c771ddec2be471806c468b349224cf542e742be02dd7069c50095d8caa8a61b7c3af5010109006408de7737c59c238890533af25896a2c20608d8b380bb01029acb392781063e");
287
        const NATIVE_TO_FOREIGN_ID_VALUE: &[u8] =
288
            &hex_literal::hex!("62e8f33b7fb0e7e2d2276564061a2f3c7bcb612e733b8bf5733ea16cee0ecba6");
289

            
290
        // Write foreign assets to pallet storage
291
1
        frame_support::storage::unhashed::put_raw(
292
1
            FOREIGN_TO_NATIVE_ID_KEY,
293
1
            FOREIGN_TO_NATIVE_ID_VALUE,
294
1
        );
295
1
        frame_support::storage::unhashed::put_raw(
296
1
            NATIVE_TO_FOREIGN_ID_KEY,
297
1
            NATIVE_TO_FOREIGN_ID_VALUE,
298
1
        );
299
1

            
300
1
        // Check storage before migration
301
1
        let f_n = snowbridge_pallet_system::ForeignToNativeId::<Runtime>::iter().collect::<Vec<_>>();
302
1
        let n_f = snowbridge_pallet_system::NativeToForeignId::<Runtime>::iter().collect::<Vec<_>>();
303
1

            
304
1
        assert_eq!(f_n, [(
305
1
            hex_literal::hex!("62e8f33b7fb0e7e2d2276564061a2f3c7bcb612e733b8bf5733ea16cee0ecba6").into(),
306
1
            Location {
307
1
                parents: 1,
308
1
                interior: X1([GlobalConsensus(NetworkId::ByGenesis(ROCOCO_GENESIS_HASH))].into()),
309
1
            },
310
1
        )]);
311
1
        assert_eq!(n_f, [(
312
1
            Location {
313
1
                parents: 1,
314
1
                interior: X1([GlobalConsensus(NetworkId::ByGenesis(ROCOCO_GENESIS_HASH))].into()),
315
1
            },
316
1
            hex_literal::hex!("62e8f33b7fb0e7e2d2276564061a2f3c7bcb612e733b8bf5733ea16cee0ecba6").into(),
317
1
        )]);
318

            
319
1
        let migration = MigrateEthSystemGenesisHashes::<Runtime, DancelightLocation>(Default::default());
320
1
        migration.migrate(Default::default());
321
1

            
322
1
        // Check storage after migration
323
1
        let f_n = snowbridge_pallet_system::ForeignToNativeId::<Runtime>::iter().collect::<Vec<_>>();
324
1
        let n_f = snowbridge_pallet_system::NativeToForeignId::<Runtime>::iter().collect::<Vec<_>>();
325
1

            
326
1
        assert_eq!(f_n, [(
327
1
            hex_literal::hex!("62e8f33b7fb0e7e2d2276564061a2f3c7bcb612e733b8bf5733ea16cee0ecba6").into(),
328
1
            DancelightLocation::get(),
329
1
        )]);
330
1
        assert_eq!(n_f, [(
331
1
            DancelightLocation::get(),
332
1
            hex_literal::hex!("62e8f33b7fb0e7e2d2276564061a2f3c7bcb612e733b8bf5733ea16cee0ecba6").into(),
333
1
        )]);
334
1
    });
335
1
}
336

            
337
#[test]
338
1
fn snowbridge_ethereum_system_token_id_does_not_change() {
339
1
    // If a new XCM version is released, we need to check that this location still encodes to the same
340
1
    // token id.
341
1
    // If this test fails, we need a migration in snowbridge_pallet_system to migrate the mappings
342
1
    // ForeignToNativeId and NativeToForeignId. Use migration SnowbridgeEthereumSystemXcmV5 as base.
343
1

            
344
1
    // Location of native starlight token.
345
1
    // When we support other token locations, maybe add them to this test as well.
346
1
    let location = Location {
347
1
        parents: 0,
348
1
        interior: Here,
349
1
    };
350
1

            
351
1
    let ethereum_location = EthereumLocation::get();
352
1
    // reanchor to Ethereum context
353
1
    // This means to add this junction: GlobalConsensus(NetworkId::ByGenesis(DANCELIGHT_GENESIS_HASH))
354
1
    let location = location
355
1
        .clone()
356
1
        .reanchored(&ethereum_location, &UniversalLocation::get())
357
1
        .unwrap();
358
1

            
359
1
    let token_id = TokenIdOf::convert_location(&location).unwrap();
360
1

            
361
1
    // The token id from stagelight has been derived using xcm v4, but we are in xcm v5.
362
1
    // The derived token id from xcm v4 (the one you will find on chain) is:
363
1
    // 0x62e8f33b7fb0e7e2d2276564061a2f3c7bcb612e733b8bf5733ea16cee0ecba6
364
1
    // So the exact token id from below is not important, as long as it does not change:
365
1
    assert_eq!(
366
1
        token_id,
367
1
        hex_literal::hex!("e95142d5aca3299068a3d9b4a659f9589559382d0a130a1d7cedc67d6c3d401d")
368
1
            .into()
369
1
    );
370
1
}