1
// Copyright 2019-2025 PureStake Inc.
2
// This file is part of Moonbeam.
3

            
4
// Moonbeam 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
// Moonbeam 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 Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
use crate::xcm_mock::*;
18
use crate::{
19
	xcm_mock::parachain::PolkadotXcm,
20
	xcm_testing::{add_supported_asset, currency_to_asset},
21
};
22
use frame_support::{assert_ok, weights::Weight};
23
use moonbase_runtime::xcm_config::AssetType;
24
use sp_std::boxed::Box;
25
use xcm::{
26
	latest::prelude::{
27
		AccountKey20, Location, PalletInstance, Parachain, QueryResponse, Response, WeightLimit,
28
		Xcm,
29
	},
30
	VersionedAssets,
31
};
32
use xcm::{VersionedLocation, WrapVersion};
33
use xcm_primitives::{split_location_into_chain_part_and_beneficiary, DEFAULT_PROOF_SIZE};
34
use xcm_simulator::TestExt;
35

            
36
#[test]
37
1
fn test_automatic_versioning_on_runtime_upgrade_with_relay() {
38
1
	MockNet::reset();
39
1

            
40
1
	let source_location = parachain::AssetType::Xcm(xcm::v3::Location::parent());
41
1
	let asset_metadata = parachain::AssetMetadata {
42
1
		name: b"RelayToken".to_vec(),
43
1
		symbol: b"Relay".to_vec(),
44
1
		decimals: 12,
45
1
	};
46
1
	// register relay asset in parachain A and set XCM version to 1
47
1
	ParaA::execute_with(|| {
48
1
		parachain::XcmVersioner::set_version(1);
49
1
		assert_ok!(AssetManager::register_foreign_asset(
50
1
			parachain::RuntimeOrigin::root(),
51
1
			source_location.clone(),
52
1
			asset_metadata,
53
1
			1u128,
54
1
			true
55
1
		));
56
1
		assert_ok!(add_supported_asset(source_location, 0));
57
1
	});
58
1

            
59
1
	let response = Response::Version(2);
60
1
	let querier: Location = [].into();
61
1

            
62
1
	// This is irrelevant, nothing will be done with this message,
63
1
	// but we need to pass a message as an argument to trigger the storage change
64
1
	let mock_message: Xcm<()> = Xcm(vec![QueryResponse {
65
1
		query_id: 0,
66
1
		response,
67
1
		max_weight: Weight::zero(),
68
1
		querier: Some(querier),
69
1
	}]);
70
1
	// The router is mocked, and we cannot use WrapVersion in ChildParachainRouter. So we will force
71
1
	// it directly here
72
1
	// Actually send relay asset to parachain
73
1
	let dest: Location = AccountKey20 {
74
1
		network: None,
75
1
		key: PARAALICE,
76
1
	}
77
1
	.into();
78
1

            
79
1
	Relay::execute_with(|| {
80
1
		// This sets the default version, for not known destinations
81
1
		assert_ok!(RelayChainPalletXcm::force_default_xcm_version(
82
1
			relay_chain::RuntimeOrigin::root(),
83
1
			Some(3)
84
1
		));
85

            
86
		// Wrap version, which sets VersionedStorage
87
		// This is necessary because the mock router does not use wrap_version, but
88
		// this is not necessary in prod
89
1
		assert_ok!(<RelayChainPalletXcm as WrapVersion>::wrap_version(
90
1
			&Parachain(1).into(),
91
1
			mock_message
92
1
		));
93

            
94
		// Transfer assets. Since it is an unknown destination, it will query for version
95
1
		assert_ok!(RelayChainPalletXcm::limited_reserve_transfer_assets(
96
1
			relay_chain::RuntimeOrigin::signed(RELAYALICE),
97
1
			Box::new(Parachain(1).into()),
98
1
			Box::new(VersionedLocation::from(dest).clone()),
99
1
			Box::new(([], 123).into()),
100
1
			0,
101
1
			WeightLimit::Unlimited
102
1
		));
103

            
104
		// Let's advance the relay. This should trigger the subscription message
105
1
		relay_chain::relay_roll_to(2);
106
1

            
107
1
		// queries should have been updated
108
1
		assert!(RelayChainPalletXcm::query(0).is_some());
109
1
	});
110
1

            
111
1
	let expected_supported_version: relay_chain::RuntimeEvent =
112
1
		pallet_xcm::Event::SupportedVersionChanged {
113
1
			location: Location {
114
1
				parents: 0,
115
1
				interior: [Parachain(1)].into(),
116
1
			},
117
1
			version: 1,
118
1
		}
119
1
		.into();
120
1

            
121
1
	Relay::execute_with(|| {
122
1
		// Assert that the events vector contains the version change
123
1
		assert!(relay_chain::relay_events().contains(&expected_supported_version));
124
1
	});
125
1

            
126
1
	// ParaA changes version to 2, and calls on_runtime_upgrade. This should notify the targets
127
1
	// of the new version change
128
1
	ParaA::execute_with(|| {
129
1
		// Set version
130
1
		parachain::XcmVersioner::set_version(2);
131
1
		// Do runtime upgrade
132
1
		parachain::on_runtime_upgrade();
133
1
		// Initialize block, to call on_initialize and notify targets
134
1
		parachain::para_roll_to(2);
135
1
		// Expect the event in the parachain
136
10
		assert!(parachain::para_events().iter().any(|e| matches!(
137
2
			e,
138
			parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::VersionChangeNotified {
139
				result: 2,
140
				..
141
			})
142
10
		)));
143
1
	});
144
1

            
145
1
	// This event should have been seen in the relay
146
1
	let expected_supported_version_2: relay_chain::RuntimeEvent =
147
1
		pallet_xcm::Event::SupportedVersionChanged {
148
1
			location: Location {
149
1
				parents: 0,
150
1
				interior: [Parachain(1)].into(),
151
1
			},
152
1
			version: 2,
153
1
		}
154
1
		.into();
155
1

            
156
1
	Relay::execute_with(|| {
157
1
		// Assert that the events vector contains the new version change
158
1
		assert!(relay_chain::relay_events().contains(&expected_supported_version_2));
159
1
	});
160
1
}
161

            
162
#[test]
163
1
fn test_automatic_versioning_on_runtime_upgrade_with_para_b() {
164
1
	MockNet::reset();
165
1

            
166
1
	let para_a_balances = Location::new(1, [Parachain(1), PalletInstance(1u8)]);
167
1
	let source_location: AssetType = para_a_balances
168
1
		.try_into()
169
1
		.expect("Location convertion to AssetType should succeed");
170
1
	let source_id: parachain::AssetId = source_location.clone().into();
171
1

            
172
1
	let asset_metadata = parachain::AssetMetadata {
173
1
		name: b"ParaAToken".to_vec(),
174
1
		symbol: b"ParaA".to_vec(),
175
1
		decimals: 18,
176
1
	};
177
1
	let response = Response::Version(2);
178
1
	let querier: Location = [].into();
179
1

            
180
1
	// This is irrelevant, nothing will be done with this message,
181
1
	// but we need to pass a message as an argument to trigger the storage change
182
1
	let mock_message: Xcm<()> = Xcm(vec![QueryResponse {
183
1
		query_id: 0,
184
1
		response,
185
1
		max_weight: Weight::zero(),
186
1
		querier: Some(querier),
187
1
	}]);
188
1

            
189
1
	ParaA::execute_with(|| {
190
1
		// advertised version
191
1
		parachain::XcmVersioner::set_version(2);
192
1
	});
193
1

            
194
1
	ParaB::execute_with(|| {
195
1
		// Let's try with v0
196
1
		parachain::XcmVersioner::set_version(0);
197
1

            
198
1
		assert_ok!(AssetManager::register_foreign_asset(
199
1
			parachain::RuntimeOrigin::root(),
200
1
			source_location.clone(),
201
1
			asset_metadata,
202
1
			1u128,
203
1
			true
204
1
		));
205
1
		assert_ok!(add_supported_asset(source_location, 0));
206
1
	});
207
1

            
208
1
	ParaA::execute_with(|| {
209
1
		// This sets the default version, for not known destinations
210
1
		assert_ok!(ParachainPalletXcm::force_default_xcm_version(
211
1
			parachain::RuntimeOrigin::root(),
212
1
			Some(3)
213
1
		));
214
		// Wrap version, which sets VersionedStorage
215
1
		assert_ok!(<ParachainPalletXcm as WrapVersion>::wrap_version(
216
1
			&Location::new(1, [Parachain(2)]).into(),
217
1
			mock_message
218
1
		));
219

            
220
1
		parachain::para_roll_to(2);
221
1

            
222
1
		// queries should have been updated
223
1
		assert!(ParachainPalletXcm::query(0).is_some());
224
1
	});
225
1

            
226
1
	let expected_supported_version: parachain::RuntimeEvent =
227
1
		pallet_xcm::Event::SupportedVersionChanged {
228
1
			location: Location {
229
1
				parents: 1,
230
1
				interior: [Parachain(2)].into(),
231
1
			},
232
1
			version: 0,
233
1
		}
234
1
		.into();
235
1

            
236
1
	ParaA::execute_with(|| {
237
1
		// Assert that the events vector contains the version change
238
1
		assert!(parachain::para_events().contains(&expected_supported_version));
239
1
	});
240
1

            
241
1
	// Let's ensure talking in v0 works
242
1
	let dest = Location {
243
1
		parents: 1,
244
1
		interior: [
245
1
			Parachain(2),
246
1
			AccountKey20 {
247
1
				network: None,
248
1
				key: PARAALICE.into(),
249
1
			},
250
1
		]
251
1
		.into(),
252
1
	}
253
1
	.into();
254
1
	let (chain_part, beneficiary) = split_location_into_chain_part_and_beneficiary(dest).unwrap();
255
1
	ParaA::execute_with(|| {
256
1
		let asset = currency_to_asset(parachain::CurrencyId::SelfReserve, 100);
257
1
		// free execution, full amount received
258
1
		assert_ok!(PolkadotXcm::transfer_assets(
259
1
			parachain::RuntimeOrigin::signed(PARAALICE.into()),
260
1
			Box::new(VersionedLocation::from(chain_part)),
261
1
			Box::new(VersionedLocation::from(beneficiary)),
262
1
			Box::new(VersionedAssets::from(vec![asset])),
263
1
			0,
264
1
			WeightLimit::Limited(Weight::from_parts(80u64, DEFAULT_PROOF_SIZE))
265
1
		));
266
		// free execution, full amount received
267
1
		assert_eq!(
268
1
			ParaBalances::free_balance(&PARAALICE.into()),
269
1
			INITIAL_BALANCE - 100
270
1
		);
271
1
	});
272
1

            
273
1
	ParaB::execute_with(|| {
274
1
		// free execution, full amount received
275
1
		assert_eq!(Assets::balance(source_id, &PARAALICE.into()), 100);
276
1
	});
277
1

            
278
1
	// ParaB changes version to 2, and calls on_runtime_upgrade. This should notify the targets
279
1
	// of the new version change
280
1
	ParaB::execute_with(|| {
281
1
		// Set version
282
1
		parachain::XcmVersioner::set_version(2);
283
1
		// Do runtime upgrade
284
1
		parachain::on_runtime_upgrade();
285
1
		// Initialize block, to call on_initialize and notify targets
286
1
		parachain::para_roll_to(2);
287
1
		// Expect the event in the parachain
288
10
		assert!(parachain::para_events().iter().any(|e| matches!(
289
2
			e,
290
			parachain::RuntimeEvent::PolkadotXcm(pallet_xcm::Event::VersionChangeNotified {
291
				result: 2,
292
				..
293
			})
294
10
		)));
295
1
	});
296
1

            
297
1
	// This event should have been seen in para A
298
1
	let expected_supported_version_2: parachain::RuntimeEvent =
299
1
		pallet_xcm::Event::SupportedVersionChanged {
300
1
			location: Location {
301
1
				parents: 1,
302
1
				interior: [Parachain(2)].into(),
303
1
			},
304
1
			version: 2,
305
1
		}
306
1
		.into();
307
1

            
308
1
	// Para A should have received the version change
309
1
	ParaA::execute_with(|| {
310
1
		// Assert that the events vector contains the new version change
311
1
		assert!(parachain::para_events().contains(&expected_supported_version_2));
312
1
	});
313
1
}