b6d35f6faf
Updated 4763 files with dual copyright: - Parity Technologies (UK) Ltd. - Dijital Kurdistan Tech Institute
282 lines
8.4 KiB
Rust
282 lines
8.4 KiB
Rust
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
//! 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<Vec<Vec<u8>>> = RefCell::new(Vec::new());
|
|
}
|
|
|
|
fn maybe_with_topic(f: impl Fn()) {
|
|
UsingTopic::set(false);
|
|
f();
|
|
UsingTopic::set(true);
|
|
f();
|
|
}
|
|
|
|
fn xcm_with_topic<T>(topic: XcmHash, mut xcm: Vec<Instruction<T>>) -> Xcm<T> {
|
|
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<T: SendXcm> {
|
|
Basic(T::Ticket),
|
|
Topic(<WithTopicSource<T, ()> as SendXcm>::Ticket),
|
|
}
|
|
|
|
struct TestTopic<R>(PhantomData<R>);
|
|
impl<R: SendXcm> SendXcm for TestTopic<R> {
|
|
type Ticket = TestTicket<R>;
|
|
fn deliver(ticket: Self::Ticket) -> core::result::Result<XcmHash, SendError> {
|
|
match ticket {
|
|
TestTicket::Basic(t) => R::deliver(t),
|
|
TestTicket::Topic(t) => WithTopicSource::<R, ()>::deliver(t),
|
|
}
|
|
}
|
|
fn validate(
|
|
destination: &mut Option<Location>,
|
|
message: &mut Option<Xcm<()>>,
|
|
) -> SendResult<Self::Ticket> {
|
|
Ok(if UsingTopic::get() {
|
|
let (t, a) = WithTopicSource::<R, ()>::validate(destination, message)?;
|
|
(TestTicket::Topic(t), a)
|
|
} else {
|
|
let (t, a) = R::validate(destination, message)?;
|
|
(TestTicket::Basic(t), a)
|
|
})
|
|
}
|
|
}
|
|
|
|
struct TestBridge<D>(PhantomData<D>);
|
|
impl<D: DispatchBlob> TestBridge<D> {
|
|
fn service() -> u64 {
|
|
BRIDGE_TRAFFIC
|
|
.with(|t| t.borrow_mut().drain(..).map(|b| D::dispatch_blob(b).map_or(0, |()| 1)).sum())
|
|
}
|
|
}
|
|
impl<D: DispatchBlob> HaulBlob for TestBridge<D> {
|
|
fn haul_blob(blob: Vec<u8>) -> Result<(), HaulBlobError> {
|
|
BRIDGE_TRAFFIC.with(|t| t.borrow_mut().push(blob));
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
std::thread_local! {
|
|
static REMOTE_INCOMING_XCM: RefCell<Vec<(Location, Xcm<()>)>> = RefCell::new(Vec::new());
|
|
}
|
|
struct TestRemoteIncomingRouter;
|
|
impl SendXcm for TestRemoteIncomingRouter {
|
|
type Ticket = (Location, Xcm<()>);
|
|
fn validate(
|
|
dest: &mut Option<Location>,
|
|
msg: &mut Option<Xcm<()>>,
|
|
) -> SendResult<(Location, Xcm<()>)> {
|
|
let pair = (dest.take().unwrap(), msg.take().unwrap());
|
|
Ok((pair, Assets::new()))
|
|
}
|
|
fn deliver(pair: (Location, Xcm<()>)) -> Result<XcmHash, SendError> {
|
|
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<Local, Remote, RemoteExporter>(
|
|
PhantomData<(Local, Remote, RemoteExporter)>,
|
|
);
|
|
|
|
fn price<RemoteExporter: ExportXcm>(
|
|
n: NetworkId,
|
|
c: u32,
|
|
s: &InteriorLocation,
|
|
d: &InteriorLocation,
|
|
m: &Xcm<()>,
|
|
) -> Result<Assets, SendError> {
|
|
Ok(validate_export::<RemoteExporter>(n, c, s.clone(), d.clone(), m.clone())?.1)
|
|
}
|
|
|
|
fn deliver<RemoteExporter: ExportXcm>(
|
|
n: NetworkId,
|
|
c: u32,
|
|
s: InteriorLocation,
|
|
d: InteriorLocation,
|
|
m: Xcm<()>,
|
|
) -> Result<XcmHash, SendError> {
|
|
export_xcm::<RemoteExporter>(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<LogEntry> = vec![];
|
|
}
|
|
|
|
impl<Local: Get<Junctions>, Remote: Get<Junctions>, RemoteExporter: ExportXcm> SendXcm
|
|
for UnpaidExecutingRouter<Local, Remote, RemoteExporter>
|
|
{
|
|
type Ticket = Xcm<()>;
|
|
|
|
fn validate(
|
|
destination: &mut Option<Location>,
|
|
message: &mut Option<Xcm<()>>,
|
|
) -> SendResult<Xcm<()>> {
|
|
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<XcmHash, SendError> {
|
|
// 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::<RemoteExporter>, deliver::<RemoteExporter>);
|
|
// Then we execute it:
|
|
let mut id = fake_id();
|
|
let outcome = XcmExecutor::<TestConfig>::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<Local, Remote, RemoteExporter>(PhantomData<(Local, Remote, RemoteExporter)>);
|
|
impl<Local: Get<Junctions>, Remote: Get<Junctions>, RemoteExporter: ExportXcm> SendXcm
|
|
for ExecutingRouter<Local, Remote, RemoteExporter>
|
|
{
|
|
type Ticket = Xcm<()>;
|
|
|
|
fn validate(
|
|
destination: &mut Option<Location>,
|
|
message: &mut Option<Xcm<()>>,
|
|
) -> SendResult<Xcm<()>> {
|
|
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<XcmHash, SendError> {
|
|
// 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::<RemoteExporter>, deliver::<RemoteExporter>);
|
|
// Then we execute it:
|
|
let mut id = fake_id();
|
|
let outcome = XcmExecutor::<TestConfig>::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")),
|
|
}
|
|
}
|
|
}
|