1
// Copyright (C) Parity Technologies (UK) Ltd.
2
// This file is part of Parity Bridges Common.
3

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

            
17
#![cfg(test)]
18

            
19
use super::*;
20
use frame_support::assert_ok;
21
use mock::*;
22

            
23
use frame_system::{EventRecord, Phase};
24
use sp_runtime::traits::Dispatchable;
25

            
26
#[test]
27
1
fn not_applicable_if_destination_is_within_other_network() {
28
1
	run_test(|| {
29
1
		// unroutable dest
30
1
		let dest = Location::new(2, [GlobalConsensus(ByGenesis([0; 32])), Parachain(1000)]);
31
1
		let xcm: Xcm<()> = vec![ClearOrigin].into();
32
1

            
33
1
		// check that router does not consume when `NotApplicable`
34
1
		let mut xcm_wrapper = Some(xcm.clone());
35
1
		assert_eq!(
36
1
			XcmBridgeHubRouter::validate(&mut Some(dest.clone()), &mut xcm_wrapper),
37
1
			Err(SendError::NotApplicable),
38
1
		);
39
		// XCM is NOT consumed and untouched
40
1
		assert_eq!(Some(xcm.clone()), xcm_wrapper);
41

            
42
		// check the full `send_xcm`
43
1
		assert_eq!(
44
1
			send_xcm::<XcmBridgeHubRouter>(dest, xcm,),
45
1
			Err(SendError::NotApplicable),
46
1
		);
47
1
	});
48
1
}
49

            
50
#[test]
51
1
fn exceeds_max_message_size_if_size_is_above_hard_limit() {
52
1
	run_test(|| {
53
1
		// routable dest with XCM version
54
1
		let dest = Location::new(
55
1
			2,
56
1
			[GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)],
57
1
		);
58
1
		// oversized XCM
59
1
		let xcm: Xcm<()> = vec![ClearOrigin; HARD_MESSAGE_SIZE_LIMIT as usize].into();
60
1

            
61
1
		// dest is routable with the inner router
62
1
		assert_ok!(<TestRuntime as Config<()>>::MessageExporter::validate(
63
1
			&mut Some(dest.clone()),
64
1
			&mut Some(xcm.clone())
65
1
		));
66

            
67
		// check for oversized message
68
1
		let mut xcm_wrapper = Some(xcm.clone());
69
1
		assert_eq!(
70
1
			XcmBridgeHubRouter::validate(&mut Some(dest.clone()), &mut xcm_wrapper),
71
1
			Err(SendError::ExceedsMaxMessageSize),
72
1
		);
73
		// XCM is consumed by the inner router
74
1
		assert!(xcm_wrapper.is_none());
75

            
76
		// check the full `send_xcm`
77
1
		assert_eq!(
78
1
			send_xcm::<XcmBridgeHubRouter>(dest, xcm,),
79
1
			Err(SendError::ExceedsMaxMessageSize),
80
1
		);
81
1
	});
82
1
}
83

            
84
#[test]
85
1
fn destination_unsupported_if_wrap_version_fails() {
86
1
	run_test(|| {
87
1
		// routable dest but we don't know XCM version
88
1
		let dest = UnknownXcmVersionForRoutableLocation::get();
89
1
		let xcm: Xcm<()> = vec![ClearOrigin].into();
90
1

            
91
1
		// dest is routable with the inner router
92
1
		assert_ok!(<TestRuntime as Config<()>>::MessageExporter::validate(
93
1
			&mut Some(dest.clone()),
94
1
			&mut Some(xcm.clone())
95
1
		));
96

            
97
		// check that it does not pass XCM version check
98
1
		let mut xcm_wrapper = Some(xcm.clone());
99
1
		assert_eq!(
100
1
			XcmBridgeHubRouter::validate(&mut Some(dest.clone()), &mut xcm_wrapper),
101
1
			Err(SendError::DestinationUnsupported),
102
1
		);
103
		// XCM is consumed by the inner router
104
1
		assert!(xcm_wrapper.is_none());
105

            
106
		// check the full `send_xcm`
107
1
		assert_eq!(
108
1
			send_xcm::<XcmBridgeHubRouter>(dest, xcm,),
109
1
			Err(SendError::DestinationUnsupported),
110
1
		);
111
1
	});
112
1
}
113

            
114
#[test]
115
1
fn returns_proper_delivery_price() {
116
1
	run_test(|| {
117
1
		let dest = Location::new(2, [GlobalConsensus(BridgedNetworkId::get())]);
118
1
		let xcm: Xcm<()> = vec![ClearOrigin].into();
119
1
		let msg_size = xcm.encoded_size();
120
1

            
121
1
		// `BASE_FEE + BYTE_FEE * msg_size` (without `HRMP_FEE`)
122
2
		let base_cost_formula = || BASE_FEE + BYTE_FEE * (msg_size as u128);
123

            
124
		// initially the base fee is used
125
1
		let expected_fee = base_cost_formula() + HRMP_FEE;
126
1
		assert_eq!(
127
1
			XcmBridgeHubRouter::validate(&mut Some(dest.clone()), &mut Some(xcm.clone()))
128
1
				.unwrap()
129
1
				.1
130
1
				.get(0),
131
1
			Some(&(BridgeFeeAsset::get(), expected_fee).into()),
132
1
		);
133

            
134
		// but when factor is larger than one, it increases the fee, so it becomes:
135
		// `base_cost_formula() * F`
136
1
		let factor = FixedU128::from_rational(125, 100);
137
1

            
138
1
		// make bridge congested + update fee factor
139
1
		set_bridge_state_for::<TestRuntime, ()>(
140
1
			&dest,
141
1
			Some(BridgeState {
142
1
				delivery_fee_factor: factor,
143
1
				is_congested: true,
144
1
			}),
145
1
		);
146
1

            
147
1
		let expected_fee = (FixedU128::saturating_from_integer(base_cost_formula()) * factor)
148
1
			.into_inner()
149
1
			/ FixedU128::DIV
150
1
			+ HRMP_FEE;
151
1
		assert_eq!(
152
1
			XcmBridgeHubRouter::validate(&mut Some(dest), &mut Some(xcm))
153
1
				.unwrap()
154
1
				.1
155
1
				.get(0),
156
1
			Some(&(BridgeFeeAsset::get(), expected_fee).into()),
157
1
		);
158
1
	});
159
1
}
160

            
161
#[test]
162
1
fn sent_message_doesnt_increase_factor_if_bridge_is_uncongested() {
163
1
	run_test(|| {
164
1
		let dest = Location::new(
165
1
			2,
166
1
			[GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)],
167
1
		);
168
1

            
169
1
		// bridge not congested
170
1
		let old_delivery_fee_factor = FixedU128::from_rational(125, 100);
171
1
		set_bridge_state_for::<TestRuntime, ()>(
172
1
			&dest,
173
1
			Some(BridgeState {
174
1
				delivery_fee_factor: old_delivery_fee_factor,
175
1
				is_congested: false,
176
1
			}),
177
1
		);
178
1

            
179
1
		assert_eq!(
180
1
			send_xcm::<XcmBridgeHubRouter>(dest.clone(), vec![ClearOrigin].into(),).map(drop),
181
1
			Ok(()),
182
1
		);
183

            
184
1
		assert!(TestXcmRouter::is_message_sent());
185
1
		assert_eq!(
186
1
			old_delivery_fee_factor,
187
1
			get_bridge_state_for::<TestRuntime, ()>(&dest)
188
1
				.unwrap()
189
1
				.delivery_fee_factor
190
1
		);
191

            
192
1
		assert_eq!(System::events(), vec![]);
193
1
	});
194
1
}
195

            
196
#[test]
197
1
fn sent_message_increases_factor_if_bridge_is_congested() {
198
1
	run_test(|| {
199
1
		let dest = Location::new(
200
1
			2,
201
1
			[GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)],
202
1
		);
203
1

            
204
1
		// make bridge congested + update fee factor
205
1
		let old_delivery_fee_factor = FixedU128::from_rational(125, 100);
206
1
		set_bridge_state_for::<TestRuntime, ()>(
207
1
			&dest,
208
1
			Some(BridgeState {
209
1
				delivery_fee_factor: old_delivery_fee_factor,
210
1
				is_congested: true,
211
1
			}),
212
1
		);
213
1

            
214
1
		assert_ok!(
215
1
			send_xcm::<XcmBridgeHubRouter>(dest.clone(), vec![ClearOrigin].into(),).map(drop)
216
1
		);
217

            
218
1
		assert!(TestXcmRouter::is_message_sent());
219
1
		assert!(
220
1
			old_delivery_fee_factor
221
1
				< get_bridge_state_for::<TestRuntime, ()>(&dest)
222
1
					.unwrap()
223
1
					.delivery_fee_factor
224
1
		);
225

            
226
		// check emitted event
227
1
		let first_system_event = System::events().first().cloned();
228
1
		assert!(matches!(
229
1
			first_system_event,
230
			Some(EventRecord {
231
				phase: Phase::Initialization,
232
				event: RuntimeEvent::XcmBridgeHubRouter(Event::DeliveryFeeFactorIncreased { .. }),
233
				..
234
			})
235
		));
236
1
	});
237
1
}
238

            
239
#[test]
240
1
fn get_messages_does_not_return_anything() {
241
1
	run_test(|| {
242
1
		assert_ok!(send_xcm::<XcmBridgeHubRouter>(
243
1
			(
244
1
				Parent,
245
1
				Parent,
246
1
				GlobalConsensus(BridgedNetworkId::get()),
247
1
				Parachain(1000)
248
1
			)
249
1
				.into(),
250
1
			vec![ClearOrigin].into()
251
1
		));
252
1
		assert_eq!(XcmBridgeHubRouter::get_messages(), vec![]);
253
1
	});
254
1
}
255

            
256
#[test]
257
1
fn update_bridge_status_works() {
258
1
	run_test(|| {
259
1
		let dest = Location::new(
260
1
			2,
261
1
			[GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)],
262
1
		);
263
1
		let bridge_id = bp_xcm_bridge::BridgeId::new(&UniversalLocation::get(), dest.interior());
264
3
		let update_bridge_status = |bridge_id, is_congested| {
265
3
			let call = RuntimeCall::XcmBridgeHubRouter(Call::update_bridge_status {
266
3
				bridge_id,
267
3
				is_congested,
268
3
			});
269
3
			assert_ok!(call.dispatch(RuntimeOrigin::root()));
270
3
		};
271

            
272
1
		assert!(get_bridge_state_for::<TestRuntime, ()>(&dest).is_none());
273
1
		update_bridge_status(bridge_id, false);
274
1
		assert!(get_bridge_state_for::<TestRuntime, ()>(&dest).is_none());
275

            
276
		// make congested
277
1
		update_bridge_status(bridge_id, true);
278
1
		assert_eq!(
279
1
			get_bridge_state_for::<TestRuntime, ()>(&dest),
280
1
			Some(BridgeState {
281
1
				delivery_fee_factor: MINIMAL_DELIVERY_FEE_FACTOR,
282
1
				is_congested: true,
283
1
			})
284
1
		);
285

            
286
		// make uncongested
287
1
		update_bridge_status(bridge_id, false);
288
1
		assert_eq!(get_bridge_state_for::<TestRuntime, ()>(&dest), None);
289
1
	});
290
1
}
291

            
292
#[test]
293
1
fn do_update_bridge_status_works() {
294
1
	run_test(|| {
295
1
		let dest = Location::new(2, [GlobalConsensus(BridgedNetworkId::get())]);
296
1
		let bridge_id = bp_xcm_bridge::BridgeId::new(&UniversalLocation::get(), dest.interior());
297
1
		assert!(get_bridge_state_for::<TestRuntime, ()>(&dest).is_none());
298

            
299
		// update as is_congested=false when `None`
300
1
		Pallet::<TestRuntime, ()>::do_update_bridge_status(bridge_id, false);
301
1
		assert!(get_bridge_state_for::<TestRuntime, ()>(&dest).is_none());
302

            
303
		// update as is_congested=true
304
1
		Pallet::<TestRuntime, ()>::do_update_bridge_status(bridge_id, true);
305
1
		assert_eq!(
306
1
			get_bridge_state_for::<TestRuntime, ()>(&dest),
307
1
			Some(BridgeState {
308
1
				delivery_fee_factor: MINIMAL_DELIVERY_FEE_FACTOR,
309
1
				is_congested: true,
310
1
			})
311
1
		);
312

            
313
		// increase fee factor when congested
314
1
		Pallet::<TestRuntime, ()>::on_message_sent_to(5, dest.clone());
315
1
		assert!(
316
1
			get_bridge_state_for::<TestRuntime, ()>(&dest)
317
1
				.unwrap()
318
1
				.delivery_fee_factor
319
1
				> MINIMAL_DELIVERY_FEE_FACTOR
320
1
		);
321
		// update as is_congested=true - should not reset fee factor
322
1
		Pallet::<TestRuntime, ()>::do_update_bridge_status(bridge_id, true);
323
1
		assert!(
324
1
			get_bridge_state_for::<TestRuntime, ()>(&dest)
325
1
				.unwrap()
326
1
				.delivery_fee_factor
327
1
				> MINIMAL_DELIVERY_FEE_FACTOR
328
1
		);
329

            
330
		// update as is_congested=false when `Some(..)`
331
1
		Pallet::<TestRuntime, ()>::do_update_bridge_status(bridge_id, false);
332
1
		assert_eq!(get_bridge_state_for::<TestRuntime, ()>(&dest), None,);
333
1
	})
334
1
}