// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute // This file is part of Pezkuwi. // Pezkuwi 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. // Pezkuwi 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 Pezkuwi. If not, see . //! Tests specific to the bridging primitives use super::mock::*; use crate::{universal_exports::*, WithTopicSource}; use pezframe_support::{parameter_types, traits::Get}; use std::{cell::RefCell, marker::PhantomData}; use xcm::AlwaysLatest; use xcm_executor::{ traits::{export_xcm, validate_export}, XcmExecutor, }; use SendError::*; mod local_para_para; mod local_relay_relay; mod paid_remote_relay_relay; mod remote_para_para; mod remote_para_para_via_relay; mod remote_relay_relay; mod universal_exports; parameter_types! { pub Local: NetworkId = ByGenesis([0; 32]); pub Remote: NetworkId = ByGenesis([1; 32]); pub Price: Assets = Assets::from((Here, 100u128)); pub static UsingTopic: bool = false; } std::thread_local! { static BRIDGE_TRAFFIC: RefCell>> = RefCell::new(Vec::new()); } fn maybe_with_topic(f: impl Fn()) { UsingTopic::set(false); f(); UsingTopic::set(true); f(); } fn xcm_with_topic(topic: XcmHash, mut xcm: Vec>) -> Xcm { if UsingTopic::get() { xcm.push(SetTopic(topic)); } Xcm(xcm) } fn fake_id() -> XcmHash { [255; 32] } fn test_weight(mut count: u64) -> Weight { if UsingTopic::get() { count += 1; } Weight::from_parts(count * 10, count * 10) } fn maybe_forward_id_for(topic: &XcmHash) -> XcmHash { match UsingTopic::get() { true => *topic, false => fake_id(), } } enum TestTicket { Basic(T::Ticket), Topic( as SendXcm>::Ticket), } struct TestTopic(PhantomData); impl SendXcm for TestTopic { type Ticket = TestTicket; fn deliver(ticket: Self::Ticket) -> core::result::Result { match ticket { TestTicket::Basic(t) => R::deliver(t), TestTicket::Topic(t) => WithTopicSource::::deliver(t), } } fn validate( destination: &mut Option, message: &mut Option>, ) -> SendResult { Ok(if UsingTopic::get() { let (t, a) = WithTopicSource::::validate(destination, message)?; (TestTicket::Topic(t), a) } else { let (t, a) = R::validate(destination, message)?; (TestTicket::Basic(t), a) }) } } struct TestBridge(PhantomData); impl TestBridge { fn service() -> u64 { BRIDGE_TRAFFIC .with(|t| t.borrow_mut().drain(..).map(|b| D::dispatch_blob(b).map_or(0, |()| 1)).sum()) } } impl HaulBlob for TestBridge { fn haul_blob(blob: Vec) -> Result<(), HaulBlobError> { BRIDGE_TRAFFIC.with(|t| t.borrow_mut().push(blob)); Ok(()) } } std::thread_local! { static REMOTE_INCOMING_XCM: RefCell)>> = RefCell::new(Vec::new()); } struct TestRemoteIncomingRouter; impl SendXcm for TestRemoteIncomingRouter { type Ticket = (Location, Xcm<()>); fn validate( dest: &mut Option, msg: &mut Option>, ) -> SendResult<(Location, Xcm<()>)> { let pair = (dest.take().unwrap(), msg.take().unwrap()); Ok((pair, Assets::new())) } fn deliver(pair: (Location, Xcm<()>)) -> Result { let hash = fake_id(); REMOTE_INCOMING_XCM.with(|q| q.borrow_mut().push(pair)); Ok(hash) } } fn take_received_remote_messages() -> Vec<(Location, Xcm<()>)> { REMOTE_INCOMING_XCM.with(|r| r.replace(vec![])) } /// This is a dummy router which accepts messages destined for `Remote` from `Local` /// and then executes them for free in a context simulated to be like that of our `Remote`. struct UnpaidExecutingRouter( PhantomData<(Local, Remote, RemoteExporter)>, ); fn price( n: NetworkId, c: u32, s: &InteriorLocation, d: &InteriorLocation, m: &Xcm<()>, ) -> Result { Ok(validate_export::(n, c, s.clone(), d.clone(), m.clone())?.1) } fn deliver( n: NetworkId, c: u32, s: InteriorLocation, d: InteriorLocation, m: Xcm<()>, ) -> Result { export_xcm::(n, c, s, d, m).map(|(hash, _)| hash) } #[derive(Eq, PartialEq, Clone, Debug)] pub struct LogEntry { local: Junctions, remote: Junctions, id: XcmHash, message: Xcm<()>, outcome: Outcome, paid: bool, } parameter_types! { pub static RoutingLog: Vec = vec![]; } impl, Remote: Get, RemoteExporter: ExportXcm> SendXcm for UnpaidExecutingRouter { type Ticket = Xcm<()>; fn validate( destination: &mut Option, message: &mut Option>, ) -> SendResult> { let expect_dest = Remote::get().relative_to(&Local::get()); if destination.as_ref().ok_or(MissingArgument)? != &expect_dest { return Err(NotApplicable); } let message = message.take().ok_or(MissingArgument)?; Ok((message, Assets::new())) } fn deliver(message: Xcm<()>) -> Result { // We now pretend that the message was delivered from `Local` to `Remote`, and execute // so we need to ensure that the `TestConfig` is set up properly for executing as // though it is `Remote`. ExecutorUniversalLocation::set(Remote::get()); let origin = Local::get().relative_to(&Remote::get()); AllowUnpaidFrom::set(vec![origin.clone()]); set_exporter_override(price::, deliver::); // Then we execute it: let mut id = fake_id(); let outcome = XcmExecutor::::prepare_and_execute( origin, message.clone().into(), &mut id, Weight::from_parts(2_000_000_000_000, 2_000_000_000_000), Weight::zero(), ); let local = Local::get(); let remote = Remote::get(); let entry = LogEntry { local, remote, id, message, outcome: outcome.clone(), paid: false }; RoutingLog::mutate(|l| l.push(entry)); match outcome { Outcome::Complete { .. } => Ok(id), Outcome::Incomplete { .. } => Err(Transport("Error executing")), Outcome::Error(_) => Err(Transport("Unable to execute")), } } } /// This is a dummy router which accepts messages destined for `Remote` from `Local` /// and then executes them in a context simulated to be like that of our `Remote`. Payment is /// needed. struct ExecutingRouter(PhantomData<(Local, Remote, RemoteExporter)>); impl, Remote: Get, RemoteExporter: ExportXcm> SendXcm for ExecutingRouter { type Ticket = Xcm<()>; fn validate( destination: &mut Option, message: &mut Option>, ) -> SendResult> { let expect_dest = Remote::get().relative_to(&Local::get()); if destination.as_ref().ok_or(MissingArgument)? != &expect_dest { return Err(NotApplicable); } let message = message.take().ok_or(MissingArgument)?; Ok((message, Assets::new())) } fn deliver(message: Xcm<()>) -> Result { // We now pretend that the message was delivered from `Local` to `Remote`, and execute // so we need to ensure that the `TestConfig` is set up properly for executing as // though it is `Remote`. ExecutorUniversalLocation::set(Remote::get()); let origin = Local::get().relative_to(&Remote::get()); AllowPaidFrom::set(vec![origin.clone()]); set_exporter_override(price::, deliver::); // Then we execute it: let mut id = fake_id(); let outcome = XcmExecutor::::prepare_and_execute( origin, message.clone().into(), &mut id, Weight::from_parts(2_000_000_000_000, 2_000_000_000_000), Weight::zero(), ); let local = Local::get(); let remote = Remote::get(); let entry = LogEntry { local, remote, id, message, outcome: outcome.clone(), paid: true }; RoutingLog::mutate(|l| l.push(entry)); match outcome { Outcome::Complete { .. } => Ok(id), Outcome::Incomplete { .. } => Err(Transport("Error executing")), Outcome::Error(_) => Err(Transport("Unable to execute")), } } }