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
//! The code that allows to use the pallet (`pallet-xcm-bridge`) as inbound
18
//! bridge messages dispatcher. Internally, it just forwards inbound blob to the
19
//! XCM-level blob dispatcher, which pushes message to some other queue (e.g.
20
//! to HRMP queue with the sibling target chain).
21
//!
22
//! This code is executed at the target bridge hub.
23

            
24
use crate::{Config, DispatchChannelStatusProvider, Pallet, LOG_TARGET};
25

            
26
use bp_messages::target_chain::{DispatchMessage, MessageDispatch};
27
use bp_runtime::messages::MessageDispatchResult;
28
use bp_xcm_bridge::XcmAsPlainPayload;
29
use frame_support::{weights::Weight, CloneNoBound, EqNoBound, PartialEqNoBound};
30
use pallet_bridge_messages::{Config as BridgeMessagesConfig, WeightInfoExt};
31
use parity_scale_codec::{Decode, Encode};
32
use scale_info::TypeInfo;
33
use sp_runtime::SaturatedConversion;
34
use xcm::prelude::*;
35
use xcm_builder::{DispatchBlob, DispatchBlobError};
36

            
37
/// Message dispatch result type for single message.
38
#[derive(CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, Debug, TypeInfo)]
39
pub enum XcmBlobMessageDispatchResult {
40
	/// We've been unable to decode message payload.
41
	InvalidPayload,
42
	/// Message has been dispatched.
43
	Dispatched,
44
	/// Message has **NOT** been dispatched because of given error.
45
	NotDispatched(#[codec(skip)] Option<DispatchBlobError>),
46
}
47

            
48
/// An easy way to access associated messages pallet weights.
49
type MessagesPalletWeights<T, I> =
50
	<T as BridgeMessagesConfig<<T as Config<I>>::BridgeMessagesPalletInstance>>::WeightInfo;
51

            
52
impl<T: Config<I>, I: 'static> MessageDispatch for Pallet<T, I>
53
where
54
	T: BridgeMessagesConfig<T::BridgeMessagesPalletInstance, InboundPayload = XcmAsPlainPayload>,
55
{
56
	type DispatchPayload = XcmAsPlainPayload;
57
	type DispatchLevelResult = XcmBlobMessageDispatchResult;
58
	type LaneId = T::LaneId;
59

            
60
2
	fn is_active(lane: Self::LaneId) -> bool {
61
2
		Pallet::<T, I>::bridge_by_lane_id(&lane)
62
2
			.and_then(|(_, bridge)| {
63
2
				bridge
64
2
					.bridge_origin_relative_location
65
2
					.try_as()
66
2
					.cloned()
67
2
					.ok()
68
2
			})
69
2
			.map(|recipient: Location| !T::BlobDispatcher::is_congested(&recipient))
70
2
			.unwrap_or(false)
71
2
	}
72

            
73
2
	fn dispatch_weight(
74
2
		message: &mut DispatchMessage<Self::DispatchPayload, Self::LaneId>,
75
2
	) -> Weight {
76
2
		match message.data.payload {
77
1
			Ok(ref payload) => {
78
1
				let payload_size = payload.encoded_size().saturated_into();
79
1
				MessagesPalletWeights::<T, I>::message_dispatch_weight(payload_size)
80
			}
81
1
			Err(_) => Weight::zero(),
82
		}
83
2
	}
84

            
85
2
	fn dispatch(
86
2
		message: DispatchMessage<Self::DispatchPayload, Self::LaneId>,
87
2
	) -> MessageDispatchResult<Self::DispatchLevelResult> {
88
2
		let payload = match message.data.payload {
89
1
			Ok(payload) => payload,
90
1
			Err(e) => {
91
1
				log::error!(
92
					target: LOG_TARGET,
93
					"dispatch - payload error: {e:?} for lane_id: {:?} and message_nonce: {:?}",
94
					message.key.lane_id,
95
					message.key.nonce
96
				);
97
1
				return MessageDispatchResult {
98
1
					unspent_weight: Weight::zero(),
99
1
					dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload,
100
1
				};
101
			}
102
		};
103
1
		let dispatch_level_result = match T::BlobDispatcher::dispatch_blob(payload) {
104
			Ok(_) => {
105
1
				log::debug!(
106
					target: LOG_TARGET,
107
					"dispatch - `DispatchBlob::dispatch_blob` was ok for lane_id: {:?} and message_nonce: {:?}",
108
					message.key.lane_id,
109
					message.key.nonce
110
				);
111
1
				XcmBlobMessageDispatchResult::Dispatched
112
			}
113
			Err(e) => {
114
				log::error!(
115
					target: LOG_TARGET,
116
					"dispatch - `DispatchBlob::dispatch_blob` failed with error: {e:?} for lane_id: {:?} and message_nonce: {:?}",
117
					message.key.lane_id,
118
					message.key.nonce
119
				);
120
				XcmBlobMessageDispatchResult::NotDispatched(Some(e))
121
			}
122
		};
123
1
		MessageDispatchResult {
124
1
			unspent_weight: Weight::zero(),
125
1
			dispatch_level_result,
126
1
		}
127
2
	}
128
}
129

            
130
#[cfg(test)]
131
mod tests {
132
	use super::*;
133
	use crate::{mock::*, Bridges, LaneToBridge, LanesManagerOf};
134

            
135
	use bp_messages::{target_chain::DispatchMessageData, LaneIdType, MessageKey};
136
	use bp_xcm_bridge::{Bridge, BridgeLocations, BridgeState};
137
	use frame_support::assert_ok;
138
	use pallet_bridge_messages::InboundLaneStorage;
139

            
140
4
	fn bridge() -> (Box<BridgeLocations>, TestLaneIdType) {
141
4
		let origin = OpenBridgeOrigin::sibling_parachain_origin();
142
4
		let with = bridged_asset_hub_universal_location();
143
4
		let locations =
144
4
			XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap();
145
4
		let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
146
4
		(locations, lane_id)
147
4
	}
148

            
149
2
	fn run_test_with_opened_bridge(test: impl FnOnce()) {
150
2
		run_test(|| {
151
2
			let (bridge, lane_id) = bridge();
152
2

            
153
2
			if !Bridges::<TestRuntime, ()>::contains_key(bridge.bridge_id()) {
154
				// insert bridge
155
2
				Bridges::<TestRuntime, ()>::insert(
156
2
					bridge.bridge_id(),
157
2
					Bridge {
158
2
						bridge_origin_relative_location: Box::new(
159
2
							bridge.bridge_origin_relative_location().clone().into(),
160
2
						),
161
2
						bridge_origin_universal_location: Box::new(
162
2
							bridge.bridge_origin_universal_location().clone().into(),
163
2
						),
164
2
						bridge_destination_universal_location: Box::new(
165
2
							bridge
166
2
								.bridge_destination_universal_location()
167
2
								.clone()
168
2
								.into(),
169
2
						),
170
2
						state: BridgeState::Opened,
171
2
						deposit: None,
172
2
						lane_id,
173
2
						maybe_notify: None,
174
2
					},
175
2
				);
176
2
				LaneToBridge::<TestRuntime, ()>::insert(lane_id, bridge.bridge_id());
177
2

            
178
2
				// create lanes
179
2
				let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
180
2
				if lanes_manager.create_inbound_lane(lane_id).is_ok() {
181
2
					assert_eq!(
182
2
						0,
183
2
						lanes_manager
184
2
							.active_inbound_lane(lane_id)
185
2
							.unwrap()
186
2
							.storage()
187
2
							.data()
188
2
							.last_confirmed_nonce
189
2
					);
190
				}
191
2
				if lanes_manager.create_outbound_lane(lane_id).is_ok() {
192
2
					assert!(lanes_manager
193
2
						.active_outbound_lane(lane_id)
194
2
						.unwrap()
195
2
						.queued_messages()
196
2
						.is_empty());
197
				}
198
			}
199
2
			assert_ok!(XcmOverBridge::do_try_state());
200

            
201
2
			test();
202
2
		});
203
2
	}
204

            
205
2
	fn invalid_message() -> DispatchMessage<Vec<u8>, TestLaneIdType> {
206
2
		DispatchMessage {
207
2
			key: MessageKey {
208
2
				lane_id: TestLaneIdType::try_new(1, 2).unwrap(),
209
2
				nonce: 1,
210
2
			},
211
2
			data: DispatchMessageData {
212
2
				payload: Err(parity_scale_codec::Error::from("test")),
213
2
			},
214
2
		}
215
2
	}
216

            
217
2
	fn valid_message() -> DispatchMessage<Vec<u8>, TestLaneIdType> {
218
2
		DispatchMessage {
219
2
			key: MessageKey {
220
2
				lane_id: TestLaneIdType::try_new(1, 2).unwrap(),
221
2
				nonce: 1,
222
2
			},
223
2
			data: DispatchMessageData {
224
2
				payload: Ok(vec![42]),
225
2
			},
226
2
		}
227
2
	}
228

            
229
	#[test]
230
1
	fn dispatcher_is_inactive_when_channel_with_target_chain_is_congested() {
231
1
		run_test_with_opened_bridge(|| {
232
1
			let bridge = bridge();
233
1
			let with = bridge.0.bridge_origin_relative_location();
234
1
			let lane_id = bridge.1;
235
1
			TestBlobDispatcher::make_congested(with);
236
1
			assert!(!XcmOverBridge::is_active(lane_id));
237
1
		});
238
1
	}
239

            
240
	#[test]
241
1
	fn dispatcher_is_active_when_channel_with_target_chain_is_not_congested() {
242
1
		run_test_with_opened_bridge(|| {
243
1
			assert!(XcmOverBridge::is_active(bridge().1));
244
1
		});
245
1
	}
246

            
247
	#[test]
248
1
	fn dispatch_weight_is_zero_if_we_have_failed_to_decode_message() {
249
1
		run_test(|| {
250
1
			assert_eq!(
251
1
				XcmOverBridge::dispatch_weight(&mut invalid_message()),
252
1
				Weight::zero()
253
1
			);
254
1
		});
255
1
	}
256

            
257
	#[test]
258
1
	fn dispatch_weight_is_non_zero_if_we_have_decoded_message() {
259
1
		run_test(|| {
260
1
			assert_ne!(
261
1
				XcmOverBridge::dispatch_weight(&mut valid_message()),
262
1
				Weight::zero()
263
1
			);
264
1
		});
265
1
	}
266

            
267
	#[test]
268
1
	fn message_is_not_dispatched_when_we_have_failed_to_decode_message() {
269
1
		run_test(|| {
270
1
			assert_eq!(
271
1
				XcmOverBridge::dispatch(invalid_message()),
272
1
				MessageDispatchResult {
273
1
					unspent_weight: Weight::zero(),
274
1
					dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload,
275
1
				},
276
1
			);
277
1
			assert!(!TestBlobDispatcher::is_dispatched());
278
1
		});
279
1
	}
280

            
281
	#[test]
282
1
	fn message_is_dispatched_when_we_have_decoded_message() {
283
1
		run_test(|| {
284
1
			assert_eq!(
285
1
				XcmOverBridge::dispatch(valid_message()),
286
1
				MessageDispatchResult {
287
1
					unspent_weight: Weight::zero(),
288
1
					dispatch_level_result: XcmBlobMessageDispatchResult::Dispatched,
289
1
				},
290
1
			);
291
1
			assert!(TestBlobDispatcher::is_dispatched());
292
1
		});
293
1
	}
294
}