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 log::error!(
95 target: LOG_TARGET,
96 "dispatch - payload error: {e:?} for lane_id: {:?} and message_nonce: {:?}",
97 message.key.lane_id,
98 message.key.nonce
99 );
100 return MessageDispatchResult {
101 unspent_weight: Weight::zero(),
102 dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload,
103 }
104 },
105 };
106 let dispatch_level_result = match T::BlobDispatcher::dispatch_blob(payload) {
107 Ok(_) => {
108 log::debug!(
109 target: LOG_TARGET,
110 "dispatch - `DispatchBlob::dispatch_blob` was ok for lane_id: {:?} and message_nonce: {:?}",
111 message.key.lane_id,
112 message.key.nonce
113 );
114 XcmBlobMessageDispatchResult::Dispatched
115 },
116 Err(e) => {
117 log::error!(
118 target: LOG_TARGET,
119 "dispatch - `DispatchBlob::dispatch_blob` failed with error: {e:?} for lane_id: {:?} and message_nonce: {:?}",
120 message.key.lane_id,
121 message.key.nonce
122 );
123 XcmBlobMessageDispatchResult::NotDispatched(Some(e))
124 },
125 };
126 MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result }
127 }
128}
129
130#[cfg(test)]
131mod 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_hub::{Bridge, BridgeLocations, BridgeState};
137 use frame_support::assert_ok;
138 use pallet_bridge_messages::InboundLaneStorage;
139 use xcm_executor::traits::ConvertLocation;
140
141 fn bridge() -> (Box<BridgeLocations>, TestLaneIdType) {
142 let origin = OpenBridgeOrigin::sibling_parachain_origin();
143 let with = bridged_asset_hub_universal_location();
144 let locations =
145 XcmOverBridge::bridge_locations_from_origin(origin, Box::new(with.into())).unwrap();
146 let lane_id = locations.calculate_lane_id(xcm::latest::VERSION).unwrap();
147 (locations, lane_id)
148 }
149
150 fn run_test_with_opened_bridge(test: impl FnOnce()) {
151 run_test(|| {
152 let (bridge, lane_id) = bridge();
153
154 if !Bridges::<TestRuntime, ()>::contains_key(bridge.bridge_id()) {
155 Bridges::<TestRuntime, ()>::insert(
157 bridge.bridge_id(),
158 Bridge {
159 bridge_origin_relative_location: Box::new(
160 bridge.bridge_origin_relative_location().clone().into(),
161 ),
162 bridge_origin_universal_location: Box::new(
163 bridge.bridge_origin_universal_location().clone().into(),
164 ),
165 bridge_destination_universal_location: Box::new(
166 bridge.bridge_destination_universal_location().clone().into(),
167 ),
168 state: BridgeState::Opened,
169 bridge_owner_account: LocationToAccountId::convert_location(
170 bridge.bridge_origin_relative_location(),
171 )
172 .expect("valid accountId"),
173 deposit: 0,
174 lane_id,
175 },
176 );
177 LaneToBridge::<TestRuntime, ()>::insert(lane_id, bridge.bridge_id());
178
179 let lanes_manager = LanesManagerOf::<TestRuntime, ()>::new();
181 if lanes_manager.create_inbound_lane(lane_id).is_ok() {
182 assert_eq!(
183 0,
184 lanes_manager
185 .active_inbound_lane(lane_id)
186 .unwrap()
187 .storage()
188 .data()
189 .last_confirmed_nonce
190 );
191 }
192 if lanes_manager.create_outbound_lane(lane_id).is_ok() {
193 assert!(lanes_manager
194 .active_outbound_lane(lane_id)
195 .unwrap()
196 .queued_messages()
197 .is_empty());
198 }
199 }
200 assert_ok!(XcmOverBridge::do_try_state());
201
202 test();
203 });
204 }
205
206 fn invalid_message() -> DispatchMessage<Vec<u8>, TestLaneIdType> {
207 DispatchMessage {
208 key: MessageKey { lane_id: TestLaneIdType::try_new(1, 2).unwrap(), nonce: 1 },
209 data: DispatchMessageData { payload: Err(codec::Error::from("test")) },
210 }
211 }
212
213 fn valid_message() -> DispatchMessage<Vec<u8>, TestLaneIdType> {
214 DispatchMessage {
215 key: MessageKey { lane_id: TestLaneIdType::try_new(1, 2).unwrap(), nonce: 1 },
216 data: DispatchMessageData { payload: Ok(vec![42]) },
217 }
218 }
219
220 #[test]
221 fn dispatcher_is_inactive_when_channel_with_target_chain_is_congested() {
222 run_test_with_opened_bridge(|| {
223 TestLocalXcmChannelManager::make_congested();
224 assert!(!XcmOverBridge::is_active(bridge().1));
225 });
226 }
227
228 #[test]
229 fn dispatcher_is_active_when_channel_with_target_chain_is_not_congested() {
230 run_test_with_opened_bridge(|| {
231 assert!(XcmOverBridge::is_active(bridge().1));
232 });
233 }
234
235 #[test]
236 fn dispatch_weight_is_zero_if_we_have_failed_to_decode_message() {
237 run_test(|| {
238 assert_eq!(XcmOverBridge::dispatch_weight(&mut invalid_message()), Weight::zero());
239 });
240 }
241
242 #[test]
243 fn dispatch_weight_is_non_zero_if_we_have_decoded_message() {
244 run_test(|| {
245 assert_ne!(XcmOverBridge::dispatch_weight(&mut valid_message()), Weight::zero());
246 });
247 }
248
249 #[test]
250 fn message_is_not_dispatched_when_we_have_failed_to_decode_message() {
251 run_test(|| {
252 assert_eq!(
253 XcmOverBridge::dispatch(invalid_message()),
254 MessageDispatchResult {
255 unspent_weight: Weight::zero(),
256 dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload,
257 },
258 );
259 assert!(!TestBlobDispatcher::is_dispatched());
260 });
261 }
262
263 #[test]
264 fn message_is_dispatched_when_we_have_decoded_message() {
265 run_test(|| {
266 assert_eq!(
267 XcmOverBridge::dispatch(valid_message()),
268 MessageDispatchResult {
269 unspent_weight: Weight::zero(),
270 dispatch_level_result: XcmBlobMessageDispatchResult::Dispatched,
271 },
272 );
273 assert!(TestBlobDispatcher::is_dispatched());
274 });
275 }
276}