feat: initialize Kurdistan SDK - independent fork of Polkadot SDK
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
[package]
|
||||
name = "pallet-xcm-precompiles"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
description = "Provides precompiles for `pallet-xcm`"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
codec = { features = ["derive"], workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
frame-support = { workspace = true }
|
||||
pallet-revive = { workspace = true }
|
||||
pallet-xcm = { workspace = true }
|
||||
|
||||
xcm = { workspace = true }
|
||||
xcm-executor = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
frame-system = { workspace = true, default-features = true }
|
||||
pallet-assets = { workspace = true, default-features = true }
|
||||
pallet-balances = { workspace = true, default-features = true }
|
||||
pallet-timestamp = { workspace = true, default-features = true }
|
||||
pezkuwi-teyrchain-primitives = { workspace = true, default-features = true }
|
||||
scale-info = { workspace = true, default-features = true }
|
||||
sp-io = { workspace = true, default-features = true }
|
||||
sp-runtime = { workspace = true, default-features = true }
|
||||
xcm-builder = { workspace = true, default-features = true }
|
||||
xcm-simulator = { workspace = true, default-features = true }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"codec/std",
|
||||
"frame-support/std",
|
||||
"frame-system/std",
|
||||
"pallet-assets/std",
|
||||
"pallet-balances/std",
|
||||
"pallet-revive/std",
|
||||
"pallet-timestamp/std",
|
||||
"pallet-xcm/std",
|
||||
"pezkuwi-teyrchain-primitives/std",
|
||||
"scale-info/std",
|
||||
"sp-io/std",
|
||||
"sp-runtime/std",
|
||||
"tracing/std",
|
||||
"xcm-builder/std",
|
||||
"xcm-executor/std",
|
||||
"xcm/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
"pallet-assets/runtime-benchmarks",
|
||||
"pallet-balances/runtime-benchmarks",
|
||||
"pallet-revive/runtime-benchmarks",
|
||||
"pallet-timestamp/runtime-benchmarks",
|
||||
"pallet-xcm/runtime-benchmarks",
|
||||
"pezkuwi-teyrchain-primitives/runtime-benchmarks",
|
||||
"sp-io/runtime-benchmarks",
|
||||
"sp-runtime/runtime-benchmarks",
|
||||
"xcm-builder/runtime-benchmarks",
|
||||
"xcm-executor/runtime-benchmarks",
|
||||
"xcm-simulator/runtime-benchmarks",
|
||||
"xcm/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = [
|
||||
"frame-support/try-runtime",
|
||||
"frame-system/try-runtime",
|
||||
"pallet-assets/try-runtime",
|
||||
"pallet-balances/try-runtime",
|
||||
"pallet-revive/try-runtime",
|
||||
"pallet-timestamp/try-runtime",
|
||||
"pallet-xcm/try-runtime",
|
||||
"sp-runtime/try-runtime",
|
||||
]
|
||||
@@ -0,0 +1,41 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
/// @dev The on-chain address of the XCM (Cross-Consensus Messaging) precompile.
|
||||
address constant XCM_PRECOMPILE_ADDRESS = address(0xA0000);
|
||||
|
||||
/// @title XCM Precompile Interface
|
||||
/// @notice A low-level interface for interacting with `pallet_xcm`.
|
||||
/// It forwards calls directly to the corresponding dispatchable functions,
|
||||
/// providing access to XCM execution and message passing.
|
||||
/// @dev Documentation:
|
||||
/// @dev - XCM: https://docs.pezkuwichain.io/develop/interoperability
|
||||
/// @dev - SCALE codec: https://docs.pezkuwichain.io/polkadot-protocol/parachain-basics/data-encoding
|
||||
/// @dev - Weights: https://docs.pezkuwichain.io/polkadot-protocol/parachain-basics/blocks-transactions-fees/fees/#transactions-weights-and-fees
|
||||
interface IXcm {
|
||||
/// @notice Weight v2 used for measurement for an XCM execution
|
||||
struct Weight {
|
||||
/// @custom:property The computational time used to execute some logic based on reference hardware.
|
||||
uint64 refTime;
|
||||
/// @custom:property The size of the proof needed to execute some logic.
|
||||
uint64 proofSize;
|
||||
}
|
||||
|
||||
/// @notice Executes an XCM message locally on the current chain with the caller's origin.
|
||||
/// @dev Internally calls `pallet_xcm::execute`.
|
||||
/// @param message A SCALE-encoded Versioned XCM message.
|
||||
/// @param weight The maximum allowed `Weight` for execution.
|
||||
/// @dev Call @custom:function weighMessage(message) to ensure sufficient weight allocation.
|
||||
function execute(bytes calldata message, Weight calldata weight) external;
|
||||
|
||||
/// @notice Sends an XCM message to another parachain or consensus system.
|
||||
/// @dev Internally calls `pallet_xcm::send`.
|
||||
/// @param destination SCALE-encoded destination MultiLocation.
|
||||
/// @param message SCALE-encoded Versioned XCM message.
|
||||
function send(bytes calldata destination, bytes calldata message) external;
|
||||
|
||||
/// @notice Estimates the `Weight` required to execute a given XCM message.
|
||||
/// @param message SCALE-encoded Versioned XCM message to analyze.
|
||||
/// @return weight Struct containing estimated `refTime` and `proofSize`.
|
||||
function weighMessage(bytes calldata message) external view returns (Weight memory weight);
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Ensure we're `no_std` when compiling for Wasm.
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use codec::{DecodeAll, DecodeLimit};
|
||||
use core::{fmt, marker::PhantomData, num::NonZero};
|
||||
use frame_support::dispatch::RawOrigin;
|
||||
use pallet_revive::{
|
||||
precompiles::{
|
||||
alloy::{self, sol_types::SolValue},
|
||||
AddressMatcher, Error, Ext, Precompile,
|
||||
},
|
||||
DispatchInfo, ExecOrigin as Origin, Weight,
|
||||
};
|
||||
use pallet_xcm::{Config, WeightInfo};
|
||||
use tracing::error;
|
||||
use xcm::{v5, IdentifyVersion, VersionedLocation, VersionedXcm, MAX_XCM_DECODE_DEPTH};
|
||||
use xcm_executor::traits::WeightBounds;
|
||||
|
||||
alloy::sol!("src/interface/IXcm.sol");
|
||||
use IXcm::IXcmCalls;
|
||||
|
||||
#[cfg(test)]
|
||||
mod mock;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
const LOG_TARGET: &str = "xcm::precompiles";
|
||||
|
||||
fn revert(error: &impl fmt::Debug, message: &str) -> Error {
|
||||
error!(target: LOG_TARGET, ?error, "{}", message);
|
||||
Error::Revert(message.into())
|
||||
}
|
||||
|
||||
// We don't allow XCM versions older than 5.
|
||||
fn ensure_xcm_version<V: IdentifyVersion>(input: &V) -> Result<(), Error> {
|
||||
let version = input.identify_version();
|
||||
if version < v5::VERSION {
|
||||
return Err(Error::Revert("Only XCM version 5 and onwards are supported.".into()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct XcmPrecompile<T>(PhantomData<T>);
|
||||
|
||||
impl<Runtime> Precompile for XcmPrecompile<Runtime>
|
||||
where
|
||||
Runtime: crate::Config + pallet_revive::Config,
|
||||
{
|
||||
type T = Runtime;
|
||||
const MATCHER: AddressMatcher = AddressMatcher::Fixed(NonZero::new(10).unwrap());
|
||||
const HAS_CONTRACT_INFO: bool = false;
|
||||
type Interface = IXcm::IXcmCalls;
|
||||
|
||||
fn call(
|
||||
_address: &[u8; 20],
|
||||
input: &Self::Interface,
|
||||
env: &mut impl Ext<T = Self::T>,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
let origin = env.caller();
|
||||
let frame_origin = match origin {
|
||||
Origin::Root => RawOrigin::Root.into(),
|
||||
Origin::Signed(account_id) => RawOrigin::Signed(account_id.clone()).into(),
|
||||
};
|
||||
|
||||
match input {
|
||||
IXcmCalls::send(_) | IXcmCalls::execute(_) if env.is_read_only() =>
|
||||
Err(Error::Error(pallet_revive::Error::<Self::T>::StateChangeDenied.into())),
|
||||
IXcmCalls::send(IXcm::sendCall { destination, message }) => {
|
||||
let _ = env.charge(<Runtime as Config>::WeightInfo::send())?;
|
||||
|
||||
let final_destination = VersionedLocation::decode_all(&mut &destination[..])
|
||||
.map_err(|error| {
|
||||
revert(&error, "XCM send failed: Invalid destination format")
|
||||
})?;
|
||||
|
||||
ensure_xcm_version(&final_destination)?;
|
||||
|
||||
let final_message = VersionedXcm::<()>::decode_all_with_depth_limit(
|
||||
MAX_XCM_DECODE_DEPTH,
|
||||
&mut &message[..],
|
||||
)
|
||||
.map_err(|error| revert(&error, "XCM send failed: Invalid message format"))?;
|
||||
|
||||
ensure_xcm_version(&final_message)?;
|
||||
|
||||
pallet_xcm::Pallet::<Runtime>::send(
|
||||
frame_origin,
|
||||
final_destination.into(),
|
||||
final_message.into(),
|
||||
)
|
||||
.map(|_| Vec::new())
|
||||
.map_err(|error| {
|
||||
revert(
|
||||
&error,
|
||||
"XCM send failed: destination or message format may be incompatible",
|
||||
)
|
||||
})
|
||||
},
|
||||
IXcmCalls::execute(IXcm::executeCall { message, weight }) => {
|
||||
let max_weight = Weight::from_parts(weight.refTime, weight.proofSize);
|
||||
let weight_to_charge =
|
||||
max_weight.saturating_add(<Runtime as Config>::WeightInfo::execute());
|
||||
let charged_amount = env.charge(weight_to_charge)?;
|
||||
|
||||
let final_message = VersionedXcm::decode_all_with_depth_limit(
|
||||
MAX_XCM_DECODE_DEPTH,
|
||||
&mut &message[..],
|
||||
)
|
||||
.map_err(|error| revert(&error, "XCM execute failed: Invalid message format"))?;
|
||||
|
||||
ensure_xcm_version(&final_message)?;
|
||||
|
||||
let result = pallet_xcm::Pallet::<Runtime>::execute(
|
||||
frame_origin,
|
||||
final_message.into(),
|
||||
max_weight,
|
||||
);
|
||||
|
||||
let pre = DispatchInfo {
|
||||
call_weight: weight_to_charge,
|
||||
extension_weight: Weight::zero(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Adjust gas using actual weight or fallback to initially charged weight
|
||||
let actual_weight = frame_support::dispatch::extract_actual_weight(&result, &pre);
|
||||
env.adjust_gas(charged_amount, actual_weight);
|
||||
|
||||
result.map(|_| Vec::new()).map_err(|error| {
|
||||
revert(
|
||||
&error,
|
||||
"XCM execute failed: message may be invalid or execution constraints not satisfied"
|
||||
)
|
||||
})
|
||||
},
|
||||
IXcmCalls::weighMessage(IXcm::weighMessageCall { message }) => {
|
||||
let _ = env.charge(<Runtime as Config>::WeightInfo::weigh_message())?;
|
||||
|
||||
let converted_message = VersionedXcm::decode_all_with_depth_limit(
|
||||
MAX_XCM_DECODE_DEPTH,
|
||||
&mut &message[..],
|
||||
)
|
||||
.map_err(|error| revert(&error, "XCM weightMessage: Invalid message format"))?;
|
||||
|
||||
ensure_xcm_version(&converted_message)?;
|
||||
|
||||
let mut final_message = converted_message.try_into().map_err(|error| {
|
||||
revert(&error, "XCM weightMessage: Conversion to Xcm failed")
|
||||
})?;
|
||||
|
||||
let weight = <<Runtime>::Weigher>::weight(&mut final_message, Weight::MAX)
|
||||
.map_err(|error| {
|
||||
revert(&error, "XCM weightMessage: Failed to calculate weight")
|
||||
})?;
|
||||
|
||||
let final_weight =
|
||||
IXcm::Weight { proofSize: weight.proof_size(), refTime: weight.ref_time() };
|
||||
|
||||
Ok(final_weight.abi_encode())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pub use core::cell::RefCell;
|
||||
use frame_support::{
|
||||
derive_impl, parameter_types,
|
||||
traits::{
|
||||
fungible::HoldConsideration, AsEnsureOriginWithArg, ConstU32, Equals, Everything,
|
||||
EverythingBut, Footprint, Nothing,
|
||||
},
|
||||
weights::Weight,
|
||||
};
|
||||
use frame_system::EnsureRoot;
|
||||
use pezkuwi_teyrchain_primitives::primitives::Id as ParaId;
|
||||
use sp_runtime::{
|
||||
traits::{Convert, IdentityLookup},
|
||||
AccountId32, BuildStorage,
|
||||
};
|
||||
use xcm::prelude::*;
|
||||
use xcm_builder::{
|
||||
AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
|
||||
AllowTopLevelPaidExecutionFrom, ChildTeyrchainConvertsVia, DescribeAllTerminal,
|
||||
EnsureDecodableXcm, FixedWeightBounds, FungibleAdapter, FungiblesAdapter, HashedDescription,
|
||||
IsConcrete, MatchedConvertedConcreteId, NoChecking, TakeWeightCredit,
|
||||
};
|
||||
use xcm_executor::{
|
||||
traits::{Identity, JustTry},
|
||||
XcmExecutor,
|
||||
};
|
||||
use xcm_simulator::helpers::derive_topic_id;
|
||||
|
||||
use crate::XcmPrecompile;
|
||||
|
||||
pub type AccountId = AccountId32;
|
||||
pub type Balance = u128;
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
|
||||
pub const ALICE: AccountId32 = AccountId::new([0u8; 32]);
|
||||
|
||||
parameter_types! {
|
||||
pub const MinimumPeriod: u64 = 1;
|
||||
}
|
||||
|
||||
impl pallet_timestamp::Config for Test {
|
||||
type Moment = u64;
|
||||
type OnTimestampSet = ();
|
||||
type MinimumPeriod = MinimumPeriod;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
frame_support::construct_runtime!(
|
||||
pub enum Test
|
||||
{
|
||||
System: frame_system,
|
||||
AssetsPallet: pallet_assets,
|
||||
Balances: pallet_balances,
|
||||
XcmPallet: pallet_xcm,
|
||||
Revive: pallet_revive,
|
||||
Timestamp: pallet_timestamp,
|
||||
}
|
||||
);
|
||||
|
||||
thread_local! {
|
||||
pub static SENT_XCM: RefCell<Vec<(Location, Xcm<()>)>> = RefCell::new(Vec::new());
|
||||
pub static FAIL_SEND_XCM: RefCell<bool> = RefCell::new(false);
|
||||
}
|
||||
pub(crate) fn sent_xcm() -> Vec<(Location, Xcm<()>)> {
|
||||
SENT_XCM.with(|q| (*q.borrow()).clone())
|
||||
}
|
||||
/// Sender that never returns error.
|
||||
pub struct TestSendXcm;
|
||||
impl SendXcm for TestSendXcm {
|
||||
type Ticket = (Location, Xcm<()>);
|
||||
fn validate(
|
||||
dest: &mut Option<Location>,
|
||||
msg: &mut Option<Xcm<()>>,
|
||||
) -> SendResult<(Location, Xcm<()>)> {
|
||||
if FAIL_SEND_XCM.with(|q| *q.borrow()) {
|
||||
return Err(SendError::Transport("Intentional send failure used in tests"));
|
||||
}
|
||||
let pair = (dest.take().unwrap(), msg.take().unwrap());
|
||||
Ok((pair, Assets::new()))
|
||||
}
|
||||
fn deliver(pair: (Location, Xcm<()>)) -> Result<XcmHash, SendError> {
|
||||
let message = pair.1.clone();
|
||||
if message
|
||||
.iter()
|
||||
.any(|instr| matches!(instr, ExpectError(Some((1, XcmError::Unimplemented)))))
|
||||
{
|
||||
return Err(SendError::Transport("Intentional deliver failure used in tests".into()));
|
||||
}
|
||||
let hash = derive_topic_id(&message);
|
||||
SENT_XCM.with(|q| q.borrow_mut().push(pair));
|
||||
Ok(hash)
|
||||
}
|
||||
}
|
||||
/// Sender that returns error if `X8` junction and stops routing
|
||||
pub struct TestSendXcmErrX8;
|
||||
impl SendXcm for TestSendXcmErrX8 {
|
||||
type Ticket = (Location, Xcm<()>);
|
||||
fn validate(
|
||||
dest: &mut Option<Location>,
|
||||
_: &mut Option<Xcm<()>>,
|
||||
) -> SendResult<(Location, Xcm<()>)> {
|
||||
if dest.as_ref().unwrap().len() == 8 {
|
||||
dest.take();
|
||||
Err(SendError::Transport("Destination location full"))
|
||||
} else {
|
||||
Err(SendError::NotApplicable)
|
||||
}
|
||||
}
|
||||
fn deliver(pair: (Location, Xcm<()>)) -> Result<XcmHash, SendError> {
|
||||
let hash = derive_topic_id(&pair.1);
|
||||
SENT_XCM.with(|q| q.borrow_mut().push(pair));
|
||||
Ok(hash)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
|
||||
impl frame_system::Config for Test {
|
||||
type AccountId = AccountId;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Block = Block;
|
||||
type AccountData = pallet_balances::AccountData<Balance>;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub ExistentialDeposit: Balance = 1;
|
||||
}
|
||||
|
||||
#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
|
||||
impl pallet_balances::Config for Test {
|
||||
type Balance = Balance;
|
||||
type ExistentialDeposit = ExistentialDeposit;
|
||||
type AccountStore = System;
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
/// Simple conversion of `u32` into an `AssetId` for use in benchmarking.
|
||||
pub struct XcmBenchmarkHelper;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
impl pallet_assets::BenchmarkHelper<Location, ()> for XcmBenchmarkHelper {
|
||||
fn create_asset_id_parameter(id: u32) -> Location {
|
||||
Location::new(1, [Teyrchain(id)])
|
||||
}
|
||||
fn create_reserve_id_parameter(_: u32) {}
|
||||
}
|
||||
|
||||
#[derive_impl(pallet_assets::config_preludes::TestDefaultConfig)]
|
||||
impl pallet_assets::Config for Test {
|
||||
type Balance = Balance;
|
||||
type AssetId = Location;
|
||||
type AssetIdParameter = Location;
|
||||
type Currency = Balances;
|
||||
type CreateOrigin = AsEnsureOriginWithArg<frame_system::EnsureSigned<AccountId>>;
|
||||
type ForceOrigin = EnsureRoot<AccountId>;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type BenchmarkHelper = XcmBenchmarkHelper;
|
||||
}
|
||||
|
||||
// This child teyrchain is not configured as trusted reserve or teleport location for any assets.
|
||||
pub const SOME_PARA_ID: u32 = 2009;
|
||||
|
||||
parameter_types! {
|
||||
pub const RelayLocation: Location = Here.into_location();
|
||||
pub const AnyNetwork: Option<NetworkId> = None;
|
||||
pub const BaseXcmWeight: Weight = Weight::from_parts(1_000, 1_000);
|
||||
pub const MaxInstructions: u32 = 100;
|
||||
pub const MaxAssetsIntoHolding: u32 = 64;
|
||||
pub UniversalLocation: InteriorLocation = GlobalConsensus(ByGenesis([0; 32])).into();
|
||||
pub CheckingAccount: AccountId = XcmPallet::check_account();
|
||||
}
|
||||
|
||||
pub type SovereignAccountOf = (
|
||||
ChildTeyrchainConvertsVia<ParaId, AccountId>,
|
||||
AccountId32Aliases<AnyNetwork, AccountId>,
|
||||
HashedDescription<AccountId, DescribeAllTerminal>,
|
||||
);
|
||||
|
||||
pub type ForeignAssetsConvertedConcreteId = MatchedConvertedConcreteId<
|
||||
Location,
|
||||
Balance,
|
||||
// Excludes relay/parent chain currency
|
||||
EverythingBut<(Equals<RelayLocation>,)>,
|
||||
Identity,
|
||||
JustTry,
|
||||
>;
|
||||
|
||||
pub type AssetTransactors = (
|
||||
FungibleAdapter<Balances, IsConcrete<RelayLocation>, SovereignAccountOf, AccountId, ()>,
|
||||
FungiblesAdapter<
|
||||
AssetsPallet,
|
||||
ForeignAssetsConvertedConcreteId,
|
||||
SovereignAccountOf,
|
||||
AccountId,
|
||||
NoChecking,
|
||||
CheckingAccount,
|
||||
>,
|
||||
);
|
||||
|
||||
pub type Barrier = (
|
||||
TakeWeightCredit,
|
||||
AllowTopLevelPaidExecutionFrom<Everything>,
|
||||
AllowKnownQueryResponses<XcmPallet>,
|
||||
AllowSubscriptionsFrom<Everything>,
|
||||
);
|
||||
|
||||
pub type XcmRouter = EnsureDecodableXcm<(TestSendXcmErrX8, TestSendXcm)>;
|
||||
|
||||
pub struct XcmConfig;
|
||||
impl xcm_executor::Config for XcmConfig {
|
||||
type RuntimeCall = RuntimeCall;
|
||||
type XcmSender = XcmRouter;
|
||||
type AssetTransactor = AssetTransactors;
|
||||
type OriginConverter = ();
|
||||
type IsReserve = ();
|
||||
type IsTeleporter = ();
|
||||
type UniversalLocation = UniversalLocation;
|
||||
type Barrier = Barrier;
|
||||
type Weigher = FixedWeightBounds<BaseXcmWeight, RuntimeCall, MaxInstructions>;
|
||||
type Trader = ();
|
||||
type ResponseHandler = XcmPallet;
|
||||
type AssetTrap = XcmPallet;
|
||||
type AssetLocker = ();
|
||||
type AssetExchanger = ();
|
||||
type AssetClaims = XcmPallet;
|
||||
type SubscriptionService = XcmPallet;
|
||||
type PalletInstancesInfo = AllPalletsWithSystem;
|
||||
type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
|
||||
type FeeManager = ();
|
||||
type MessageExporter = ();
|
||||
type UniversalAliases = Nothing;
|
||||
type CallDispatcher = RuntimeCall;
|
||||
type SafeCallFilter = Everything;
|
||||
type Aliasers = Nothing;
|
||||
type TransactionalProcessor = xcm_builder::FrameTransactionalProcessor;
|
||||
type HrmpNewChannelOpenRequestHandler = ();
|
||||
type HrmpChannelAcceptedHandler = ();
|
||||
type HrmpChannelClosingHandler = ();
|
||||
type XcmRecorder = XcmPallet;
|
||||
type XcmEventEmitter = XcmPallet;
|
||||
}
|
||||
|
||||
pub type LocalOriginToLocation = xcm_builder::SignedToAccountId32<RuntimeOrigin, AccountId, ()>;
|
||||
|
||||
parameter_types! {
|
||||
pub static AdvertisedXcmVersion: xcm::prelude::XcmVersion = 4;
|
||||
pub const AuthorizeAliasHoldReason: RuntimeHoldReason = RuntimeHoldReason::XcmPallet(pallet_xcm::HoldReason::AuthorizeAlias);
|
||||
}
|
||||
|
||||
pub struct ConvertDeposit;
|
||||
impl Convert<Footprint, u128> for ConvertDeposit {
|
||||
fn convert(a: Footprint) -> u128 {
|
||||
(a.count * 2 + a.size) as u128
|
||||
}
|
||||
}
|
||||
|
||||
impl pallet_xcm::Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type SendXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
|
||||
type XcmRouter = XcmRouter;
|
||||
type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
|
||||
type XcmExecuteFilter = Everything;
|
||||
type XcmExecutor = XcmExecutor<XcmConfig>;
|
||||
type XcmTeleportFilter = Nothing;
|
||||
type XcmReserveTransferFilter = Everything;
|
||||
type Weigher = FixedWeightBounds<BaseXcmWeight, RuntimeCall, MaxInstructions>;
|
||||
type UniversalLocation = UniversalLocation;
|
||||
type RuntimeOrigin = RuntimeOrigin;
|
||||
type RuntimeCall = RuntimeCall;
|
||||
const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
|
||||
type AdvertisedXcmVersion = AdvertisedXcmVersion;
|
||||
type Currency = Balances;
|
||||
type CurrencyMatcher = ();
|
||||
type TrustedLockers = ();
|
||||
type SovereignAccountOf = ();
|
||||
type MaxLockers = ConstU32<8>;
|
||||
type MaxRemoteLockConsumers = ConstU32<0>;
|
||||
type RemoteLockConsumerIdentifier = ();
|
||||
type WeightInfo = pallet_xcm::TestWeightInfo;
|
||||
type AdminOrigin = EnsureRoot<AccountId>;
|
||||
type AuthorizedAliasConsideration =
|
||||
HoldConsideration<AccountId, Balances, AuthorizeAliasHoldReason, ConvertDeposit>;
|
||||
}
|
||||
|
||||
#[derive_impl(pallet_revive::config_preludes::TestDefaultConfig)]
|
||||
impl pallet_revive::Config for Test {
|
||||
type AddressMapper = pallet_revive::AccountId32Mapper<Self>;
|
||||
type Balance = Balance;
|
||||
type Currency = Balances;
|
||||
type Precompiles = (XcmPrecompile<Self>,);
|
||||
type Time = Timestamp;
|
||||
type UploadOrigin = frame_system::EnsureSigned<AccountId>;
|
||||
type InstantiateOrigin = frame_system::EnsureSigned<AccountId>;
|
||||
}
|
||||
|
||||
pub(crate) fn buy_execution<C>(fees: impl Into<Asset>) -> Instruction<C> {
|
||||
use xcm::latest::prelude::*;
|
||||
BuyExecution { fees: fees.into(), weight_limit: Unlimited }
|
||||
}
|
||||
|
||||
pub(crate) fn new_test_ext_with_balances(
|
||||
balances: Vec<(AccountId, Balance)>,
|
||||
) -> sp_io::TestExternalities {
|
||||
new_test_ext_with_balances_and_xcm_version(balances, Some(XCM_VERSION), vec![])
|
||||
}
|
||||
|
||||
pub fn new_test_ext_with_balances_and_xcm_version(
|
||||
balances: Vec<(AccountId, Balance)>,
|
||||
safe_xcm_version: Option<XcmVersion>,
|
||||
supported_version: Vec<(Location, XcmVersion)>,
|
||||
) -> sp_io::TestExternalities {
|
||||
let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
|
||||
|
||||
pallet_balances::GenesisConfig::<Test> { balances, ..Default::default() }
|
||||
.assimilate_storage(&mut t)
|
||||
.unwrap();
|
||||
|
||||
pallet_xcm::GenesisConfig::<Test> { safe_xcm_version, supported_version, ..Default::default() }
|
||||
.assimilate_storage(&mut t)
|
||||
.unwrap();
|
||||
|
||||
pallet_revive::GenesisConfig::<Test> { mapped_accounts: vec![ALICE], ..Default::default() }
|
||||
.assimilate_storage(&mut t)
|
||||
.unwrap();
|
||||
|
||||
let mut ext = sp_io::TestExternalities::new(t);
|
||||
ext.execute_with(|| System::set_block_number(1));
|
||||
ext
|
||||
}
|
||||
@@ -0,0 +1,713 @@
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// This file is part of Pezkuwi.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
mock::*,
|
||||
IXcm::{self, weighMessageCall},
|
||||
VersionedLocation, VersionedXcm,
|
||||
};
|
||||
use frame_support::traits::Currency;
|
||||
use pallet_revive::{
|
||||
precompiles::{
|
||||
alloy::{
|
||||
hex,
|
||||
sol_types::{SolInterface, SolValue},
|
||||
},
|
||||
H160,
|
||||
},
|
||||
ExecConfig, U256,
|
||||
};
|
||||
use pezkuwi_teyrchain_primitives::primitives::Id as ParaId;
|
||||
use sp_runtime::traits::AccountIdConversion;
|
||||
use xcm::{prelude::*, v3, v4};
|
||||
|
||||
const BOB: AccountId = AccountId::new([1u8; 32]);
|
||||
const CHARLIE: AccountId = AccountId::new([2u8; 32]);
|
||||
const SEND_AMOUNT: u128 = 10;
|
||||
const CUSTOM_INITIAL_BALANCE: u128 = 100_000_000_000u128;
|
||||
|
||||
#[test]
|
||||
fn test_xcm_send_precompile_works() {
|
||||
use codec::Encode;
|
||||
|
||||
let balances = vec![
|
||||
(ALICE, CUSTOM_INITIAL_BALANCE),
|
||||
(ParaId::from(SOME_PARA_ID).into_account_truncating(), CUSTOM_INITIAL_BALANCE),
|
||||
];
|
||||
new_test_ext_with_balances(balances).execute_with(|| {
|
||||
let xcm_precompile_addr = H160::from(
|
||||
hex::const_decode_to_array(b"00000000000000000000000000000000000A0000").unwrap(),
|
||||
);
|
||||
|
||||
let sender: Location = AccountId32 { network: None, id: ALICE.into() }.into();
|
||||
let message = Xcm(vec![
|
||||
ReserveAssetDeposited((Parent, SEND_AMOUNT).into()),
|
||||
ClearOrigin,
|
||||
buy_execution((Parent, SEND_AMOUNT)),
|
||||
DepositAsset { assets: AllCounted(1).into(), beneficiary: sender.clone() },
|
||||
]);
|
||||
|
||||
let versioned_dest: VersionedLocation = RelayLocation::get().into();
|
||||
let versioned_message: VersionedXcm<()> = VersionedXcm::from(message.clone());
|
||||
|
||||
let xcm_send_params = IXcm::sendCall {
|
||||
destination: versioned_dest.encode().into(),
|
||||
message: versioned_message.encode().into(),
|
||||
};
|
||||
let call = IXcm::IXcmCalls::send(xcm_send_params);
|
||||
let encoded_call = call.abi_encode();
|
||||
|
||||
let result = pallet_revive::Pallet::<Test>::bare_call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
xcm_precompile_addr,
|
||||
U256::zero(),
|
||||
Weight::MAX,
|
||||
u128::MAX,
|
||||
encoded_call,
|
||||
ExecConfig::new_substrate_tx(),
|
||||
);
|
||||
assert!(result.result.is_ok());
|
||||
let sent_message = Xcm(Some(DescendOrigin(sender.clone().try_into().unwrap()))
|
||||
.into_iter()
|
||||
.chain(message.0.clone().into_iter())
|
||||
.collect());
|
||||
assert_eq!(sent_xcm(), vec![(Here.into(), sent_message)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xcm_send_precompile_to_teyrchain() {
|
||||
use codec::Encode;
|
||||
|
||||
let balances = vec![
|
||||
(ALICE, CUSTOM_INITIAL_BALANCE),
|
||||
(ParaId::from(SOME_PARA_ID).into_account_truncating(), CUSTOM_INITIAL_BALANCE),
|
||||
];
|
||||
new_test_ext_with_balances(balances).execute_with(|| {
|
||||
let xcm_precompile_addr = H160::from(
|
||||
hex::const_decode_to_array(b"00000000000000000000000000000000000A0000").unwrap(),
|
||||
);
|
||||
|
||||
let sender: Location = AccountId32 { network: None, id: ALICE.into() }.into();
|
||||
let message = Xcm(vec![
|
||||
ReserveAssetDeposited((Parent, SEND_AMOUNT).into()),
|
||||
ClearOrigin,
|
||||
buy_execution((Parent, SEND_AMOUNT)),
|
||||
DepositAsset { assets: AllCounted(1).into(), beneficiary: sender.clone() },
|
||||
]);
|
||||
|
||||
let destination: VersionedLocation = Teyrchain(SOME_PARA_ID).into();
|
||||
let versioned_message: VersionedXcm<()> = VersionedXcm::from(message.clone());
|
||||
|
||||
let xcm_send_params = IXcm::sendCall {
|
||||
destination: destination.encode().into(),
|
||||
message: versioned_message.encode().into(),
|
||||
};
|
||||
let call = IXcm::IXcmCalls::send(xcm_send_params);
|
||||
let encoded_call = call.abi_encode();
|
||||
|
||||
let result = pallet_revive::Pallet::<Test>::bare_call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
xcm_precompile_addr,
|
||||
U256::zero(),
|
||||
Weight::MAX,
|
||||
u128::MAX,
|
||||
encoded_call,
|
||||
ExecConfig::new_substrate_tx(),
|
||||
);
|
||||
|
||||
assert!(result.result.is_ok());
|
||||
let sent_message = Xcm(Some(DescendOrigin(sender.clone().try_into().unwrap()))
|
||||
.into_iter()
|
||||
.chain(message.0.clone().into_iter())
|
||||
.collect());
|
||||
assert_eq!(sent_xcm(), vec![(Teyrchain(SOME_PARA_ID).into(), sent_message)]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xcm_send_precompile_fails() {
|
||||
use codec::Encode;
|
||||
|
||||
let balances = vec![
|
||||
(ALICE, CUSTOM_INITIAL_BALANCE),
|
||||
(ParaId::from(SOME_PARA_ID).into_account_truncating(), CUSTOM_INITIAL_BALANCE),
|
||||
];
|
||||
new_test_ext_with_balances(balances).execute_with(|| {
|
||||
let xcm_precompile_addr = H160::from(
|
||||
hex::const_decode_to_array(b"00000000000000000000000000000000000A0000").unwrap(),
|
||||
);
|
||||
|
||||
let sender: Location = AccountId32 { network: None, id: ALICE.into() }.into();
|
||||
let message = Xcm(vec![
|
||||
ReserveAssetDeposited((Parent, SEND_AMOUNT).into()),
|
||||
buy_execution((Parent, SEND_AMOUNT)),
|
||||
DepositAsset { assets: AllCounted(1).into(), beneficiary: sender },
|
||||
]);
|
||||
|
||||
let destination: VersionedLocation = VersionedLocation::from(Location::ancestor(8));
|
||||
let versioned_message: VersionedXcm<()> = VersionedXcm::from(message.clone());
|
||||
|
||||
let xcm_send_params = IXcm::sendCall {
|
||||
destination: destination.encode().into(),
|
||||
message: versioned_message.encode().into(),
|
||||
};
|
||||
let call = IXcm::IXcmCalls::send(xcm_send_params);
|
||||
let encoded_call = call.abi_encode();
|
||||
|
||||
let result = pallet_revive::Pallet::<Test>::bare_call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
xcm_precompile_addr,
|
||||
U256::zero(),
|
||||
Weight::MAX,
|
||||
u128::MAX,
|
||||
encoded_call,
|
||||
ExecConfig::new_substrate_tx(),
|
||||
);
|
||||
let return_value = match result.result {
|
||||
Ok(value) => value,
|
||||
Err(err) => panic!("XcmSendPrecompile call failed with error: {err:?}"),
|
||||
};
|
||||
assert!(return_value.did_revert());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_fails_on_old_location_version() {
|
||||
use codec::Encode;
|
||||
|
||||
let balances = vec![
|
||||
(ALICE, CUSTOM_INITIAL_BALANCE),
|
||||
(ParaId::from(SOME_PARA_ID).into_account_truncating(), CUSTOM_INITIAL_BALANCE),
|
||||
];
|
||||
new_test_ext_with_balances(balances).execute_with(|| {
|
||||
let xcm_precompile_addr = H160::from(
|
||||
hex::const_decode_to_array(b"00000000000000000000000000000000000A0000").unwrap(),
|
||||
);
|
||||
|
||||
let sender: Location = AccountId32 { network: None, id: ALICE.into() }.into();
|
||||
let message = Xcm(vec![
|
||||
ReserveAssetDeposited((Parent, SEND_AMOUNT).into()),
|
||||
ClearOrigin,
|
||||
buy_execution((Parent, SEND_AMOUNT)),
|
||||
DepositAsset { assets: AllCounted(1).into(), beneficiary: sender.clone() },
|
||||
]);
|
||||
|
||||
// V4 location is old and will fail.
|
||||
let destination: VersionedLocation =
|
||||
VersionedLocation::V4(v4::Junction::Teyrchain(SOME_PARA_ID).into());
|
||||
let versioned_message: VersionedXcm<RuntimeCall> = VersionedXcm::from(message.clone());
|
||||
|
||||
let xcm_send_params = IXcm::sendCall {
|
||||
destination: destination.encode().into(),
|
||||
message: versioned_message.encode().into(),
|
||||
};
|
||||
let call = IXcm::IXcmCalls::send(xcm_send_params);
|
||||
let encoded_call = call.abi_encode();
|
||||
|
||||
let result = pallet_revive::Pallet::<Test>::bare_call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
xcm_precompile_addr,
|
||||
U256::zero(),
|
||||
Weight::MAX,
|
||||
u128::MAX,
|
||||
encoded_call,
|
||||
ExecConfig::new_substrate_tx(),
|
||||
);
|
||||
let return_value = match result.result {
|
||||
Ok(value) => value,
|
||||
Err(err) => panic!("XcmSendPrecompile call failed with error: {err:?}"),
|
||||
};
|
||||
assert!(return_value.did_revert());
|
||||
|
||||
// V3 also fails.
|
||||
let destination: VersionedLocation =
|
||||
VersionedLocation::V3(v3::Junction::Teyrchain(SOME_PARA_ID).into());
|
||||
let versioned_message: VersionedXcm<RuntimeCall> = VersionedXcm::from(message);
|
||||
|
||||
let xcm_send_params = IXcm::sendCall {
|
||||
destination: destination.encode().into(),
|
||||
message: versioned_message.encode().into(),
|
||||
};
|
||||
let call = IXcm::IXcmCalls::send(xcm_send_params);
|
||||
let encoded_call = call.abi_encode();
|
||||
|
||||
let result = pallet_revive::Pallet::<Test>::bare_call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
xcm_precompile_addr,
|
||||
U256::zero(),
|
||||
Weight::MAX,
|
||||
u128::MAX,
|
||||
encoded_call,
|
||||
ExecConfig::new_substrate_tx(),
|
||||
);
|
||||
let return_value = match result.result {
|
||||
Ok(value) => value,
|
||||
Err(err) => panic!("XcmSendPrecompile call failed with error: {err:?}"),
|
||||
};
|
||||
assert!(return_value.did_revert());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_fails_on_old_xcm_version() {
|
||||
use codec::Encode;
|
||||
|
||||
let balances = vec![
|
||||
(ALICE, CUSTOM_INITIAL_BALANCE),
|
||||
(ParaId::from(SOME_PARA_ID).into_account_truncating(), CUSTOM_INITIAL_BALANCE),
|
||||
];
|
||||
new_test_ext_with_balances(balances).execute_with(|| {
|
||||
let xcm_precompile_addr = H160::from(
|
||||
hex::const_decode_to_array(b"00000000000000000000000000000000000A0000").unwrap(),
|
||||
);
|
||||
|
||||
let sender: Location = AccountId32 { network: None, id: ALICE.into() }.into();
|
||||
let message = Xcm(vec![
|
||||
ReserveAssetDeposited((Parent, SEND_AMOUNT).into()),
|
||||
ClearOrigin,
|
||||
buy_execution((Parent, SEND_AMOUNT)),
|
||||
DepositAsset { assets: AllCounted(1).into(), beneficiary: sender.clone() },
|
||||
]);
|
||||
// V4 is old and fails.
|
||||
let v4_message: v4::Xcm<RuntimeCall> = message.try_into().unwrap();
|
||||
|
||||
let destination: VersionedLocation = Teyrchain(SOME_PARA_ID).into();
|
||||
let versioned_message: VersionedXcm<RuntimeCall> = VersionedXcm::V4(v4_message.clone());
|
||||
|
||||
let xcm_send_params = IXcm::sendCall {
|
||||
destination: destination.encode().into(),
|
||||
message: versioned_message.encode().into(),
|
||||
};
|
||||
let call = IXcm::IXcmCalls::send(xcm_send_params);
|
||||
let encoded_call = call.abi_encode();
|
||||
|
||||
let result = pallet_revive::Pallet::<Test>::bare_call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
xcm_precompile_addr,
|
||||
U256::zero(),
|
||||
Weight::MAX,
|
||||
u128::MAX,
|
||||
encoded_call,
|
||||
ExecConfig::new_substrate_tx(),
|
||||
);
|
||||
let return_value = match result.result {
|
||||
Ok(value) => value,
|
||||
Err(err) => panic!("XcmSendPrecompile call failed with error: {err:?}"),
|
||||
};
|
||||
assert!(return_value.did_revert());
|
||||
|
||||
// With V3 it also fails.
|
||||
let v3_message: v3::Xcm<RuntimeCall> = v4_message.try_into().unwrap();
|
||||
|
||||
let destination: VersionedLocation = Teyrchain(SOME_PARA_ID).into();
|
||||
let versioned_message: VersionedXcm<RuntimeCall> = VersionedXcm::V3(v3_message);
|
||||
|
||||
let xcm_send_params = IXcm::sendCall {
|
||||
destination: destination.encode().into(),
|
||||
message: versioned_message.encode().into(),
|
||||
};
|
||||
let call = IXcm::IXcmCalls::send(xcm_send_params);
|
||||
let encoded_call = call.abi_encode();
|
||||
|
||||
let result = pallet_revive::Pallet::<Test>::bare_call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
xcm_precompile_addr,
|
||||
U256::zero(),
|
||||
Weight::MAX,
|
||||
u128::MAX,
|
||||
encoded_call,
|
||||
ExecConfig::new_substrate_tx(),
|
||||
);
|
||||
let return_value = match result.result {
|
||||
Ok(value) => value,
|
||||
Err(err) => panic!("XcmSendPrecompile call failed with error: {err:?}"),
|
||||
};
|
||||
assert!(return_value.did_revert());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xcm_execute_precompile_works() {
|
||||
use codec::Encode;
|
||||
|
||||
let balances = vec![
|
||||
(ALICE, CUSTOM_INITIAL_BALANCE),
|
||||
(ParaId::from(SOME_PARA_ID).into_account_truncating(), CUSTOM_INITIAL_BALANCE),
|
||||
];
|
||||
new_test_ext_with_balances(balances).execute_with(|| {
|
||||
let xcm_precompile_addr = H160::from(
|
||||
hex::const_decode_to_array(b"00000000000000000000000000000000000A0000").unwrap(),
|
||||
);
|
||||
|
||||
let dest: Location = Junction::AccountId32 { network: None, id: BOB.into() }.into();
|
||||
assert_eq!(Balances::total_balance(&ALICE), CUSTOM_INITIAL_BALANCE);
|
||||
|
||||
let message: VersionedXcm<RuntimeCall> = VersionedXcm::from(Xcm(vec![
|
||||
WithdrawAsset((Here, SEND_AMOUNT).into()),
|
||||
buy_execution((Here, SEND_AMOUNT)),
|
||||
DepositAsset { assets: AllCounted(1).into(), beneficiary: dest },
|
||||
]));
|
||||
|
||||
let weight_params = weighMessageCall { message: message.encode().into() };
|
||||
let weight_call = IXcm::IXcmCalls::weighMessage(weight_params);
|
||||
let encoded_weight_call = weight_call.abi_encode();
|
||||
|
||||
let xcm_weight_results = pallet_revive::Pallet::<Test>::bare_call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
xcm_precompile_addr,
|
||||
U256::zero(),
|
||||
Weight::MAX,
|
||||
u128::MAX,
|
||||
encoded_weight_call,
|
||||
ExecConfig::new_substrate_tx(),
|
||||
);
|
||||
|
||||
let weight_result = match xcm_weight_results.result {
|
||||
Ok(value) => value,
|
||||
Err(err) => panic!("XcmExecutePrecompile Failed to decode weight with error {err:?}"),
|
||||
};
|
||||
|
||||
let weight: IXcm::Weight = IXcm::Weight::abi_decode(&weight_result.data[..])
|
||||
.expect("XcmExecutePrecompile Failed to decode weight");
|
||||
|
||||
let xcm_execute_params = IXcm::executeCall { message: message.encode().into(), weight };
|
||||
let call = IXcm::IXcmCalls::execute(xcm_execute_params);
|
||||
let encoded_call = call.abi_encode();
|
||||
|
||||
let result = pallet_revive::Pallet::<Test>::bare_call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
xcm_precompile_addr,
|
||||
U256::zero(),
|
||||
Weight::MAX,
|
||||
u128::MAX,
|
||||
encoded_call,
|
||||
ExecConfig::new_substrate_tx(),
|
||||
);
|
||||
|
||||
assert!(result.result.is_ok());
|
||||
assert_eq!(Balances::total_balance(&ALICE), CUSTOM_INITIAL_BALANCE - SEND_AMOUNT);
|
||||
assert_eq!(Balances::total_balance(&BOB), SEND_AMOUNT);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xcm_execute_precompile_different_beneficiary() {
|
||||
use codec::Encode;
|
||||
|
||||
let balances = vec![(ALICE, CUSTOM_INITIAL_BALANCE), (CHARLIE, CUSTOM_INITIAL_BALANCE)];
|
||||
new_test_ext_with_balances(balances).execute_with(|| {
|
||||
let xcm_precompile_addr = H160::from(
|
||||
hex::const_decode_to_array(b"00000000000000000000000000000000000A0000").unwrap(),
|
||||
);
|
||||
|
||||
let dest: Location = Junction::AccountId32 { network: None, id: CHARLIE.into() }.into();
|
||||
assert_eq!(Balances::total_balance(&ALICE), CUSTOM_INITIAL_BALANCE);
|
||||
|
||||
let message: VersionedXcm<RuntimeCall> = VersionedXcm::from(Xcm(vec![
|
||||
WithdrawAsset((Here, SEND_AMOUNT).into()),
|
||||
buy_execution((Here, SEND_AMOUNT)),
|
||||
DepositAsset { assets: AllCounted(1).into(), beneficiary: dest },
|
||||
]));
|
||||
|
||||
let weight_params = weighMessageCall { message: message.encode().into() };
|
||||
let weight_call = IXcm::IXcmCalls::weighMessage(weight_params);
|
||||
let encoded_weight_call = weight_call.abi_encode();
|
||||
|
||||
let xcm_weight_results = pallet_revive::Pallet::<Test>::bare_call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
xcm_precompile_addr,
|
||||
U256::zero(),
|
||||
Weight::MAX,
|
||||
u128::MAX,
|
||||
encoded_weight_call,
|
||||
ExecConfig::new_substrate_tx(),
|
||||
);
|
||||
|
||||
let weight_result = match xcm_weight_results.result {
|
||||
Ok(value) => value,
|
||||
Err(err) => panic!("XcmExecutePrecompile Failed to decode weight with error: {err:?}"),
|
||||
};
|
||||
|
||||
let weight: IXcm::Weight = IXcm::Weight::abi_decode(&weight_result.data[..])
|
||||
.expect("XcmExecutePrecompile Failed to decode weight");
|
||||
|
||||
let xcm_execute_params = IXcm::executeCall { message: message.encode().into(), weight };
|
||||
let call = IXcm::IXcmCalls::execute(xcm_execute_params);
|
||||
let encoded_call = call.abi_encode();
|
||||
|
||||
let result = pallet_revive::Pallet::<Test>::bare_call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
xcm_precompile_addr,
|
||||
U256::zero(),
|
||||
Weight::MAX,
|
||||
u128::MAX,
|
||||
encoded_call,
|
||||
ExecConfig::new_substrate_tx(),
|
||||
);
|
||||
|
||||
let return_value = match result.result {
|
||||
Ok(value) => value,
|
||||
Err(err) => panic!("XcmExecutePrecompile call failed with error: {err:?}"),
|
||||
};
|
||||
|
||||
assert!(!return_value.did_revert());
|
||||
assert_eq!(Balances::total_balance(&ALICE), CUSTOM_INITIAL_BALANCE - SEND_AMOUNT);
|
||||
assert_eq!(Balances::total_balance(&CHARLIE), CUSTOM_INITIAL_BALANCE + SEND_AMOUNT);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xcm_execute_precompile_fails() {
|
||||
use codec::Encode;
|
||||
|
||||
let balances = vec![(ALICE, CUSTOM_INITIAL_BALANCE), (BOB, CUSTOM_INITIAL_BALANCE)];
|
||||
new_test_ext_with_balances(balances).execute_with(|| {
|
||||
let xcm_precompile_addr = H160::from(
|
||||
hex::const_decode_to_array(b"00000000000000000000000000000000000A0000").unwrap(),
|
||||
);
|
||||
|
||||
let dest: Location = Junction::AccountId32 { network: None, id: BOB.into() }.into();
|
||||
assert_eq!(Balances::total_balance(&ALICE), CUSTOM_INITIAL_BALANCE);
|
||||
let amount_to_send = CUSTOM_INITIAL_BALANCE - ExistentialDeposit::get();
|
||||
let assets: Assets = (Here, amount_to_send).into();
|
||||
|
||||
let message: VersionedXcm<RuntimeCall> = VersionedXcm::from(Xcm(vec![
|
||||
WithdrawAsset(assets.clone()),
|
||||
buy_execution(assets.inner()[0].clone()),
|
||||
DepositAsset { assets: assets.clone().into(), beneficiary: dest },
|
||||
WithdrawAsset(assets),
|
||||
]));
|
||||
|
||||
let weight_params = weighMessageCall { message: message.encode().into() };
|
||||
let weight_call = IXcm::IXcmCalls::weighMessage(weight_params);
|
||||
let encoded_weight_call = weight_call.abi_encode();
|
||||
|
||||
let xcm_weight_results = pallet_revive::Pallet::<Test>::bare_call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
xcm_precompile_addr,
|
||||
U256::zero(),
|
||||
Weight::MAX,
|
||||
u128::MAX,
|
||||
encoded_weight_call,
|
||||
ExecConfig::new_substrate_tx(),
|
||||
);
|
||||
|
||||
let weight_result = match xcm_weight_results.result {
|
||||
Ok(value) => value,
|
||||
Err(err) => panic!("XcmExecutePrecompile Failed to decode weight with error: {err:?}"),
|
||||
};
|
||||
|
||||
let weight: IXcm::Weight = IXcm::Weight::abi_decode(&weight_result.data[..])
|
||||
.expect("XcmExecutePrecompile Failed to decode weight");
|
||||
|
||||
let xcm_execute_params = IXcm::executeCall { message: message.encode().into(), weight };
|
||||
let call = IXcm::IXcmCalls::execute(xcm_execute_params);
|
||||
let encoded_call = call.abi_encode();
|
||||
|
||||
let result = pallet_revive::Pallet::<Test>::bare_call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
xcm_precompile_addr,
|
||||
U256::zero(),
|
||||
Weight::MAX,
|
||||
u128::MAX,
|
||||
encoded_call,
|
||||
ExecConfig::new_substrate_tx(),
|
||||
);
|
||||
let return_value = match result.result {
|
||||
Ok(value) => value,
|
||||
Err(err) => panic!("XcmExecutePrecompile call failed with error: {err:?}"),
|
||||
};
|
||||
assert!(return_value.did_revert());
|
||||
assert_eq!(Balances::total_balance(&ALICE), CUSTOM_INITIAL_BALANCE);
|
||||
assert_eq!(Balances::total_balance(&BOB), CUSTOM_INITIAL_BALANCE);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_fails_on_old_version() {
|
||||
use codec::Encode;
|
||||
|
||||
let balances = vec![
|
||||
(ALICE, CUSTOM_INITIAL_BALANCE),
|
||||
(ParaId::from(SOME_PARA_ID).into_account_truncating(), CUSTOM_INITIAL_BALANCE),
|
||||
];
|
||||
new_test_ext_with_balances(balances).execute_with(|| {
|
||||
let xcm_precompile_addr = H160::from(
|
||||
hex::const_decode_to_array(b"00000000000000000000000000000000000A0000").unwrap(),
|
||||
);
|
||||
|
||||
let dest: Location = Junction::AccountId32 { network: None, id: BOB.into() }.into();
|
||||
assert_eq!(Balances::total_balance(&ALICE), CUSTOM_INITIAL_BALANCE);
|
||||
|
||||
let message = Xcm(vec![
|
||||
WithdrawAsset((Here, SEND_AMOUNT).into()),
|
||||
buy_execution((Here, SEND_AMOUNT)),
|
||||
DepositAsset { assets: AllCounted(1).into(), beneficiary: dest },
|
||||
]);
|
||||
let versioned_message = VersionedXcm::from(message.clone());
|
||||
|
||||
let weight_params = weighMessageCall { message: versioned_message.encode().into() };
|
||||
let weight_call = IXcm::IXcmCalls::weighMessage(weight_params);
|
||||
let encoded_weight_call = weight_call.abi_encode();
|
||||
|
||||
let xcm_weight_results = pallet_revive::Pallet::<Test>::bare_call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
xcm_precompile_addr,
|
||||
U256::zero(),
|
||||
Weight::MAX,
|
||||
u128::MAX,
|
||||
encoded_weight_call,
|
||||
ExecConfig::new_substrate_tx(),
|
||||
);
|
||||
|
||||
let weight_result = match xcm_weight_results.result {
|
||||
Ok(value) => value,
|
||||
Err(err) => panic!("XcmExecutePrecompile Failed to decode weight with error {err:?}"),
|
||||
};
|
||||
|
||||
let weight: IXcm::Weight = IXcm::Weight::abi_decode(&weight_result.data[..])
|
||||
.expect("XcmExecutePrecompile Failed to decode weight");
|
||||
|
||||
// Using a V4 message to check that it fails.
|
||||
let v4_message: v4::Xcm<RuntimeCall> = message.clone().try_into().unwrap();
|
||||
let versioned_message = VersionedXcm::V4(v4_message.clone());
|
||||
|
||||
let xcm_execute_params = IXcm::executeCall {
|
||||
message: versioned_message.encode().into(),
|
||||
weight: weight.clone(),
|
||||
};
|
||||
let call = IXcm::IXcmCalls::execute(xcm_execute_params);
|
||||
let encoded_call = call.abi_encode();
|
||||
|
||||
let result = pallet_revive::Pallet::<Test>::bare_call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
xcm_precompile_addr,
|
||||
U256::zero(),
|
||||
Weight::MAX,
|
||||
u128::MAX,
|
||||
encoded_call,
|
||||
ExecConfig::new_substrate_tx(),
|
||||
);
|
||||
|
||||
let return_value = match result.result {
|
||||
Ok(value) => value,
|
||||
Err(err) => panic!("XcmExecutePrecompile call failed with error: {err:?}"),
|
||||
};
|
||||
assert!(return_value.did_revert());
|
||||
assert_eq!(Balances::total_balance(&ALICE), CUSTOM_INITIAL_BALANCE);
|
||||
assert_eq!(Balances::total_balance(&BOB), 0);
|
||||
|
||||
// Now using a V3 message.
|
||||
let v3_message: v3::Xcm<RuntimeCall> = v4_message.try_into().unwrap();
|
||||
let versioned_message = VersionedXcm::V3(v3_message);
|
||||
|
||||
let xcm_execute_params =
|
||||
IXcm::executeCall { message: versioned_message.encode().into(), weight };
|
||||
let call = IXcm::IXcmCalls::execute(xcm_execute_params);
|
||||
let encoded_call = call.abi_encode();
|
||||
|
||||
let result = pallet_revive::Pallet::<Test>::bare_call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
xcm_precompile_addr,
|
||||
U256::zero(),
|
||||
Weight::MAX,
|
||||
u128::MAX,
|
||||
encoded_call,
|
||||
ExecConfig::new_substrate_tx(),
|
||||
);
|
||||
|
||||
let return_value = match result.result {
|
||||
Ok(value) => value,
|
||||
Err(err) => panic!("XcmExecutePrecompile call failed with error: {err:?}"),
|
||||
};
|
||||
assert!(return_value.did_revert());
|
||||
assert_eq!(Balances::total_balance(&ALICE), CUSTOM_INITIAL_BALANCE);
|
||||
assert_eq!(Balances::total_balance(&BOB), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn weight_fails_on_old_version() {
|
||||
use codec::Encode;
|
||||
|
||||
let balances = vec![
|
||||
(ALICE, CUSTOM_INITIAL_BALANCE),
|
||||
(ParaId::from(SOME_PARA_ID).into_account_truncating(), CUSTOM_INITIAL_BALANCE),
|
||||
];
|
||||
new_test_ext_with_balances(balances).execute_with(|| {
|
||||
let xcm_precompile_addr = H160::from(
|
||||
hex::const_decode_to_array(b"00000000000000000000000000000000000A0000").unwrap(),
|
||||
);
|
||||
|
||||
let dest: Location = Junction::AccountId32 { network: None, id: BOB.into() }.into();
|
||||
assert_eq!(Balances::total_balance(&ALICE), CUSTOM_INITIAL_BALANCE);
|
||||
|
||||
let message: Xcm<RuntimeCall> = Xcm(vec![
|
||||
WithdrawAsset((Here, SEND_AMOUNT).into()),
|
||||
buy_execution((Here, SEND_AMOUNT)),
|
||||
DepositAsset { assets: AllCounted(1).into(), beneficiary: dest },
|
||||
]);
|
||||
// V4 version is old, fails.
|
||||
let v4_message: v4::Xcm<RuntimeCall> = message.try_into().unwrap();
|
||||
let versioned_message = VersionedXcm::V4(v4_message.clone());
|
||||
|
||||
let weight_params = weighMessageCall { message: versioned_message.encode().into() };
|
||||
let weight_call = IXcm::IXcmCalls::weighMessage(weight_params);
|
||||
let encoded_weight_call = weight_call.abi_encode();
|
||||
|
||||
let xcm_weight_results = pallet_revive::Pallet::<Test>::bare_call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
xcm_precompile_addr,
|
||||
U256::zero(),
|
||||
Weight::MAX,
|
||||
u128::MAX,
|
||||
encoded_weight_call,
|
||||
ExecConfig::new_substrate_tx(),
|
||||
);
|
||||
|
||||
let result = match xcm_weight_results.result {
|
||||
Ok(value) => value,
|
||||
Err(err) => panic!("XcmExecutePrecompile Failed to decode weight with error {err:?}"),
|
||||
};
|
||||
assert!(result.did_revert());
|
||||
|
||||
// Now we also try V3.
|
||||
let v3_message: v3::Xcm<RuntimeCall> = v4_message.try_into().unwrap();
|
||||
let versioned_message = VersionedXcm::V3(v3_message);
|
||||
|
||||
let weight_params = weighMessageCall { message: versioned_message.encode().into() };
|
||||
let weight_call = IXcm::IXcmCalls::weighMessage(weight_params);
|
||||
let encoded_weight_call = weight_call.abi_encode();
|
||||
|
||||
let xcm_weight_results = pallet_revive::Pallet::<Test>::bare_call(
|
||||
RuntimeOrigin::signed(ALICE),
|
||||
xcm_precompile_addr,
|
||||
U256::zero(),
|
||||
Weight::MAX,
|
||||
u128::MAX,
|
||||
encoded_weight_call,
|
||||
ExecConfig::new_substrate_tx(),
|
||||
);
|
||||
|
||||
let result = match xcm_weight_results.result {
|
||||
Ok(value) => value,
|
||||
Err(err) => panic!("XcmExecutePrecompile Failed to decode weight with error {err:?}"),
|
||||
};
|
||||
assert!(result.did_revert());
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user