1use crate::{Config, Pallet, LOG_TARGET};
25
26use bp_messages::target_chain::{DispatchMessage, MessageDispatch};
27use bp_runtime::messages::MessageDispatchResult;
28use bp_xcm_bridge_hub::{LocalXcmChannelManager, XcmAsPlainPayload};
29use codec::{Decode, DecodeWithMemTracking, Encode};
30use frame_support::{weights::Weight, CloneNoBound, EqNoBound, PartialEqNoBound};
31use pallet_bridge_messages::{Config as BridgeMessagesConfig, WeightInfoExt};
32use scale_info::TypeInfo;
33use sp_runtime::SaturatedConversion;
34use xcm::prelude::*;
35use xcm_builder::{DispatchBlob, DispatchBlobError};
36
37#[derive(
39 CloneNoBound,
40 EqNoBound,
41 PartialEqNoBound,
42 Encode,
43 Decode,
44 DecodeWithMemTracking,
45 Debug,
46 TypeInfo,
47)]
48pub enum XcmBlobMessageDispatchResult {
49 InvalidPayload,
51 Dispatched,
53 NotDispatched(#[codec(skip)] Option<DispatchBlobError>),
55}
56
57type MessagesPalletWeights<T, I> =
59 <T as BridgeMessagesConfig<<T as Config<I>>::BridgeMessagesPalletInstance>>::WeightInfo;
60
61impl<T: Config<I>, I: 'static> MessageDispatch for Pallet<T, I>
62where
63 T: BridgeMessagesConfig<T::BridgeMessagesPalletInstance, InboundPayload = XcmAsPlainPayload>,
64{
65 type DispatchPayload = XcmAsPlainPayload;
66 type DispatchLevelResult = XcmBlobMessageDispatchResult;
67 type LaneId = T::LaneId;
68
69 fn is_active(lane: Self::LaneId) -> bool {
70 Pallet::<T, I>::bridge_by_lane_id(&lane)
71 .and_then(|(_, bridge)| (*bridge.bridge_origin_relative_location).try_into().ok())
72 .map(|recipient: Location| !T::LocalXcmChannelManager::is_congested(&recipient))
73 .unwrap_or(false)
74 }
75
76 fn dispatch_weight(
77 message: &mut DispatchMessage<Self::DispatchPayload, Self::LaneId>,
78 ) -> Weight {
79 match message.data.payload {
80 Ok(ref payload) => {
81 let payload_size = payload.encoded_size().saturated_into();
82 MessagesPalletWeights::<T, I>::message_dispatch_weight(payload_size)
83 },
84 Err(_) => Weight::zero(),
85 }
86 }
87
88 fn dispatch(
89 message: DispatchMessage<Self::DispatchPayload, Self::LaneId>,
90 ) -> MessageDispatchResult<Self::DispatchLevelResult> {
91 let payload = match message.data.payload {
92 Ok(payload) => payload,
93 Err(e) => {
94 tracing::error!(
95 target: LOG_TARGET,
96 error=?e,
97 lane_id=?message.key.lane_id,
98 message_nonce=?message.key.nonce,
99 "dispatch - payload error"
100 );
101 return MessageDispatchResult {
102 unspent_weight: Weight::zero(),
103 dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload,
104 }
105 },
106 };
107 let dispatch_level_result = match T::BlobDispatcher::dispatch_blob(payload) {
108 Ok(_) => {
109 tracing::debug!(
110 target: LOG_TARGET,
111 lane_id=?message.key.lane_id,
112 message_nonce=?message.key.nonce,
113 "dispatch - `DispatchBlob::dispatch_blob` was ok"
114 );
115 XcmBlobMessageDispatchResult::Dispatched
116 },
117 Err(e) => {
118 tracing::error!(
119 target: LOG_TARGET,
120 error=?e,
121 lane_id=?message.key.lane_id,
122 message_nonce=?message.key.nonce,
123 "dispatch - `DispatchBlob::dispatch_blob` failed"
124 );
125 XcmBlobMessageDispatchResult::NotDispatched(Some(e))
126 },
127 };
128 MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result }
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use crate::{mock::*, Bridges, LaneToBridge, LanesManagerOf};
136
137 use bp_messages::{target_chain::DispatchMessageData, LaneIdType, MessageKey};
138 use bp_xcm_bridge_hub::{Bridge, BridgeLocations, BridgeState};
139 use frame_support::assert_ok;
140 use pallet_bridge_messages::InboundLaneStorage;
141 use xcm_executor::traits::ConvertLocation;
142
143 fn bridge() -> (Box<BridgeLocations>, TestLaneIdType) {
144 let origin = OpenBridgeOrigin::sibling_parachain_origin();
145 let with = bridged_asset_hub_universal_location();
146 let locations =
147 XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap();
148 let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
149 (locations, lane_id)
150 }
151
152 fn run_test_with_opened_bridge(test: impl FnOnce()) {
153 run_test(|| {
154 let (bridge, lane_id) = bridge();
155
156 if !Bridges::<TestRuntime, ()>::contains_key(bridge.bridge_id()) {
157 Bridges::<TestRuntime, ()>::insert(
159 bridge.bridge_id(),
160 Bridge {
161 bridge_origin_relative_location: Box::new(
162 bridge.bridge_origin_relative_location().clone().into(),
163 ),
164 bridge_origin_universal_location: Box::new(
165 bridge.bridge_origin_universal_location().clone().into(),
166 ),
167 bridge_destination_universal_location: Box::new(
168 bridge.bridge_destination_universal_location().clone().into(),
169 ),
170 state: BridgeState::Opened,
171 bridge_owner_account: LocationToAccountId::convert_location(
172 bridge.bridge_origin_relative_location(),
173 )
174 .expect("valid accountId"),
175 deposit: 0,
176 lane_id,
177 },
178 );
179 LaneToBridge::<TestRuntime, ()>::insert(lane_id, bridge.bridge_id());
180
181 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
183 if lanes_manager.create_inbound_lane(lane_id).is_ok() {
184 assert_eq!(
185 0,
186 lanes_manager
187 .active_inbound_lane(lane_id)
188 .unwrap()
189 .storage()
190 .data()
191 .last_confirmed_nonce
192 );
193 }
194 if lanes_manager.create_outbound_lane(lane_id).is_ok() {
195 assert!(lanes_manager
196 .active_outbound_lane(lane_id)
197 .unwrap()
198 .queued_messages()
199 .is_empty());
200 }
201 }
202 assert_ok!(XcmOverBridge::do_try_state());
203
204 test();
205 });
206 }
207
208 fn invalid_message() -> DispatchMessage<Vec<u8>, TestLaneIdType> {
209 DispatchMessage {
210 key: MessageKey { lane_id: TestLaneIdType::try_new(1, 2).unwrap(), nonce: 1 },
211 data: DispatchMessageData { payload: Err(codec::Error::from("test")) },
212 }
213 }
214
215 fn valid_message() -> DispatchMessage<Vec<u8>, TestLaneIdType> {
216 DispatchMessage {
217 key: MessageKey { lane_id: TestLaneIdType::try_new(1, 2).unwrap(), nonce: 1 },
218 data: DispatchMessageData { payload: Ok(vec![42]) },
219 }
220 }
221
222 #[test]
223 fn dispatcher_is_inactive_when_channel_with_target_chain_is_congested() {
224 run_test_with_opened_bridge(|| {
225 TestLocalXcmChannelManager::make_congested();
226 assert!(!XcmOverBridge::is_active(bridge().1));
227 });
228 }
229
230 #[test]
231 fn dispatcher_is_active_when_channel_with_target_chain_is_not_congested() {
232 run_test_with_opened_bridge(|| {
233 assert!(XcmOverBridge::is_active(bridge().1));
234 });
235 }
236
237 #[test]
238 fn dispatch_weight_is_zero_if_we_have_failed_to_decode_message() {
239 run_test(|| {
240 assert_eq!(XcmOverBridge::dispatch_weight(&mut invalid_message()), Weight::zero());
241 });
242 }
243
244 #[test]
245 fn dispatch_weight_is_non_zero_if_we_have_decoded_message() {
246 run_test(|| {
247 assert_ne!(XcmOverBridge::dispatch_weight(&mut valid_message()), Weight::zero());
248 });
249 }
250
251 #[test]
252 fn message_is_not_dispatched_when_we_have_failed_to_decode_message() {
253 run_test(|| {
254 assert_eq!(
255 XcmOverBridge::dispatch(invalid_message()),
256 MessageDispatchResult {
257 unspent_weight: Weight::zero(),
258 dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload,
259 },
260 );
261 assert!(!TestBlobDispatcher::is_dispatched());
262 });
263 }
264
265 #[test]
266 fn message_is_dispatched_when_we_have_decoded_message() {
267 run_test(|| {
268 assert_eq!(
269 XcmOverBridge::dispatch(valid_message()),
270 MessageDispatchResult {
271 unspent_weight: Weight::zero(),
272 dispatch_level_result: XcmBlobMessageDispatchResult::Dispatched,
273 },
274 );
275 assert!(TestBlobDispatcher::is_dispatched());
276 });
277 }
278}