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 crate as pallet_xcm_bridge_router;
20

            
21
use crate::impls::EnsureIsRemoteBridgeIdResolver;
22
use bp_xcm_bridge_router::{BridgeState, ResolveBridgeId};
23
use frame_support::{
24
	construct_runtime, derive_impl, parameter_types,
25
	traits::{Contains, Equals},
26
};
27
use frame_system::EnsureRoot;
28
use parity_scale_codec::Encode;
29
use sp_runtime::{traits::ConstU128, BuildStorage};
30
use sp_std::cell::RefCell;
31
use xcm::prelude::*;
32
use xcm_builder::{
33
	InspectMessageQueues, NetworkExportTable, NetworkExportTableItem, SovereignPaidRemoteExporter,
34
};
35

            
36
type Block = frame_system::mocking::MockBlock<TestRuntime>;
37

            
38
/// HRMP fee.
39
pub const HRMP_FEE: u128 = 500;
40
/// Base bridge fee.
41
pub const BASE_FEE: u128 = 1_000_000;
42
/// Byte bridge fee.
43
pub const BYTE_FEE: u128 = 1_000;
44

            
45
170
construct_runtime! {
46
24
	pub enum TestRuntime
47
24
	{
48
24
		System: frame_system,
49
24
		XcmBridgeHubRouter: pallet_xcm_bridge_router,
50
24
	}
51
174
}
52

            
53
parameter_types! {
54
	pub ThisNetworkId: NetworkId = Polkadot;
55
	pub BridgedNetworkId: NetworkId = Kusama;
56
	pub UniversalLocation: InteriorLocation = [GlobalConsensus(ThisNetworkId::get()), Parachain(1000)].into();
57
	pub SiblingBridgeHubLocation: Location = ParentThen([Parachain(1002)].into()).into();
58
	pub BridgeFeeAsset: AssetId = Location::parent().into();
59
	pub BridgeTable: Vec<NetworkExportTableItem>
60
		= vec![
61
			NetworkExportTableItem::new(
62
				BridgedNetworkId::get(),
63
				None,
64
				SiblingBridgeHubLocation::get(),
65
				Some((BridgeFeeAsset::get(), BASE_FEE).into())
66
			)
67
		];
68
	pub UnknownXcmVersionForRoutableLocation: Location = Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(9999)]);
69
}
70

            
71
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
72
impl frame_system::Config for TestRuntime {
73
	type Block = Block;
74
}
75

            
76
/// Simple implementation where every dest resolves to the exact one `BridgeId`.
77
pub struct EveryDestinationToSameBridgeIdResolver;
78
impl ResolveBridgeId for EveryDestinationToSameBridgeIdResolver {
79
	type BridgeId = ();
80

            
81
	fn resolve_for_dest(_dest: &Location) -> Option<Self::BridgeId> {
82
		Some(())
83
	}
84

            
85
	fn resolve_for(
86
		_bridged_network: &NetworkId,
87
		_bridged_dest: &InteriorLocation,
88
	) -> Option<Self::BridgeId> {
89
		Some(())
90
	}
91
}
92

            
93
/// An instance of `pallet_xcm_bridge_hub_router` configured to use a remote exporter with the
94
/// `ExportMessage` instruction, which will be delivered to a sibling parachain using
95
/// `SiblingBridgeHubLocation`.
96
#[derive_impl(pallet_xcm_bridge_router::config_preludes::TestDefaultConfig)]
97
impl pallet_xcm_bridge_router::Config<()> for TestRuntime {
98
	type DestinationVersion =
99
		LatestOrNoneForLocationVersionChecker<Equals<UnknownXcmVersionForRoutableLocation>>;
100

            
101
	type MessageExporter = SovereignPaidRemoteExporter<
102
		pallet_xcm_bridge_router::impls::ViaRemoteBridgeExporter<
103
			TestRuntime,
104
			(),
105
			NetworkExportTable<BridgeTable>,
106
			BridgedNetworkId,
107
			SiblingBridgeHubLocation,
108
		>,
109
		TestXcmRouter,
110
		UniversalLocation,
111
	>;
112

            
113
	type BridgeIdResolver = EnsureIsRemoteBridgeIdResolver<UniversalLocation>;
114
	type UpdateBridgeStatusOrigin = EnsureRoot<u64>;
115

            
116
	type ByteFee = ConstU128<BYTE_FEE>;
117
	type FeeAsset = BridgeFeeAsset;
118
}
119

            
120
pub struct LatestOrNoneForLocationVersionChecker<Location>(sp_std::marker::PhantomData<Location>);
121
impl<LocationValue: Contains<Location>> GetVersion
122
	for LatestOrNoneForLocationVersionChecker<LocationValue>
123
{
124
7
	fn get_version_for(dest: &Location) -> Option<XcmVersion> {
125
7
		if LocationValue::contains(dest) {
126
2
			return None;
127
5
		}
128
5
		Some(XCM_VERSION)
129
7
	}
130
}
131

            
132
pub struct TestXcmRouter;
133

            
134
impl TestXcmRouter {
135
2
	pub fn is_message_sent() -> bool {
136
2
		!Self::get_messages().is_empty()
137
2
	}
138
}
139

            
140
thread_local! {
141
	pub static SENT_XCM: RefCell<Vec<(Location, Xcm<()>)>> = RefCell::new(Vec::new());
142
}
143

            
144
impl SendXcm for TestXcmRouter {
145
	type Ticket = (Location, Xcm<()>);
146

            
147
11
	fn validate(
148
11
		destination: &mut Option<Location>,
149
11
		message: &mut Option<Xcm<()>>,
150
11
	) -> SendResult<Self::Ticket> {
151
11
		let pair = (destination.take().unwrap(), message.take().unwrap());
152
11
		Ok((pair, (BridgeFeeAsset::get(), HRMP_FEE).into()))
153
11
	}
154

            
155
3
	fn deliver(pair: Self::Ticket) -> Result<XcmHash, SendError> {
156
3
		let hash = fake_message_hash(&pair.1);
157
3
		SENT_XCM.with(|q| q.borrow_mut().push(pair));
158
3
		Ok(hash)
159
3
	}
160
}
161

            
162
impl InspectMessageQueues for TestXcmRouter {
163
	fn clear_messages() {
164
		SENT_XCM.with(|q| q.borrow_mut().clear());
165
	}
166

            
167
2
	fn get_messages() -> Vec<(VersionedLocation, Vec<VersionedXcm<()>>)> {
168
2
		SENT_XCM.with(|q| {
169
2
			(*q.borrow())
170
2
				.clone()
171
2
				.iter()
172
2
				.map(|(location, message)| {
173
2
					(
174
2
						VersionedLocation::from(location.clone()),
175
2
						vec![VersionedXcm::from(message.clone())],
176
2
					)
177
2
				})
178
2
				.collect()
179
2
		})
180
2
	}
181
}
182

            
183
/// Return test externalities to use in tests.
184
9
pub fn new_test_ext() -> sp_io::TestExternalities {
185
9
	let t = frame_system::GenesisConfig::<TestRuntime>::default()
186
9
		.build_storage()
187
9
		.unwrap();
188
9
	sp_io::TestExternalities::new(t)
189
9
}
190

            
191
/// Run pallet test.
192
9
pub fn run_test<T>(test: impl FnOnce() -> T) -> T {
193
9
	new_test_ext().execute_with(|| {
194
9
		System::set_block_number(1);
195
9
		System::reset_events();
196
9

            
197
9
		test()
198
9
	})
199
9
}
200

            
201
3
pub(crate) fn fake_message_hash<T>(message: &Xcm<T>) -> XcmHash {
202
3
	message.using_encoded(sp_io::hashing::blake2_256)
203
3
}
204

            
205
3
pub(crate) fn set_bridge_state_for<T: pallet_xcm_bridge_router::Config<I>, I: 'static>(
206
3
	dest: &Location,
207
3
	bridge_state: Option<BridgeState>,
208
3
) -> pallet_xcm_bridge_router::BridgeIdOf<T, I> {
209
3
	let bridge_id = <T::BridgeIdResolver as ResolveBridgeId>::resolve_for_dest(dest).unwrap();
210
3
	if let Some(bridge_state) = bridge_state {
211
3
		pallet_xcm_bridge_router::Bridges::<T, I>::insert(&bridge_id, bridge_state);
212
3
	} else {
213
		pallet_xcm_bridge_router::Bridges::<T, I>::remove(&bridge_id);
214
	}
215
3
	bridge_id
216
3
}
217

            
218
12
pub(crate) fn get_bridge_state_for<T: pallet_xcm_bridge_router::Config<I>, I: 'static>(
219
12
	dest: &Location,
220
12
) -> Option<BridgeState> {
221
12
	let bridge_id = <T::BridgeIdResolver as ResolveBridgeId>::resolve_for_dest(dest).unwrap();
222
12
	pallet_xcm_bridge_router::Bridges::<T, I>::get(bridge_id)
223
12
}
224

            
225
#[cfg(feature = "runtime-benchmarks")]
226
impl crate::benchmarking::Config<()> for TestRuntime {
227
	fn ensure_bridged_target_destination() -> Result<Location, frame_benchmarking::BenchmarkError> {
228
		Ok(Location::new(2, [GlobalConsensus(BridgedNetworkId::get())]))
229
	}
230
	fn update_bridge_status_origin() -> Option<RuntimeOrigin> {
231
		Some(RuntimeOrigin::root())
232
	}
233
}