Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
PayOverXcm: Add test skeleton
Browse files Browse the repository at this point in the history
  • Loading branch information
franciscoaguirre committed Jun 5, 2023
1 parent cf0ecdd commit aeaaf22
Show file tree
Hide file tree
Showing 7 changed files with 491 additions and 2 deletions.
2 changes: 1 addition & 1 deletion xcm/pallet-xcm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1135,7 +1135,7 @@ impl<T: Config> QueryHandler for Pallet<T> {
/// Attempt to create a new query ID and register it as a query that is yet to respond.
fn new_query(
responder: impl Into<MultiLocation>,
timeout: T::BlockNumber,
timeout: Self::BlockNumber,
match_querier: impl Into<MultiLocation>,
) -> Self::QueryId {
Self::do_new_query(responder, None, timeout, match_querier).into()
Expand Down
59 changes: 58 additions & 1 deletion xcm/xcm-builder/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub use sp_std::{
marker::PhantomData,
};
pub use xcm::latest::{prelude::*, Weight};
use xcm_executor::traits::Properties;
use xcm_executor::traits::{Properties, QueryHandler, QueryResponseStatus};
pub use xcm_executor::{
traits::{
AssetExchange, AssetLock, CheckSuspension, ConvertOrigin, Enact, ExportXcm, FeeManager,
Expand Down Expand Up @@ -410,6 +410,63 @@ pub fn response(query_id: u64) -> Option<Response> {
})
}

// TODO: Add counter for query ids
// TODO: Add more information in QUERIES
pub struct TestQueryHandler<T, BlockNumber>(core::marker::PhantomData<(T, BlockNumber)>);
impl<T: Config, BlockNumber: sp_runtime::traits::Zero> QueryHandler
for TestQueryHandler<T, BlockNumber>
{
type QueryId = u64;
type BlockNumber = BlockNumber;
type Error = XcmError;
type UniversalLocation = T::UniversalLocation;

fn new_query(
responder: impl Into<MultiLocation>,
timeout: Self::BlockNumber,
match_querier: impl Into<MultiLocation>,
) -> Self::QueryId {
let query_id = 1;
expect_response(query_id, responder.into());
query_id
}

fn report_outcome(
message: &mut Xcm<()>,
responder: impl Into<MultiLocation>,
timeout: Self::BlockNumber,
) -> Result<Self::QueryId, Self::Error> {
let responder = responder.into();
let destination = Self::UniversalLocation::get()
.invert_target(&responder)
.map_err(|()| XcmError::LocationNotInvertible)?;
let query_id = Self::new_query(responder, timeout, Here);
let response_info = QueryResponseInfo { destination, query_id, max_weight: Weight::zero() };
let report_error = Xcm(vec![ReportError(response_info)]);
message.0.insert(0, SetAppendix(report_error));
Ok(query_id)
}

fn take_response(query_id: Self::QueryId) -> QueryResponseStatus<Self::BlockNumber> {
QUERIES
.with(|q| {
q.borrow().get(&query_id).and_then(|v| match v {
ResponseSlot::Received(r) => Some(QueryResponseStatus::Ready {
response: r.clone(),
at: Self::BlockNumber::zero(),
}),
_ => Some(QueryResponseStatus::NotFound),
})
})
.unwrap_or(QueryResponseStatus::NotFound)
}

// #[cfg(feature = "runtime-benchmarks")]
// fn expect_response(id: Self::QueryId, response: xcm::latest::Result) {
// // Unneeded since this is only test
// }
}

parameter_types! {
pub static ExecutorUniversalLocation: InteriorMultiLocation
= (ByGenesis([0; 32]), Parachain(42)).into();
Expand Down
1 change: 1 addition & 0 deletions xcm/xcm-builder/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ mod bridging;
mod expecting;
mod locking;
mod origins;
mod pay;
mod querying;
mod transacting;
mod version_subscriptions;
Expand Down
69 changes: 69 additions & 0 deletions xcm/xcm-builder/src/tests/pay.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Polkadot.

// Polkadot 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.

// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.

use super::*;
use frame_support::{assert_ok, traits::tokens::Pay};
use sp_runtime::traits::Convert;

type BlockNumber = u64;
type AccountId = u64;

parameter_types! {
pub Interior: InteriorMultiLocation = AccountIndex64 { index: 3u64, network: None }.into();
pub Timeout: BlockNumber = 5; // 5 blocks
pub Beneficiary: AccountId = 5;
}

#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq)]
pub struct AssetKind {
destination: xcm::latest::MultiLocation,
asset_id: xcm::latest::AssetId,
}

pub struct LocatableAssetKindConverter;
impl sp_runtime::traits::Convert<AssetKind, LocatableAssetId> for LocatableAssetKindConverter {
fn convert(value: AssetKind) -> LocatableAssetId {
LocatableAssetId { asset_id: value.asset_id, location: value.destination }
}
}

pub struct AliasesIntoAccountIndex64;
impl<'a> sp_runtime::traits::Convert<&'a AccountId, MultiLocation> for AliasesIntoAccountIndex64 {
fn convert(who: &AccountId) -> MultiLocation {
AccountIndex64 { network: None, index: who.clone().into() }.into()
}
}

#[test]
fn pay_over_xcm_works() {
AllowExplicitUnpaidFrom::set(vec![X1(Parachain(1)).into()]);
add_asset(AliasesIntoAccountIndex64::convert(&3u64), (Here, 1000u128));
let who = 5u64;
let asset_kind =
AssetKind { destination: (Parent, Parachain(2)).into(), asset_id: Here.into() };
let amount = 1000u128;
assert_ok!(PayOverXcm::<
Interior,
TestMessageSender,
TestQueryHandler<TestConfig, BlockNumber>,
Timeout,
AccountId,
AssetKind,
LocatableAssetKindConverter,
AliasesIntoAccountIndex64,
>::pay(&who, asset_kind, amount));
dbg!(&sent_xcm());
}
25 changes: 25 additions & 0 deletions xcm/xcm-builder/src/tests/pay/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
mod pay;
mod salary;

/// Type representing both a location and an asset that is held at that location.
/// The id of the held asset is relative to the location where it is being held.
#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq)]
pub struct AssetKind {
destination: MultiLocation,
asset_id: AssetId,
}

pub struct LocatableAssetKindConverter;
impl sp_runtime::traits::Convert<AssetKind, LocatableAssetId> for LocatableAssetKindConverter {
fn convert(value: AssetKind) -> LocatableAssetId {
LocatableAssetId { asset_id: value.asset_id, location: value.destination }
}
}

/// Simple converter to turn a u64 into a MultiLocation using the AccountIndex64 junction and no parents
pub struct AliasesIntoAccountIndex64;
impl<'a> sp_runtime::traits::Convert<&'a AccountId, MultiLocation> for AliasesIntoAccountIndex64 {
fn convert(who: &AccountId) -> MultiLocation {
AccountIndex64 { network: None, index: who.clone().into() }.into()
}
}
107 changes: 107 additions & 0 deletions xcm/xcm-builder/src/tests/pay/pay.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Polkadot.

// Polkadot 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.

// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.

//! Tests for making sure `PayOverXcm::pay` generates the correct message and sends it to the correct destination

use super::{super::*, *};
use frame_support::{assert_ok, traits::tokens::Pay};

type BlockNumber = u64;
type AccountId = u64;

parameter_types! {
pub InteriorAccount: InteriorMultiLocation = AccountIndex64 { index: 3u64, network: None }.into();
pub InteriorBody: InteriorMultiLocation = Plurality { id: BodyId::Treasury, part: BodyPart::Voice }.into();
pub Timeout: BlockNumber = 5; // 5 blocks
pub Beneficiary: AccountId = 5;
}

/// Scenario:
/// Account #3 on the local chain, Parachain(42), controls an amount of funds on Parachain(2).
/// PayOverXcm::pay creates the correct message for account #3 to pay another account, account #5, on Parachain(2), remotely, in its native token.
#[test]
fn pay_over_xcm_works() {
let who = 5u64;
let asset_kind =
AssetKind { destination: (Parent, Parachain(2)).into(), asset_id: Here.into() };
let amount = 1000u128;

assert_ok!(PayOverXcm::<
InteriorAccount,
TestMessageSender,
TestQueryHandler<TestConfig, BlockNumber>,
Timeout,
AccountId,
AssetKind,
LocatableAssetKindConverter,
AliasesIntoAccountIndex64,
>::pay(&who, asset_kind, amount));

let expected_message = Xcm(vec![
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
SetAppendix(Xcm(vec![ReportError(QueryResponseInfo {
destination: (Parent, Parachain(42)).into(),
query_id: 1,
max_weight: Weight::zero(),
})])),
DescendOrigin(AccountIndex64 { index: 3, network: None }.into()),
TransferAsset {
assets: (Here, 1000u128).into(),
beneficiary: AccountIndex64 { index: 5, network: None }.into(),
},
]);

let expected_hash = fake_message_hash(&expected_message);
assert_eq!(sent_xcm(), vec![((Parent, Parachain(2)).into(), expected_message, expected_hash)]);
}

/// Scenario:
/// A pluralistic body, a Treasury, on the local chain, Parachain(42), controls an amount of funds on Parachain(2).
/// PayOverXcm::pay creates the correct message for the treasury to pay another account, account #7, on Parachain(2), remotely, in the relay's token.
#[test]
fn pay_over_xcm_governance_body() {
let who = 7u64;
let asset_kind =
AssetKind { destination: (Parent, Parachain(2)).into(), asset_id: Parent.into() };
let amount = 500u128;

assert_ok!(PayOverXcm::<
InteriorBody,
TestMessageSender,
TestQueryHandler<TestConfig, BlockNumber>,
Timeout,
AccountId,
AssetKind,
LocatableAssetKindConverter,
AliasesIntoAccountIndex64,
>::pay(&who, asset_kind, amount));

let expected_message = Xcm(vec![
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
SetAppendix(Xcm(vec![ReportError(QueryResponseInfo {
destination: (Parent, Parachain(42)).into(),
query_id: 1,
max_weight: Weight::zero(),
})])),
DescendOrigin(Plurality { id: BodyId::Treasury, part: BodyPart::Voice }.into()),
TransferAsset {
assets: (Parent, 500u128).into(),
beneficiary: AccountIndex64 { index: 7, network: None }.into(),
},
]);
let expected_hash = fake_message_hash(&expected_message);
assert_eq!(sent_xcm(), vec![((Parent, Parachain(2)).into(), expected_message, expected_hash)]);
}
Loading

0 comments on commit aeaaf22

Please sign in to comment.