// 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")),
}
}
}