// Copyright 2019-2021 Parity Technologies (UK) Ltd. // This file is part of Parity Bridges Common. // Parity Bridges Common is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity Bridges Common is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . //! The code that allows to use the pallet (`pallet-xcm-bridge-hub`) as XCM message //! exporter at the sending bridge hub. Internally, it just enqueues outbound blob //! in the messages pallet queue. //! //! This code is executed at the source bridge hub. use crate::{Config, Pallet, LOG_TARGET}; use bp_messages::source_chain::MessagesBridge; use bp_xcm_bridge_hub::XcmAsPlainPayload; use bridge_runtime_common::messages_xcm_extension::{LocalXcmQueueManager, SenderAndLane}; use pallet_bridge_messages::{Config as BridgeMessagesConfig, Pallet as BridgeMessagesPallet}; use xcm::prelude::*; use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter}; use xcm_executor::traits::ExportXcm; /// An easy way to access `HaulBlobExporter`. pub type PalletAsHaulBlobExporter = HaulBlobExporter< DummyHaulBlob, >::BridgedNetwork, >::DestinationVersion, >::MessageExportPrice, >; /// An easy way to access associated messages pallet. type MessagesPallet = BridgeMessagesPallet>::BridgeMessagesPalletInstance>; impl, I: 'static> ExportXcm for Pallet where T: BridgeMessagesConfig, { type Ticket = ( SenderAndLane, as MessagesBridge>::SendMessageArgs, XcmHash, ); fn validate( network: NetworkId, channel: u32, universal_source: &mut Option, destination: &mut Option, message: &mut Option>, ) -> Result<(Self::Ticket, Assets), SendError> { // Find supported lane_id. let sender_and_lane = Self::lane_for( universal_source.as_ref().ok_or(SendError::MissingArgument)?, (&network, destination.as_ref().ok_or(SendError::MissingArgument)?), ) .ok_or(SendError::NotApplicable)?; // check if we are able to route the message. We use existing `HaulBlobExporter` for that. // It will make all required changes and will encode message properly, so that the // `DispatchBlob` at the bridged bridge hub will be able to decode it let ((blob, id), price) = PalletAsHaulBlobExporter::::validate( network, channel, universal_source, destination, message, )?; let bridge_message = MessagesPallet::::validate_message(sender_and_lane.lane, &blob) .map_err(|e| { log::debug!( target: LOG_TARGET, "XCM message {:?} cannot be exported because of bridge error {:?} on bridge {:?}", id, e, sender_and_lane.lane, ); SendError::Transport("BridgeValidateError") })?; Ok(((sender_and_lane, bridge_message, id), price)) } fn deliver((sender_and_lane, bridge_message, id): Self::Ticket) -> Result { let lane_id = sender_and_lane.lane; let artifacts = MessagesPallet::::send_message(bridge_message); log::info!( target: LOG_TARGET, "XCM message {:?} has been enqueued at bridge {:?} with nonce {}", id, lane_id, artifacts.nonce, ); // notify XCM queue manager about updated lane state LocalXcmQueueManager::::on_bridge_message_enqueued( &sender_and_lane, artifacts.enqueued_messages, ); Ok(id) } } /// Dummy implementation of the `HaulBlob` trait that is never called. /// /// We are using `HaulBlobExporter`, which requires `HaulBlob` implementation. It assumes that /// there's a single channel between two bridge hubs - `HaulBlob` only accepts the blob and nothing /// else. But bridge messages pallet may have a dedicated channel (lane) for every pair of bridged /// chains. So we are using our own `ExportXcm` implementation, but to utilize `HaulBlobExporter` we /// still need this `DummyHaulBlob`. pub struct DummyHaulBlob; impl HaulBlob for DummyHaulBlob { fn haul_blob(_blob: XcmAsPlainPayload) -> Result<(), HaulBlobError> { Err(HaulBlobError::Transport("DummyHaulBlob")) } } #[cfg(test)] mod tests { use super::*; use crate::mock::*; use frame_support::assert_ok; use xcm_executor::traits::export_xcm; fn universal_source() -> InteriorLocation { [GlobalConsensus(RelayNetwork::get()), Parachain(SIBLING_ASSET_HUB_ID)].into() } fn universal_destination() -> InteriorLocation { BridgedDestination::get() } #[test] fn export_works() { run_test(|| { assert_ok!(export_xcm::( BridgedRelayNetwork::get(), 0, universal_source(), universal_destination(), vec![Instruction::ClearOrigin].into(), )); }) } #[test] fn export_fails_if_argument_is_missing() { run_test(|| { assert_eq!( XcmOverBridge::validate( BridgedRelayNetwork::get(), 0, &mut None, &mut Some(universal_destination()), &mut Some(Vec::new().into()), ), Err(SendError::MissingArgument), ); assert_eq!( XcmOverBridge::validate( BridgedRelayNetwork::get(), 0, &mut Some(universal_source()), &mut None, &mut Some(Vec::new().into()), ), Err(SendError::MissingArgument), ); }) } #[test] fn exporter_computes_correct_lane_id() { run_test(|| { let expected_lane_id = TEST_LANE_ID; assert_eq!( XcmOverBridge::validate( BridgedRelayNetwork::get(), 0, &mut Some(universal_source()), &mut Some(universal_destination()), &mut Some(Vec::new().into()), ) .unwrap() .0 .0 .lane, expected_lane_id, ); }) } }