1
// Copyright 2019-2021 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 bp_messages::LaneIdType;
21
use mock::*;
22

            
23
use frame_support::{assert_err, assert_noop, assert_ok, BoundedVec};
24
use frame_system::{EventRecord, Phase};
25
use sp_runtime::{traits::Zero, TryRuntimeError};
26

            
27
3
fn mock_open_bridge_from_with(
28
3
	origin: RuntimeOrigin,
29
3
	deposit: Option<Balance>,
30
3
	with: InteriorLocation,
31
3
) -> (BridgeOf<TestRuntime, ()>, BridgeLocations) {
32
3
	let locations =
33
3
		XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap();
34
3
	let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
35
3

            
36
3
	let deposit = deposit.map(|deposit| {
37
1
		let bridge_owner_account =
38
1
			fund_origin_sovereign_account(&locations, deposit + ExistentialDeposit::get());
39
1
		Balances::hold(
40
1
			&HoldReason::BridgeDeposit.into(),
41
1
			&bridge_owner_account,
42
1
			deposit,
43
1
		)
44
1
		.unwrap();
45
1
		Deposit::new(bridge_owner_account, deposit)
46
3
	});
47
3

            
48
3
	let bridge = Bridge {
49
3
		bridge_origin_relative_location: Box::new(
50
3
			locations.bridge_origin_relative_location().clone().into(),
51
3
		),
52
3
		bridge_origin_universal_location: Box::new(
53
3
			locations.bridge_origin_universal_location().clone().into(),
54
3
		),
55
3
		bridge_destination_universal_location: Box::new(
56
3
			locations
57
3
				.bridge_destination_universal_location()
58
3
				.clone()
59
3
				.into(),
60
3
		),
61
3
		state: BridgeState::Opened,
62
3
		deposit,
63
3
		lane_id,
64
3
		maybe_notify: None,
65
3
	};
66
3
	Bridges::<TestRuntime, ()>::insert(locations.bridge_id(), bridge.clone());
67
3
	LaneToBridge::<TestRuntime, ()>::insert(bridge.lane_id, locations.bridge_id());
68
3

            
69
3
	let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
70
3
	lanes_manager.create_inbound_lane(bridge.lane_id).unwrap();
71
3
	lanes_manager.create_outbound_lane(bridge.lane_id).unwrap();
72
3

            
73
3
	assert_ok!(XcmOverBridge::do_try_state());
74

            
75
3
	(bridge, *locations)
76
3
}
77

            
78
3
fn mock_open_bridge_from(
79
3
	origin: RuntimeOrigin,
80
3
	deposit: Option<Balance>,
81
3
) -> (BridgeOf<TestRuntime, ()>, BridgeLocations) {
82
3
	mock_open_bridge_from_with(origin, deposit, bridged_asset_hub_universal_location())
83
3
}
84

            
85
32
fn enqueue_message(lane: TestLaneIdType) {
86
32
	let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
87
32
	lanes_manager
88
32
		.active_outbound_lane(lane)
89
32
		.unwrap()
90
32
		.send_message(BoundedVec::try_from(vec![42]).expect("We craft valid messages"));
91
32
}
92

            
93
#[test]
94
1
fn open_bridge_fails_if_origin_is_not_allowed() {
95
1
	run_test(|| {
96
1
		assert_noop!(
97
1
			XcmOverBridge::open_bridge(
98
1
				OpenBridgeOrigin::disallowed_origin(),
99
1
				Box::new(bridged_asset_hub_universal_location().into()),
100
1
				None,
101
1
			),
102
1
			sp_runtime::DispatchError::BadOrigin,
103
1
		);
104
1
	})
105
1
}
106

            
107
#[test]
108
1
fn open_bridge_fails_if_origin_is_not_relative() {
109
1
	run_test(|| {
110
1
		assert_noop!(
111
1
			XcmOverBridge::open_bridge(
112
1
				OpenBridgeOrigin::parent_relay_chain_universal_origin(),
113
1
				Box::new(bridged_asset_hub_universal_location().into()),
114
1
				None,
115
1
			),
116
1
			Error::<TestRuntime, ()>::BridgeLocations(BridgeLocationsError::InvalidBridgeOrigin),
117
1
		);
118

            
119
1
		assert_noop!(
120
1
			XcmOverBridge::open_bridge(
121
1
				OpenBridgeOrigin::sibling_parachain_universal_origin(),
122
1
				Box::new(bridged_asset_hub_universal_location().into()),
123
1
				None,
124
1
			),
125
1
			Error::<TestRuntime, ()>::BridgeLocations(BridgeLocationsError::InvalidBridgeOrigin),
126
1
		);
127
1
	})
128
1
}
129

            
130
#[test]
131
1
fn open_bridge_fails_if_destination_is_not_remote() {
132
1
	run_test(|| {
133
1
		assert_noop!(
134
1
			XcmOverBridge::open_bridge(
135
1
				OpenBridgeOrigin::parent_relay_chain_origin(),
136
1
				Box::new(
137
1
					[
138
1
						GlobalConsensus(RelayNetwork::get()),
139
1
						Parachain(BRIDGED_ASSET_HUB_ID)
140
1
					]
141
1
					.into()
142
1
				),
143
1
				None,
144
1
			),
145
1
			Error::<TestRuntime, ()>::BridgeLocations(BridgeLocationsError::DestinationIsLocal),
146
1
		);
147
1
	});
148
1
}
149

            
150
#[test]
151
1
fn open_bridge_fails_if_outside_of_bridged_consensus() {
152
1
	run_test(|| {
153
1
		assert_noop!(
154
1
			XcmOverBridge::open_bridge(
155
1
				OpenBridgeOrigin::parent_relay_chain_origin(),
156
1
				Box::new(
157
1
					[
158
1
						GlobalConsensus(NonBridgedRelayNetwork::get()),
159
1
						Parachain(BRIDGED_ASSET_HUB_ID)
160
1
					]
161
1
					.into()
162
1
				),
163
1
				None,
164
1
			),
165
1
			Error::<TestRuntime, ()>::BridgeLocations(BridgeLocationsError::UnreachableDestination),
166
1
		);
167
1
	});
168
1
}
169

            
170
#[test]
171
1
fn open_bridge_fails_if_origin_has_no_sovereign_account() {
172
1
	run_test(|| {
173
1
		assert_noop!(
174
1
			XcmOverBridge::open_bridge(
175
1
				OpenBridgeOrigin::origin_without_sovereign_account(),
176
1
				Box::new(bridged_asset_hub_universal_location().into()),
177
1
				None,
178
1
			),
179
1
			Error::<TestRuntime, ()>::InvalidBridgeOriginAccount,
180
1
		);
181
1
	});
182
1
}
183

            
184
#[test]
185
1
fn open_bridge_fails_if_origin_sovereign_account_has_no_enough_funds() {
186
1
	run_test(|| {
187
1
		assert_noop!(
188
1
			XcmOverBridge::open_bridge(
189
1
				OpenBridgeOrigin::sibling_parachain_origin(),
190
1
				Box::new(bridged_asset_hub_universal_location().into()),
191
1
				None,
192
1
			),
193
1
			Error::<TestRuntime, ()>::FailedToReserveBridgeDeposit,
194
1
		);
195
1
	});
196
1
}
197

            
198
#[test]
199
1
fn open_bridge_fails_if_it_already_exists() {
200
1
	run_test(|| {
201
1
		let origin = OpenBridgeOrigin::parent_relay_chain_origin();
202
1
		let locations = XcmOverBridge::bridge_locations_from_origin(
203
1
			origin.clone(),
204
1
			Box::new(bridged_asset_hub_universal_location().into()),
205
1
		)
206
1
		.unwrap();
207
1
		let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
208
1

            
209
1
		Bridges::<TestRuntime, ()>::insert(
210
1
			locations.bridge_id(),
211
1
			Bridge {
212
1
				bridge_origin_relative_location: Box::new(
213
1
					locations.bridge_origin_relative_location().clone().into(),
214
1
				),
215
1
				bridge_origin_universal_location: Box::new(
216
1
					locations.bridge_origin_universal_location().clone().into(),
217
1
				),
218
1
				bridge_destination_universal_location: Box::new(
219
1
					locations
220
1
						.bridge_destination_universal_location()
221
1
						.clone()
222
1
						.into(),
223
1
				),
224
1
				state: BridgeState::Opened,
225
1
				deposit: None,
226
1
				lane_id,
227
1
				maybe_notify: None,
228
1
			},
229
1
		);
230
1

            
231
1
		assert_noop!(
232
1
			XcmOverBridge::open_bridge(
233
1
				origin,
234
1
				Box::new(bridged_asset_hub_universal_location().into()),
235
1
				None,
236
1
			),
237
1
			Error::<TestRuntime, ()>::BridgeAlreadyExists,
238
1
		);
239
1
	})
240
1
}
241

            
242
#[test]
243
1
fn open_bridge_fails_if_its_lanes_already_exists() {
244
1
	run_test(|| {
245
1
		let origin = OpenBridgeOrigin::parent_relay_chain_origin();
246
1
		let locations = XcmOverBridge::bridge_locations_from_origin(
247
1
			origin.clone(),
248
1
			Box::new(bridged_asset_hub_universal_location().into()),
249
1
		)
250
1
		.unwrap();
251
1
		let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
252
1
		fund_origin_sovereign_account(&locations, BridgeDeposit::get() + ExistentialDeposit::get());
253
1

            
254
1
		let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
255
1

            
256
1
		lanes_manager.create_inbound_lane(lane_id).unwrap();
257
1
		assert_noop!(
258
1
			XcmOverBridge::open_bridge(
259
1
				origin.clone(),
260
1
				Box::new(bridged_asset_hub_universal_location().into()),
261
1
				None,
262
1
			),
263
1
			Error::<TestRuntime, ()>::LanesManager(LanesManagerError::InboundLaneAlreadyExists),
264
1
		);
265

            
266
1
		lanes_manager.active_inbound_lane(lane_id).unwrap().purge();
267
1
		lanes_manager.create_outbound_lane(lane_id).unwrap();
268
1
		assert_noop!(
269
1
			XcmOverBridge::open_bridge(
270
1
				origin,
271
1
				Box::new(bridged_asset_hub_universal_location().into()),
272
1
				None,
273
1
			),
274
1
			Error::<TestRuntime, ()>::LanesManager(LanesManagerError::OutboundLaneAlreadyExists),
275
1
		);
276
1
	})
277
1
}
278

            
279
#[test]
280
1
fn open_bridge_works() {
281
1
	run_test(|| {
282
1
		// in our test runtime, we expect that bridge may be opened by parent relay chain,
283
1
		// any sibling parachain or local root
284
1
		let origins = [
285
1
			(OpenBridgeOrigin::parent_relay_chain_origin(), None),
286
1
			(
287
1
				OpenBridgeOrigin::sibling_parachain_origin(),
288
1
				Some(BridgeDeposit::get()),
289
1
			),
290
1
			(RuntimeOrigin::root(), None),
291
1
		];
292
1

            
293
1
		// check that every origin may open the bridge
294
1
		let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
295
1
		let existential_deposit = ExistentialDeposit::get();
296
4
		for (origin, expected_deposit_amount) in origins {
297
			// reset events
298
3
			System::set_block_number(1);
299
3
			System::reset_events();
300
3

            
301
3
			// compute all other locations
302
3
			let xcm_version = xcm::latest::VERSION;
303
3
			let locations = XcmOverBridge::bridge_locations_from_origin(
304
3
				origin.clone(),
305
3
				Box::new(
306
3
					VersionedInteriorLocation::from(bridged_asset_hub_universal_location())
307
3
						.into_version(xcm_version)
308
3
						.expect("valid conversion"),
309
3
				),
310
3
			)
311
3
			.unwrap();
312
3
			let lane_id = locations.calculate_lane_id(xcm_version).unwrap();
313
3

            
314
3
			// ensure that there's no bridge and lanes in the storage
315
3
			assert_eq!(Bridges::<TestRuntime, ()>::get(locations.bridge_id()), None);
316
3
			assert_eq!(
317
3
				lanes_manager.active_inbound_lane(lane_id).map(drop),
318
3
				Err(LanesManagerError::UnknownInboundLane)
319
3
			);
320
3
			assert_eq!(
321
3
				lanes_manager.active_outbound_lane(lane_id).map(drop),
322
3
				Err(LanesManagerError::UnknownOutboundLane)
323
3
			);
324
3
			assert_eq!(LaneToBridge::<TestRuntime, ()>::get(lane_id), None);
325

            
326
			// give enough funds to the sovereign account of the bridge origin
327
3
			let expected_deposit = expected_deposit_amount.map(|deposit_amount| {
328
1
				let bridge_owner_account =
329
1
					fund_origin_sovereign_account(&locations, deposit_amount + existential_deposit);
330
1
				assert_eq!(
331
1
					Balances::free_balance(&bridge_owner_account),
332
1
					deposit_amount + existential_deposit
333
1
				);
334
1
				assert_eq!(Balances::reserved_balance(&bridge_owner_account), 0);
335
1
				Deposit::new(bridge_owner_account, deposit_amount)
336
3
			});
337
3

            
338
3
			let maybe_notify = Some(Receiver::new(13, 15));
339
3

            
340
3
			// now open the bridge
341
3
			assert_ok!(XcmOverBridge::open_bridge(
342
3
				origin,
343
3
				Box::new(
344
3
					locations
345
3
						.bridge_destination_universal_location()
346
3
						.clone()
347
3
						.into()
348
3
				),
349
3
				maybe_notify.clone(),
350
3
			));
351

            
352
			// ensure that everything has been set up in the runtime storage
353
3
			assert_eq!(
354
3
				Bridges::<TestRuntime, ()>::get(locations.bridge_id()),
355
3
				Some(Bridge {
356
3
					bridge_origin_relative_location: Box::new(
357
3
						locations.bridge_origin_relative_location().clone().into()
358
3
					),
359
3
					bridge_origin_universal_location: Box::new(
360
3
						locations.bridge_origin_universal_location().clone().into(),
361
3
					),
362
3
					bridge_destination_universal_location: Box::new(
363
3
						locations
364
3
							.bridge_destination_universal_location()
365
3
							.clone()
366
3
							.into(),
367
3
					),
368
3
					state: BridgeState::Opened,
369
3
					deposit: expected_deposit.clone(),
370
3
					lane_id,
371
3
					maybe_notify,
372
3
				}),
373
3
			);
374
3
			assert_eq!(
375
3
				lanes_manager
376
3
					.active_inbound_lane(lane_id)
377
3
					.map(|l| l.state()),
378
3
				Ok(LaneState::Opened)
379
3
			);
380
3
			assert_eq!(
381
3
				lanes_manager
382
3
					.active_outbound_lane(lane_id)
383
3
					.map(|l| l.state()),
384
3
				Ok(LaneState::Opened)
385
3
			);
386
3
			assert_eq!(
387
3
				LaneToBridge::<TestRuntime, ()>::get(lane_id),
388
3
				Some(*locations.bridge_id())
389
3
			);
390
3
			if let Some(expected_deposit) = expected_deposit.as_ref() {
391
1
				assert_eq!(
392
1
					Balances::free_balance(&expected_deposit.account),
393
1
					existential_deposit
394
1
				);
395
1
				assert_eq!(
396
1
					Balances::reserved_balance(&expected_deposit.account),
397
1
					expected_deposit.amount
398
1
				);
399
2
			}
400

            
401
			// ensure that the proper event is deposited
402
3
			assert_eq!(
403
3
				System::events().last(),
404
3
				Some(&EventRecord {
405
3
					phase: Phase::Initialization,
406
3
					event: RuntimeEvent::XcmOverBridge(Event::BridgeOpened {
407
3
						bridge_id: *locations.bridge_id(),
408
3
						bridge_deposit: expected_deposit,
409
3
						local_endpoint: Box::new(
410
3
							locations.bridge_origin_universal_location().clone()
411
3
						),
412
3
						remote_endpoint: Box::new(
413
3
							locations.bridge_destination_universal_location().clone()
414
3
						),
415
3
						lane_id: lane_id.into()
416
3
					}),
417
3
					topics: vec![],
418
3
				}),
419
3
			);
420

            
421
			// check state
422
3
			assert_ok!(XcmOverBridge::do_try_state());
423
		}
424
1
	});
425
1
}
426

            
427
#[test]
428
1
fn close_bridge_fails_if_origin_is_not_allowed() {
429
1
	run_test(|| {
430
1
		assert_noop!(
431
1
			XcmOverBridge::close_bridge(
432
1
				OpenBridgeOrigin::disallowed_origin(),
433
1
				Box::new(bridged_asset_hub_universal_location().into()),
434
1
				0,
435
1
			),
436
1
			sp_runtime::DispatchError::BadOrigin,
437
1
		);
438
1
	})
439
1
}
440

            
441
#[test]
442
1
fn close_bridge_fails_if_origin_is_not_relative() {
443
1
	run_test(|| {
444
1
		assert_noop!(
445
1
			XcmOverBridge::close_bridge(
446
1
				OpenBridgeOrigin::parent_relay_chain_universal_origin(),
447
1
				Box::new(bridged_asset_hub_universal_location().into()),
448
1
				0,
449
1
			),
450
1
			Error::<TestRuntime, ()>::BridgeLocations(BridgeLocationsError::InvalidBridgeOrigin),
451
1
		);
452

            
453
1
		assert_noop!(
454
1
			XcmOverBridge::close_bridge(
455
1
				OpenBridgeOrigin::sibling_parachain_universal_origin(),
456
1
				Box::new(bridged_asset_hub_universal_location().into()),
457
1
				0,
458
1
			),
459
1
			Error::<TestRuntime, ()>::BridgeLocations(BridgeLocationsError::InvalidBridgeOrigin),
460
1
		);
461
1
	})
462
1
}
463

            
464
#[test]
465
1
fn close_bridge_fails_if_its_lanes_are_unknown() {
466
1
	run_test(|| {
467
1
		let origin = OpenBridgeOrigin::parent_relay_chain_origin();
468
1
		let (bridge, locations) = mock_open_bridge_from(origin.clone(), None);
469
1

            
470
1
		let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
471
1
		lanes_manager
472
1
			.any_state_inbound_lane(bridge.lane_id)
473
1
			.unwrap()
474
1
			.purge();
475
1
		assert_noop!(
476
1
			XcmOverBridge::close_bridge(
477
1
				origin.clone(),
478
1
				Box::new(
479
1
					locations
480
1
						.bridge_destination_universal_location()
481
1
						.clone()
482
1
						.into()
483
1
				),
484
1
				0,
485
1
			),
486
1
			Error::<TestRuntime, ()>::LanesManager(LanesManagerError::UnknownInboundLane),
487
1
		);
488
1
		lanes_manager
489
1
			.any_state_outbound_lane(bridge.lane_id)
490
1
			.unwrap()
491
1
			.purge();
492
1

            
493
1
		let (_, locations) = mock_open_bridge_from(origin.clone(), None);
494
1
		lanes_manager
495
1
			.any_state_outbound_lane(bridge.lane_id)
496
1
			.unwrap()
497
1
			.purge();
498
1
		assert_noop!(
499
1
			XcmOverBridge::close_bridge(
500
1
				origin,
501
1
				Box::new(
502
1
					locations
503
1
						.bridge_destination_universal_location()
504
1
						.clone()
505
1
						.into()
506
1
				),
507
1
				0,
508
1
			),
509
1
			Error::<TestRuntime, ()>::LanesManager(LanesManagerError::UnknownOutboundLane),
510
1
		);
511
1
	});
512
1
}
513

            
514
#[test]
515
1
fn close_bridge_works() {
516
1
	run_test(|| {
517
1
		let origin = OpenBridgeOrigin::parent_relay_chain_origin();
518
1
		let expected_deposit = BridgeDeposit::get();
519
1
		let (bridge, locations) = mock_open_bridge_from(origin.clone(), Some(expected_deposit));
520
1
		System::set_block_number(1);
521
1
		let bridge_owner_account = bridge.deposit.unwrap().account;
522
1

            
523
1
		// remember owner balances
524
1
		let free_balance = Balances::free_balance(&bridge_owner_account);
525
1
		let reserved_balance = Balances::reserved_balance(&bridge_owner_account);
526

            
527
		// enqueue some messages
528
33
		for _ in 0..32 {
529
32
			enqueue_message(bridge.lane_id);
530
32
		}
531

            
532
		// now call the `close_bridge`, which will only partially prune messages
533
1
		assert_ok!(XcmOverBridge::close_bridge(
534
1
			origin.clone(),
535
1
			Box::new(
536
1
				locations
537
1
					.bridge_destination_universal_location()
538
1
					.clone()
539
1
					.into()
540
1
			),
541
1
			16,
542
1
		),);
543

            
544
		// as a result, the bridge and lanes are switched to the `Closed` state, some messages
545
		// are pruned, but funds are not unreserved
546
1
		let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
547
1
		assert_eq!(
548
1
			Bridges::<TestRuntime, ()>::get(locations.bridge_id()).map(|b| b.state),
549
1
			Some(BridgeState::Closed)
550
1
		);
551
1
		assert_eq!(
552
1
			lanes_manager
553
1
				.any_state_inbound_lane(bridge.lane_id)
554
1
				.unwrap()
555
1
				.state(),
556
1
			LaneState::Closed
557
1
		);
558
1
		assert_eq!(
559
1
			lanes_manager
560
1
				.any_state_outbound_lane(bridge.lane_id)
561
1
				.unwrap()
562
1
				.state(),
563
1
			LaneState::Closed
564
1
		);
565
1
		assert_eq!(
566
1
			lanes_manager
567
1
				.any_state_outbound_lane(bridge.lane_id)
568
1
				.unwrap()
569
1
				.queued_messages()
570
1
				.checked_len(),
571
1
			Some(16)
572
1
		);
573
1
		assert_eq!(
574
1
			LaneToBridge::<TestRuntime, ()>::get(bridge.lane_id),
575
1
			Some(*locations.bridge_id())
576
1
		);
577
1
		assert_eq!(Balances::free_balance(&bridge_owner_account), free_balance);
578
1
		assert_eq!(
579
1
			Balances::reserved_balance(&bridge_owner_account),
580
1
			reserved_balance
581
1
		);
582
1
		assert_eq!(
583
1
			System::events().last(),
584
1
			Some(&EventRecord {
585
1
				phase: Phase::Initialization,
586
1
				event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge {
587
1
					bridge_id: *locations.bridge_id(),
588
1
					lane_id: bridge.lane_id.into(),
589
1
					pruned_messages: 16,
590
1
					enqueued_messages: 16,
591
1
				}),
592
1
				topics: vec![],
593
1
			}),
594
1
		);
595

            
596
		// now call the `close_bridge` again, which will only partially prune messages
597
1
		assert_ok!(XcmOverBridge::close_bridge(
598
1
			origin.clone(),
599
1
			Box::new(
600
1
				locations
601
1
					.bridge_destination_universal_location()
602
1
					.clone()
603
1
					.into()
604
1
			),
605
1
			8,
606
1
		),);
607

            
608
		// nothing is changed (apart from the pruned messages)
609
1
		assert_eq!(
610
1
			Bridges::<TestRuntime, ()>::get(locations.bridge_id()).map(|b| b.state),
611
1
			Some(BridgeState::Closed)
612
1
		);
613
1
		assert_eq!(
614
1
			lanes_manager
615
1
				.any_state_inbound_lane(bridge.lane_id)
616
1
				.unwrap()
617
1
				.state(),
618
1
			LaneState::Closed
619
1
		);
620
1
		assert_eq!(
621
1
			lanes_manager
622
1
				.any_state_outbound_lane(bridge.lane_id)
623
1
				.unwrap()
624
1
				.state(),
625
1
			LaneState::Closed
626
1
		);
627
1
		assert_eq!(
628
1
			lanes_manager
629
1
				.any_state_outbound_lane(bridge.lane_id)
630
1
				.unwrap()
631
1
				.queued_messages()
632
1
				.checked_len(),
633
1
			Some(8)
634
1
		);
635
1
		assert_eq!(
636
1
			LaneToBridge::<TestRuntime, ()>::get(bridge.lane_id),
637
1
			Some(*locations.bridge_id())
638
1
		);
639
1
		assert_eq!(Balances::free_balance(&bridge_owner_account), free_balance);
640
1
		assert_eq!(
641
1
			Balances::reserved_balance(&bridge_owner_account),
642
1
			reserved_balance
643
1
		);
644
1
		assert_eq!(
645
1
			System::events().last(),
646
1
			Some(&EventRecord {
647
1
				phase: Phase::Initialization,
648
1
				event: RuntimeEvent::XcmOverBridge(Event::ClosingBridge {
649
1
					bridge_id: *locations.bridge_id(),
650
1
					lane_id: bridge.lane_id.into(),
651
1
					pruned_messages: 8,
652
1
					enqueued_messages: 8,
653
1
				}),
654
1
				topics: vec![],
655
1
			}),
656
1
		);
657

            
658
		// now call the `close_bridge` again that will prune all remaining messages and the
659
		// bridge
660
1
		assert_ok!(XcmOverBridge::close_bridge(
661
1
			origin,
662
1
			Box::new(
663
1
				locations
664
1
					.bridge_destination_universal_location()
665
1
					.clone()
666
1
					.into()
667
1
			),
668
1
			9,
669
1
		),);
670

            
671
		// there's no traces of bridge in the runtime storage and funds are unreserved
672
1
		assert_eq!(
673
1
			Bridges::<TestRuntime, ()>::get(locations.bridge_id()).map(|b| b.state),
674
1
			None
675
1
		);
676
1
		assert_eq!(
677
1
			lanes_manager
678
1
				.any_state_inbound_lane(bridge.lane_id)
679
1
				.map(drop),
680
1
			Err(LanesManagerError::UnknownInboundLane)
681
1
		);
682
1
		assert_eq!(
683
1
			lanes_manager
684
1
				.any_state_outbound_lane(bridge.lane_id)
685
1
				.map(drop),
686
1
			Err(LanesManagerError::UnknownOutboundLane)
687
1
		);
688
1
		assert_eq!(LaneToBridge::<TestRuntime, ()>::get(bridge.lane_id), None);
689
1
		assert_eq!(
690
1
			Balances::free_balance(&bridge_owner_account),
691
1
			free_balance + reserved_balance
692
1
		);
693
1
		assert_eq!(Balances::reserved_balance(&bridge_owner_account), 0);
694
1
		assert_eq!(
695
1
			System::events().last(),
696
1
			Some(&EventRecord {
697
1
				phase: Phase::Initialization,
698
1
				event: RuntimeEvent::XcmOverBridge(Event::BridgePruned {
699
1
					bridge_id: *locations.bridge_id(),
700
1
					lane_id: bridge.lane_id.into(),
701
1
					bridge_deposit: Some(Deposit::new(bridge_owner_account, expected_deposit)),
702
1
					pruned_messages: 8,
703
1
				}),
704
1
				topics: vec![],
705
1
			}),
706
1
		);
707
1
	});
708
1
}
709

            
710
#[test]
711
1
fn update_notification_receiver_works() {
712
1
	run_test(|| {
713
1
		let origin = OpenBridgeOrigin::parent_relay_chain_origin();
714
1
		let locations = XcmOverBridge::bridge_locations_from_origin(
715
1
			origin.clone(),
716
1
			Box::new(VersionedInteriorLocation::from(
717
1
				bridged_asset_hub_universal_location(),
718
1
			)),
719
1
		)
720
1
		.unwrap();
721
1

            
722
1
		// open the bridge
723
1
		assert_ok!(XcmOverBridge::open_bridge(
724
1
			origin.clone(),
725
1
			Box::new(
726
1
				locations
727
1
					.bridge_destination_universal_location()
728
1
					.clone()
729
1
					.into()
730
1
			),
731
1
			Some(Receiver::new(13, 15)),
732
1
		));
733
1
		assert_eq!(
734
1
			Bridges::<TestRuntime, ()>::get(locations.bridge_id())
735
1
				.map(|b| b.maybe_notify)
736
1
				.unwrap(),
737
1
			Some(Receiver::new(13, 15))
738
1
		);
739

            
740
		// update the notification receiver to `None`
741
1
		assert_ok!(XcmOverBridge::update_notification_receiver(
742
1
			origin.clone(),
743
1
			Box::new(
744
1
				locations
745
1
					.bridge_destination_universal_location()
746
1
					.clone()
747
1
					.into()
748
1
			),
749
1
			None,
750
1
		));
751
1
		assert_eq!(
752
1
			Bridges::<TestRuntime, ()>::get(locations.bridge_id())
753
1
				.map(|b| b.maybe_notify)
754
1
				.unwrap(),
755
1
			None,
756
1
		);
757

            
758
		// update the notification receiver to `Some(..)`
759
1
		assert_ok!(XcmOverBridge::update_notification_receiver(
760
1
			origin.clone(),
761
1
			Box::new(
762
1
				locations
763
1
					.bridge_destination_universal_location()
764
1
					.clone()
765
1
					.into()
766
1
			),
767
1
			Some(Receiver::new(29, 43)),
768
1
		));
769
1
		assert_eq!(
770
1
			Bridges::<TestRuntime, ()>::get(locations.bridge_id())
771
1
				.map(|b| b.maybe_notify)
772
1
				.unwrap(),
773
1
			Some(Receiver::new(29, 43))
774
1
		);
775
		// update the notification receiver to `Some(..)`
776
1
		assert_ok!(XcmOverBridge::update_notification_receiver(
777
1
			origin.clone(),
778
1
			Box::new(
779
1
				locations
780
1
					.bridge_destination_universal_location()
781
1
					.clone()
782
1
					.into()
783
1
			),
784
1
			Some(Receiver::new(29, 79)),
785
1
		));
786
1
		assert_eq!(
787
1
			Bridges::<TestRuntime, ()>::get(locations.bridge_id())
788
1
				.map(|b| b.maybe_notify)
789
1
				.unwrap(),
790
1
			Some(Receiver::new(29, 79))
791
1
		);
792
1
	});
793
1
}
794

            
795
#[test]
796
1
fn do_try_state_works() {
797
1
	let bridge_origin_relative_location = SiblingLocation::get();
798
1
	let bridge_origin_universal_location = SiblingUniversalLocation::get();
799
1
	let bridge_destination_universal_location = BridgedUniversalDestination::get();
800
1
	let bridge_owner_account =
801
1
		LocationToAccountId::convert_location(&bridge_origin_relative_location)
802
1
			.expect("valid accountId");
803
1
	let bridge_owner_account_mismatch =
804
1
		LocationToAccountId::convert_location(&Location::parent()).expect("valid accountId");
805
1
	let bridge_id = BridgeId::new(
806
1
		&bridge_origin_universal_location,
807
1
		&bridge_destination_universal_location,
808
1
	);
809
1
	let bridge_id_mismatch = BridgeId::new(&InteriorLocation::Here, &InteriorLocation::Here);
810
1
	let lane_id = TestLaneIdType::try_new(1, 2).unwrap();
811
1
	let lane_id_mismatch = TestLaneIdType::try_new(3, 4).unwrap();
812
1

            
813
1
	let test_bridge_state = |id,
814
	                         bridge,
815
	                         (lane_id, bridge_id),
816
	                         (inbound_lane_id, outbound_lane_id),
817
6
	                         expected_error: Option<TryRuntimeError>| {
818
6
		Bridges::<TestRuntime, ()>::insert(id, bridge);
819
6
		LaneToBridge::<TestRuntime, ()>::insert(lane_id, bridge_id);
820
6

            
821
6
		let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
822
6
		lanes_manager.create_inbound_lane(inbound_lane_id).unwrap();
823
6
		lanes_manager
824
6
			.create_outbound_lane(outbound_lane_id)
825
6
			.unwrap();
826
6

            
827
6
		let result = XcmOverBridge::do_try_state();
828
6
		if let Some(e) = expected_error {
829
5
			assert_err!(result, e);
830
		} else {
831
1
			assert_ok!(result);
832
		}
833
6
	};
834
8
	let cleanup = |bridge_id, lane_ids| {
835
8
		Bridges::<TestRuntime, ()>::remove(bridge_id);
836
18
		for lane_id in lane_ids {
837
10
			LaneToBridge::<TestRuntime, ()>::remove(lane_id);
838
10
			let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
839
10
			if let Ok(lane) = lanes_manager.any_state_inbound_lane(lane_id) {
840
7
				lane.purge();
841
7
			}
842
10
			if let Ok(lane) = lanes_manager.any_state_outbound_lane(lane_id) {
843
7
				lane.purge();
844
7
			}
845
		}
846
8
		assert_ok!(XcmOverBridge::do_try_state());
847
8
	};
848

            
849
1
	run_test(|| {
850
1
		// ok state
851
1
		test_bridge_state(
852
1
			bridge_id,
853
1
			Bridge {
854
1
				bridge_origin_relative_location: Box::new(VersionedLocation::from(
855
1
					bridge_origin_relative_location.clone(),
856
1
				)),
857
1
				bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
858
1
					bridge_origin_universal_location.clone(),
859
1
				)),
860
1
				bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from(
861
1
					bridge_destination_universal_location.clone(),
862
1
				)),
863
1
				state: BridgeState::Opened,
864
1
				deposit: Some(Deposit::new(bridge_owner_account.clone(), Zero::zero())),
865
1
				lane_id,
866
1
				maybe_notify: None,
867
1
			},
868
1
			(lane_id, bridge_id),
869
1
			(lane_id, lane_id),
870
1
			None,
871
1
		);
872
1
		cleanup(bridge_id, vec![lane_id]);
873
1

            
874
1
		// error - missing `LaneToBridge` mapping
875
1
		test_bridge_state(
876
1
			bridge_id,
877
1
			Bridge {
878
1
				bridge_origin_relative_location: Box::new(VersionedLocation::from(
879
1
					bridge_origin_relative_location.clone(),
880
1
				)),
881
1
				bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
882
1
					bridge_origin_universal_location.clone(),
883
1
				)),
884
1
				bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from(
885
1
					bridge_destination_universal_location.clone(),
886
1
				)),
887
1
				state: BridgeState::Opened,
888
1
				deposit: Some(Deposit::new(bridge_owner_account.clone(), Zero::zero())),
889
1
				lane_id,
890
1
				maybe_notify: None,
891
1
			},
892
1
			(lane_id, bridge_id_mismatch),
893
1
			(lane_id, lane_id),
894
1
			Some(TryRuntimeError::Other(
895
1
				"Found `LaneToBridge` inconsistency for bridge_id - missing mapping!",
896
1
			)),
897
1
		);
898
1
		cleanup(bridge_id, vec![lane_id]);
899
1

            
900
1
		// error bridge owner account cannot be calculated
901
1
		test_bridge_state(
902
1
			bridge_id,
903
1
			Bridge {
904
1
				bridge_origin_relative_location: Box::new(VersionedLocation::from(
905
1
					bridge_origin_relative_location.clone(),
906
1
				)),
907
1
				bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
908
1
					bridge_origin_universal_location.clone(),
909
1
				)),
910
1
				bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from(
911
1
					bridge_destination_universal_location.clone(),
912
1
				)),
913
1
				state: BridgeState::Opened,
914
1
				deposit: Some(Deposit::new(bridge_owner_account_mismatch.clone(), Zero::zero())),
915
1
				lane_id,
916
1
				maybe_notify: None,
917
1
			},
918
1
			(lane_id, bridge_id),
919
1
			(lane_id, lane_id),
920
1
			Some(TryRuntimeError::Other("`bridge.deposit.account` is different than calculated from `bridge.bridge_origin_relative_location`, needs migration!")),
921
1
		);
922
1
		cleanup(bridge_id, vec![lane_id]);
923
1

            
924
1
		// error when (bridge_origin_universal_location + bridge_destination_universal_location)
925
1
		// produces different `BridgeId`
926
1
		test_bridge_state(
927
1
			bridge_id_mismatch,
928
1
			Bridge {
929
1
				bridge_origin_relative_location: Box::new(VersionedLocation::from(
930
1
					bridge_origin_relative_location.clone(),
931
1
				)),
932
1
				bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
933
1
					bridge_origin_universal_location.clone(),
934
1
				)),
935
1
				bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from(
936
1
					bridge_destination_universal_location.clone(),
937
1
				)),
938
1
				state: BridgeState::Opened,
939
1
				deposit: Some(Deposit::new(bridge_owner_account_mismatch.clone(), Zero::zero())),
940
1
				lane_id,
941
1
				maybe_notify: None,
942
1
			},
943
1
			(lane_id, bridge_id_mismatch),
944
1
			(lane_id, lane_id),
945
1
			Some(TryRuntimeError::Other("`bridge_id` is different than calculated from `bridge_origin_universal_location_as_latest` and `bridge_destination_universal_location_as_latest`, needs migration!")),
946
1
		);
947
1
		cleanup(bridge_id_mismatch, vec![lane_id]);
948
1

            
949
1
		// missing inbound lane for a bridge
950
1
		test_bridge_state(
951
1
			bridge_id,
952
1
			Bridge {
953
1
				bridge_origin_relative_location: Box::new(VersionedLocation::from(
954
1
					bridge_origin_relative_location.clone(),
955
1
				)),
956
1
				bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
957
1
					bridge_origin_universal_location.clone(),
958
1
				)),
959
1
				bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from(
960
1
					bridge_destination_universal_location.clone(),
961
1
				)),
962
1
				state: BridgeState::Opened,
963
1
				deposit: Some(Deposit::new(bridge_owner_account.clone(), Zero::zero())),
964
1
				lane_id,
965
1
				maybe_notify: None,
966
1
			},
967
1
			(lane_id, bridge_id),
968
1
			(lane_id_mismatch, lane_id),
969
1
			Some(TryRuntimeError::Other("Inbound lane not found!")),
970
1
		);
971
1
		cleanup(bridge_id, vec![lane_id, lane_id_mismatch]);
972
1

            
973
1
		// missing outbound lane for a bridge
974
1
		test_bridge_state(
975
1
			bridge_id,
976
1
			Bridge {
977
1
				bridge_origin_relative_location: Box::new(VersionedLocation::from(
978
1
					bridge_origin_relative_location.clone(),
979
1
				)),
980
1
				bridge_origin_universal_location: Box::new(VersionedInteriorLocation::from(
981
1
					bridge_origin_universal_location.clone(),
982
1
				)),
983
1
				bridge_destination_universal_location: Box::new(VersionedInteriorLocation::from(
984
1
					bridge_destination_universal_location.clone(),
985
1
				)),
986
1
				state: BridgeState::Opened,
987
1
				deposit: Some(Deposit::new(bridge_owner_account, Zero::zero())),
988
1
				lane_id,
989
1
				maybe_notify: None,
990
1
			},
991
1
			(lane_id, bridge_id),
992
1
			(lane_id, lane_id_mismatch),
993
1
			Some(TryRuntimeError::Other("Outbound lane not found!")),
994
1
		);
995
1
		cleanup(bridge_id, vec![lane_id, lane_id_mismatch]);
996
1

            
997
1
		// missing bridge for inbound lane
998
1
		let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
999
1
		assert!(lanes_manager.create_inbound_lane(lane_id).is_ok());
1
		assert_err!(XcmOverBridge::do_try_state(), TryRuntimeError::Other("Found `LaneToBridge` inconsistency for `InboundLanes`'s lane_id - missing mapping!"));
1
		cleanup(bridge_id, vec![lane_id]);
1

            
1
		// missing bridge for outbound lane
1
		let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
1
		assert!(lanes_manager.create_outbound_lane(lane_id).is_ok());
1
		assert_err!(XcmOverBridge::do_try_state(), TryRuntimeError::Other("Found `LaneToBridge` inconsistency for `OutboundLanes`'s lane_id - missing mapping!"));
1
		cleanup(bridge_id, vec![lane_id]);
1
	});
1
}
#[test]
1
fn ensure_encoding_compatibility() {
	use parity_scale_codec::Encode;
1
	let bridge_destination_universal_location = BridgedUniversalDestination::get();
1
	let may_prune_messages = 13;
1
	let receiver = Receiver::new(13, 15);
1

            
1
	assert_eq!(
1
		bp_xcm_bridge::XcmBridgeCall::open_bridge {
1
			bridge_destination_universal_location: Box::new(
1
				bridge_destination_universal_location.clone().into()
1
			),
1
			maybe_notify: Some(receiver.clone()),
1
		}
1
		.encode(),
1
		Call::<TestRuntime, ()>::open_bridge {
1
			bridge_destination_universal_location: Box::new(
1
				bridge_destination_universal_location.clone().into()
1
			),
1
			maybe_notify: Some(receiver),
1
		}
1
		.encode()
1
	);
1
	assert_eq!(
1
		bp_xcm_bridge::XcmBridgeCall::close_bridge {
1
			bridge_destination_universal_location: Box::new(
1
				bridge_destination_universal_location.clone().into()
1
			),
1
			may_prune_messages,
1
		}
1
		.encode(),
1
		Call::<TestRuntime, ()>::close_bridge {
1
			bridge_destination_universal_location: Box::new(
1
				bridge_destination_universal_location.clone().into()
1
			),
1
			may_prune_messages,
1
		}
1
		.encode()
1
	);
1
}