767 lines
24 KiB
Rust
767 lines
24 KiB
Rust
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// 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 core::marker::PhantomData;
|
|
|
|
use codec::{Decode, DecodeLimit};
|
|
use pezcumulus_pallet_teyrchain_system::teyrchain_inherent::{
|
|
deconstruct_teyrchain_inherent_data, InboundMessagesData,
|
|
};
|
|
use pezcumulus_primitives_core::{
|
|
relay_chain::Slot, AbridgedHrmpChannel, ParaId, PersistedValidationData,
|
|
};
|
|
use pezcumulus_primitives_teyrchain_inherent::TeyrchainInherentData;
|
|
use pezcumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
|
|
use pezframe_support::{
|
|
dispatch::{DispatchResult, GetDispatchInfo, RawOrigin},
|
|
inherent::{InherentData, ProvideInherent},
|
|
pezpallet_prelude::Get,
|
|
traits::{OnFinalize, OnInitialize, OriginTrait, UnfilteredDispatchable},
|
|
weights::Weight,
|
|
};
|
|
use pezframe_system::pezpallet_prelude::{BlockNumberFor, HeaderFor};
|
|
use pezkuwi_teyrchain_primitives::primitives::{
|
|
HeadData, HrmpChannelId, RelayChainBlockNumber, XcmpMessageFormat,
|
|
};
|
|
use pezsp_consensus_aura::{SlotDuration, AURA_ENGINE_ID};
|
|
use pezsp_core::{Encode, U256};
|
|
use pezsp_runtime::{
|
|
traits::{Dispatchable, Header},
|
|
BuildStorage, Digest, DigestItem, DispatchError, Either, SaturatedConversion,
|
|
};
|
|
use xcm::{
|
|
latest::{Asset, Location, XcmContext, XcmHash},
|
|
prelude::*,
|
|
VersionedXcm, MAX_XCM_DECODE_DEPTH,
|
|
};
|
|
use xcm_executor::{traits::TransactAsset, AssetsInHolding};
|
|
|
|
pub mod test_cases;
|
|
|
|
pub type BalanceOf<Runtime> = <Runtime as pezpallet_balances::Config>::Balance;
|
|
pub type AccountIdOf<Runtime> = <Runtime as pezframe_system::Config>::AccountId;
|
|
pub type RuntimeCallOf<Runtime> = <Runtime as pezframe_system::Config>::RuntimeCall;
|
|
pub type RuntimeOriginOf<Runtime> = <Runtime as pezframe_system::Config>::RuntimeOrigin;
|
|
pub type ValidatorIdOf<Runtime> = <Runtime as pezpallet_session::Config>::ValidatorId;
|
|
pub type SessionKeysOf<Runtime> = <Runtime as pezpallet_session::Config>::Keys;
|
|
|
|
pub struct CollatorSessionKey<
|
|
Runtime: pezframe_system::Config + pezpallet_balances::Config + pezpallet_session::Config,
|
|
> {
|
|
collator: AccountIdOf<Runtime>,
|
|
validator: ValidatorIdOf<Runtime>,
|
|
key: SessionKeysOf<Runtime>,
|
|
}
|
|
|
|
pub struct CollatorSessionKeys<
|
|
Runtime: pezframe_system::Config + pezpallet_balances::Config + pezpallet_session::Config,
|
|
> {
|
|
items: Vec<CollatorSessionKey<Runtime>>,
|
|
}
|
|
|
|
impl<Runtime: pezframe_system::Config + pezpallet_balances::Config + pezpallet_session::Config>
|
|
CollatorSessionKey<Runtime>
|
|
{
|
|
pub fn new(
|
|
collator: AccountIdOf<Runtime>,
|
|
validator: ValidatorIdOf<Runtime>,
|
|
key: SessionKeysOf<Runtime>,
|
|
) -> Self {
|
|
Self { collator, validator, key }
|
|
}
|
|
}
|
|
|
|
impl<Runtime: pezframe_system::Config + pezpallet_balances::Config + pezpallet_session::Config> Default
|
|
for CollatorSessionKeys<Runtime>
|
|
{
|
|
fn default() -> Self {
|
|
Self { items: vec![] }
|
|
}
|
|
}
|
|
|
|
impl<Runtime: pezframe_system::Config + pezpallet_balances::Config + pezpallet_session::Config>
|
|
CollatorSessionKeys<Runtime>
|
|
{
|
|
pub fn new(
|
|
collator: AccountIdOf<Runtime>,
|
|
validator: ValidatorIdOf<Runtime>,
|
|
key: SessionKeysOf<Runtime>,
|
|
) -> Self {
|
|
Self { items: vec![CollatorSessionKey::new(collator, validator, key)] }
|
|
}
|
|
|
|
pub fn add(mut self, item: CollatorSessionKey<Runtime>) -> Self {
|
|
self.items.push(item);
|
|
self
|
|
}
|
|
|
|
pub fn collators(&self) -> Vec<AccountIdOf<Runtime>> {
|
|
self.items.iter().map(|item| item.collator.clone()).collect::<Vec<_>>()
|
|
}
|
|
|
|
pub fn session_keys(
|
|
&self,
|
|
) -> Vec<(AccountIdOf<Runtime>, ValidatorIdOf<Runtime>, SessionKeysOf<Runtime>)> {
|
|
self.items
|
|
.iter()
|
|
.map(|item| (item.collator.clone(), item.validator.clone(), item.key.clone()))
|
|
.collect::<Vec<_>>()
|
|
}
|
|
}
|
|
|
|
pub struct SlotDurations {
|
|
pub relay: SlotDuration,
|
|
pub para: SlotDuration,
|
|
}
|
|
|
|
/// A set of traits for a minimal teyrchain runtime, that may be used in conjunction with the
|
|
/// `ExtBuilder` and the `RuntimeHelper`.
|
|
pub trait BasicTeyrchainRuntime:
|
|
pezframe_system::Config
|
|
+ pezpallet_balances::Config
|
|
+ pezpallet_session::Config
|
|
+ pezpallet_xcm::Config
|
|
+ teyrchain_info::Config
|
|
+ pezpallet_collator_selection::Config
|
|
+ pezcumulus_pallet_teyrchain_system::Config
|
|
+ pezpallet_timestamp::Config
|
|
{
|
|
}
|
|
|
|
impl<T> BasicTeyrchainRuntime for T
|
|
where
|
|
T: pezframe_system::Config
|
|
+ pezpallet_balances::Config
|
|
+ pezpallet_session::Config
|
|
+ pezpallet_xcm::Config
|
|
+ teyrchain_info::Config
|
|
+ pezpallet_collator_selection::Config
|
|
+ pezcumulus_pallet_teyrchain_system::Config
|
|
+ pezpallet_timestamp::Config,
|
|
ValidatorIdOf<T>: From<AccountIdOf<T>>,
|
|
{
|
|
}
|
|
|
|
/// Basic builder based on balances, collators and pezpallet_session.
|
|
pub struct ExtBuilder<Runtime: BasicTeyrchainRuntime> {
|
|
// endowed accounts with balances
|
|
balances: Vec<(AccountIdOf<Runtime>, BalanceOf<Runtime>)>,
|
|
// collators to test block prod
|
|
collators: Vec<AccountIdOf<Runtime>>,
|
|
// keys added to pallet session
|
|
keys: Vec<(AccountIdOf<Runtime>, ValidatorIdOf<Runtime>, SessionKeysOf<Runtime>)>,
|
|
// safe XCM version for pezpallet_xcm
|
|
safe_xcm_version: Option<XcmVersion>,
|
|
// para id
|
|
para_id: Option<ParaId>,
|
|
_runtime: PhantomData<Runtime>,
|
|
}
|
|
|
|
impl<Runtime: BasicTeyrchainRuntime> Default for ExtBuilder<Runtime> {
|
|
fn default() -> ExtBuilder<Runtime> {
|
|
ExtBuilder {
|
|
balances: vec![],
|
|
collators: vec![],
|
|
keys: vec![],
|
|
safe_xcm_version: None,
|
|
para_id: None,
|
|
_runtime: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<Runtime: BasicTeyrchainRuntime> ExtBuilder<Runtime> {
|
|
pub fn with_balances(
|
|
mut self,
|
|
balances: Vec<(AccountIdOf<Runtime>, BalanceOf<Runtime>)>,
|
|
) -> Self {
|
|
self.balances = balances;
|
|
self
|
|
}
|
|
pub fn with_collators(mut self, collators: Vec<AccountIdOf<Runtime>>) -> Self {
|
|
self.collators = collators;
|
|
self
|
|
}
|
|
|
|
pub fn with_session_keys(
|
|
mut self,
|
|
keys: Vec<(AccountIdOf<Runtime>, ValidatorIdOf<Runtime>, SessionKeysOf<Runtime>)>,
|
|
) -> Self {
|
|
self.keys = keys;
|
|
self
|
|
}
|
|
|
|
pub fn with_tracing(self) -> Self {
|
|
pezsp_tracing::try_init_simple();
|
|
self
|
|
}
|
|
|
|
pub fn with_safe_xcm_version(mut self, safe_xcm_version: XcmVersion) -> Self {
|
|
self.safe_xcm_version = Some(safe_xcm_version);
|
|
self
|
|
}
|
|
|
|
pub fn with_para_id(mut self, para_id: ParaId) -> Self {
|
|
self.para_id = Some(para_id);
|
|
self
|
|
}
|
|
|
|
pub fn build(self) -> pezsp_io::TestExternalities {
|
|
let mut t = pezframe_system::GenesisConfig::<Runtime>::default().build_storage().unwrap();
|
|
|
|
pezpallet_xcm::GenesisConfig::<Runtime> {
|
|
safe_xcm_version: self.safe_xcm_version,
|
|
..Default::default()
|
|
}
|
|
.assimilate_storage(&mut t)
|
|
.unwrap();
|
|
|
|
if let Some(para_id) = self.para_id {
|
|
teyrchain_info::GenesisConfig::<Runtime> {
|
|
teyrchain_id: para_id,
|
|
..Default::default()
|
|
}
|
|
.assimilate_storage(&mut t)
|
|
.unwrap();
|
|
}
|
|
|
|
pezpallet_balances::GenesisConfig::<Runtime> { balances: self.balances, ..Default::default() }
|
|
.assimilate_storage(&mut t)
|
|
.unwrap();
|
|
|
|
pezpallet_collator_selection::GenesisConfig::<Runtime> {
|
|
invulnerables: self.collators.clone(),
|
|
candidacy_bond: Default::default(),
|
|
desired_candidates: Default::default(),
|
|
}
|
|
.assimilate_storage(&mut t)
|
|
.unwrap();
|
|
|
|
pezpallet_session::GenesisConfig::<Runtime> { keys: self.keys, ..Default::default() }
|
|
.assimilate_storage(&mut t)
|
|
.unwrap();
|
|
|
|
let mut ext = pezsp_io::TestExternalities::new(t);
|
|
|
|
ext.execute_with(|| {
|
|
pezframe_system::Pallet::<Runtime>::set_block_number(1u32.into());
|
|
});
|
|
|
|
ext
|
|
}
|
|
}
|
|
|
|
pub struct RuntimeHelper<Runtime, AllPalletsWithoutSystem>(
|
|
PhantomData<(Runtime, AllPalletsWithoutSystem)>,
|
|
);
|
|
/// Utility function that advances the chain to the desired block number.
|
|
/// If an author is provided, that author information is injected to all the blocks in the meantime.
|
|
impl<
|
|
Runtime: pezframe_system::Config + pezcumulus_pallet_teyrchain_system::Config + pezpallet_timestamp::Config,
|
|
AllPalletsWithoutSystem,
|
|
> RuntimeHelper<Runtime, AllPalletsWithoutSystem>
|
|
where
|
|
AccountIdOf<Runtime>:
|
|
Into<<<Runtime as pezframe_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
|
|
AllPalletsWithoutSystem:
|
|
OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
|
|
{
|
|
pub fn run_to_block(n: u32, author: AccountIdOf<Runtime>) -> HeaderFor<Runtime> {
|
|
let mut last_header = None;
|
|
loop {
|
|
let block_number = pezframe_system::Pallet::<Runtime>::block_number();
|
|
if block_number >= n.into() {
|
|
break;
|
|
}
|
|
// Set the new block number and author
|
|
|
|
// Inherent is not created at every block, don't finalize teyrchain
|
|
// system to avoid panicking.
|
|
let header = pezframe_system::Pallet::<Runtime>::finalize();
|
|
|
|
let pre_digest =
|
|
Digest { logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, author.encode())] };
|
|
pezframe_system::Pallet::<Runtime>::reset_events();
|
|
|
|
let next_block_number = block_number + 1u32.into();
|
|
pezframe_system::Pallet::<Runtime>::initialize(
|
|
&next_block_number,
|
|
&header.hash(),
|
|
&pre_digest,
|
|
);
|
|
AllPalletsWithoutSystem::on_initialize(next_block_number);
|
|
last_header = Some(header);
|
|
}
|
|
last_header.expect("run_to_block empty block range")
|
|
}
|
|
|
|
pub fn run_to_block_with_finalize(n: u32) -> HeaderFor<Runtime> {
|
|
let mut last_header = None;
|
|
loop {
|
|
let block_number = pezframe_system::Pallet::<Runtime>::block_number();
|
|
if block_number >= n.into() {
|
|
break;
|
|
}
|
|
// Set the new block number and author
|
|
let header = pezframe_system::Pallet::<Runtime>::finalize();
|
|
|
|
let pre_digest = Digest {
|
|
logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, block_number.encode())],
|
|
};
|
|
pezframe_system::Pallet::<Runtime>::reset_events();
|
|
|
|
let next_block_number = block_number + 1u32.into();
|
|
pezframe_system::Pallet::<Runtime>::initialize(
|
|
&next_block_number,
|
|
&header.hash(),
|
|
&pre_digest,
|
|
);
|
|
AllPalletsWithoutSystem::on_initialize(next_block_number);
|
|
|
|
let parent_head = HeadData(header.encode());
|
|
let sproof_builder = RelayStateSproofBuilder {
|
|
para_id: <Runtime>::SelfParaId::get(),
|
|
included_para_head: parent_head.clone().into(),
|
|
..Default::default()
|
|
};
|
|
|
|
let (relay_parent_storage_root, relay_chain_state) =
|
|
sproof_builder.into_state_root_and_proof();
|
|
let inherent_data = TeyrchainInherentData {
|
|
validation_data: PersistedValidationData {
|
|
parent_head,
|
|
relay_parent_number: (block_number.saturated_into::<u32>() * 2 + 1).into(),
|
|
relay_parent_storage_root,
|
|
max_pov_size: 100_000_000,
|
|
},
|
|
relay_chain_state,
|
|
downward_messages: Default::default(),
|
|
horizontal_messages: Default::default(),
|
|
relay_parent_descendants: Default::default(),
|
|
collator_peer_id: None,
|
|
};
|
|
|
|
let (inherent_data, downward_messages, horizontal_messages) =
|
|
deconstruct_teyrchain_inherent_data(inherent_data);
|
|
|
|
let _ = pezcumulus_pallet_teyrchain_system::Pallet::<Runtime>::set_validation_data(
|
|
Runtime::RuntimeOrigin::none(),
|
|
inherent_data,
|
|
InboundMessagesData::new(
|
|
downward_messages.into_abridged(&mut usize::MAX.clone()),
|
|
horizontal_messages.into_abridged(&mut usize::MAX.clone()),
|
|
),
|
|
);
|
|
let _ = pezpallet_timestamp::Pallet::<Runtime>::set(
|
|
Runtime::RuntimeOrigin::none(),
|
|
300_u32.into(),
|
|
);
|
|
AllPalletsWithoutSystem::on_finalize(next_block_number);
|
|
let header = pezframe_system::Pallet::<Runtime>::finalize();
|
|
last_header = Some(header);
|
|
}
|
|
last_header.expect("run_to_block empty block range")
|
|
}
|
|
|
|
pub fn root_origin() -> <Runtime as pezframe_system::Config>::RuntimeOrigin {
|
|
<Runtime as pezframe_system::Config>::RuntimeOrigin::root()
|
|
}
|
|
|
|
pub fn block_number() -> U256 {
|
|
pezframe_system::Pallet::<Runtime>::block_number().into()
|
|
}
|
|
|
|
pub fn origin_of(
|
|
account_id: AccountIdOf<Runtime>,
|
|
) -> <Runtime as pezframe_system::Config>::RuntimeOrigin {
|
|
<Runtime as pezframe_system::Config>::RuntimeOrigin::signed(account_id.into())
|
|
}
|
|
}
|
|
|
|
impl<XcmConfig: xcm_executor::Config, AllPalletsWithoutSystem>
|
|
RuntimeHelper<XcmConfig, AllPalletsWithoutSystem>
|
|
{
|
|
pub fn do_transfer(
|
|
from: Location,
|
|
to: Location,
|
|
(asset, amount): (Location, u128),
|
|
) -> Result<AssetsInHolding, XcmError> {
|
|
<XcmConfig::AssetTransactor as TransactAsset>::transfer_asset(
|
|
&Asset { id: AssetId(asset), fun: Fungible(amount) },
|
|
&from,
|
|
&to,
|
|
// We aren't able to track the XCM that initiated the fee deposit, so we create a
|
|
// fake message hash here
|
|
&XcmContext::with_message_id([0; 32]),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<
|
|
Runtime: pezpallet_xcm::Config + pezcumulus_pallet_teyrchain_system::Config,
|
|
AllPalletsWithoutSystem,
|
|
> RuntimeHelper<Runtime, AllPalletsWithoutSystem>
|
|
{
|
|
pub fn do_teleport_assets<HrmpChannelOpener>(
|
|
origin: <Runtime as pezframe_system::Config>::RuntimeOrigin,
|
|
dest: Location,
|
|
beneficiary: Location,
|
|
(asset, amount): (Location, u128),
|
|
open_hrmp_channel: Option<(u32, u32)>,
|
|
included_head: HeaderFor<Runtime>,
|
|
slot_digest: &[u8],
|
|
slot_durations: &SlotDurations,
|
|
) -> DispatchResult
|
|
where
|
|
HrmpChannelOpener: pezframe_support::inherent::ProvideInherent<
|
|
Call = pezcumulus_pallet_teyrchain_system::Call<Runtime>,
|
|
>,
|
|
{
|
|
// open hrmp (if needed)
|
|
if let Some((source_para_id, target_para_id)) = open_hrmp_channel {
|
|
mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
|
|
source_para_id.into(),
|
|
target_para_id.into(),
|
|
included_head,
|
|
slot_digest,
|
|
slot_durations,
|
|
);
|
|
}
|
|
|
|
// do teleport
|
|
<pezpallet_xcm::Pallet<Runtime>>::limited_teleport_assets(
|
|
origin,
|
|
Box::new(dest.into()),
|
|
Box::new(beneficiary.into()),
|
|
Box::new((AssetId(asset.clone()), amount).into()),
|
|
Box::new(AssetId(asset).into()),
|
|
Unlimited,
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<
|
|
Runtime: pezcumulus_pallet_teyrchain_system::Config + pezpallet_xcm::Config,
|
|
AllPalletsWithoutSystem,
|
|
> RuntimeHelper<Runtime, AllPalletsWithoutSystem>
|
|
{
|
|
#[deprecated(
|
|
note = "Will be removed after Aug 2025; It uses hard-coded `Location::parent()`, \
|
|
use `execute_as_governance_call` instead."
|
|
)]
|
|
pub fn execute_as_governance(call: Vec<u8>) -> Outcome {
|
|
// prepare xcm as governance will do
|
|
let xcm = Xcm(vec![
|
|
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
|
Transact {
|
|
origin_kind: OriginKind::Superuser,
|
|
call: call.into(),
|
|
fallback_max_weight: None,
|
|
},
|
|
ExpectTransactStatus(MaybeErrorCode::Success),
|
|
]);
|
|
|
|
// execute xcm as parent origin
|
|
let mut hash = xcm.using_encoded(pezsp_io::hashing::blake2_256);
|
|
<<Runtime as pezpallet_xcm::Config>::XcmExecutor>::prepare_and_execute(
|
|
Location::parent(),
|
|
xcm,
|
|
&mut hash,
|
|
Self::xcm_max_weight(XcmReceivedFrom::Parent),
|
|
Weight::zero(),
|
|
)
|
|
}
|
|
|
|
pub fn execute_as_governance_call<Call: Dispatchable + Encode>(
|
|
call: Call,
|
|
governance_origin: GovernanceOrigin<Call::RuntimeOrigin>,
|
|
) -> Result<(), Either<DispatchError, InstructionError>> {
|
|
// execute xcm as governance would send
|
|
let execute_xcm = |call: Call, governance_location, descend_origin| {
|
|
// prepare xcm
|
|
let xcm = if let Some(descend_origin) = descend_origin {
|
|
Xcm::builder_unsafe().descend_origin(descend_origin)
|
|
} else {
|
|
Xcm::builder_unsafe()
|
|
}
|
|
.unpaid_execution(Unlimited, None)
|
|
.transact(OriginKind::Superuser, None, call.encode())
|
|
.expect_transact_status(MaybeErrorCode::Success)
|
|
.build();
|
|
|
|
let xcm_max_weight = Self::xcm_max_weight_for_location(&governance_location);
|
|
let mut hash = xcm.using_encoded(pezsp_io::hashing::blake2_256);
|
|
|
|
<<Runtime as pezpallet_xcm::Config>::XcmExecutor>::prepare_and_execute(
|
|
governance_location,
|
|
xcm,
|
|
&mut hash,
|
|
xcm_max_weight,
|
|
Weight::zero(),
|
|
)
|
|
};
|
|
|
|
match governance_origin {
|
|
// we are simulating a case of receiving an XCM
|
|
// and Location::Here() is not a valid destionation for XcmRouter in the fist place
|
|
GovernanceOrigin::Location(location) if location == Location::here() => {
|
|
panic!("Location::here() not supported, use GovernanceOrigin::Origin instead")
|
|
},
|
|
GovernanceOrigin::Location(location) =>
|
|
execute_xcm(call, location, None).ensure_complete().map_err(Either::Right),
|
|
GovernanceOrigin::LocationAndDescendOrigin(location, descend_origin) =>
|
|
execute_xcm(call, location, Some(descend_origin))
|
|
.ensure_complete()
|
|
.map_err(Either::Right),
|
|
GovernanceOrigin::Origin(origin) =>
|
|
call.dispatch(origin).map(|_| ()).map_err(|e| Either::Left(e.error)),
|
|
}
|
|
}
|
|
|
|
pub fn execute_as_origin<Call: GetDispatchInfo + Encode>(
|
|
(origin, origin_kind): (Location, OriginKind),
|
|
call: Call,
|
|
maybe_buy_execution_fee: Option<Asset>,
|
|
) -> Outcome {
|
|
let mut instructions = if let Some(buy_execution_fee) = maybe_buy_execution_fee {
|
|
vec![
|
|
WithdrawAsset(buy_execution_fee.clone().into()),
|
|
BuyExecution { fees: buy_execution_fee.clone(), weight_limit: Unlimited },
|
|
]
|
|
} else {
|
|
vec![UnpaidExecution { check_origin: None, weight_limit: Unlimited }]
|
|
};
|
|
|
|
// prepare `Transact` xcm
|
|
instructions.extend(vec![
|
|
Transact { origin_kind, call: call.encode().into(), fallback_max_weight: None },
|
|
ExpectTransactStatus(MaybeErrorCode::Success),
|
|
]);
|
|
let xcm = Xcm(instructions);
|
|
let xcm_max_weight = Self::xcm_max_weight_for_location(&origin);
|
|
|
|
// execute xcm as parent origin
|
|
let mut hash = xcm.using_encoded(pezsp_io::hashing::blake2_256);
|
|
<<Runtime as pezpallet_xcm::Config>::XcmExecutor>::prepare_and_execute(
|
|
origin,
|
|
xcm,
|
|
&mut hash,
|
|
xcm_max_weight,
|
|
Weight::zero(),
|
|
)
|
|
}
|
|
}
|
|
|
|
/// Enum representing governance origin/location.
|
|
#[derive(Clone)]
|
|
pub enum GovernanceOrigin<RuntimeOrigin> {
|
|
Location(Location),
|
|
LocationAndDescendOrigin(Location, InteriorLocation),
|
|
Origin(RuntimeOrigin),
|
|
}
|
|
|
|
pub enum XcmReceivedFrom {
|
|
Parent,
|
|
Sibling,
|
|
}
|
|
|
|
impl<TeyrchainSystem: pezcumulus_pallet_teyrchain_system::Config, AllPalletsWithoutSystem>
|
|
RuntimeHelper<TeyrchainSystem, AllPalletsWithoutSystem>
|
|
{
|
|
pub fn xcm_max_weight(from: XcmReceivedFrom) -> Weight {
|
|
match from {
|
|
XcmReceivedFrom::Parent => TeyrchainSystem::ReservedDmpWeight::get(),
|
|
XcmReceivedFrom::Sibling => TeyrchainSystem::ReservedXcmpWeight::get(),
|
|
}
|
|
}
|
|
|
|
pub fn xcm_max_weight_for_location(location: &Location) -> Weight {
|
|
Self::xcm_max_weight(if location == &Location::parent() {
|
|
XcmReceivedFrom::Parent
|
|
} else {
|
|
XcmReceivedFrom::Sibling
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<Runtime: pezframe_system::Config + pezpallet_xcm::Config, AllPalletsWithoutSystem>
|
|
RuntimeHelper<Runtime, AllPalletsWithoutSystem>
|
|
{
|
|
pub fn assert_pallet_xcm_event_outcome(
|
|
unwrap_pallet_xcm_event: &Box<dyn Fn(Vec<u8>) -> Option<pezpallet_xcm::Event<Runtime>>>,
|
|
assert_outcome: fn(Outcome),
|
|
) {
|
|
assert_outcome(Self::get_pallet_xcm_event_outcome(unwrap_pallet_xcm_event));
|
|
}
|
|
|
|
pub fn get_pallet_xcm_event_outcome(
|
|
unwrap_pallet_xcm_event: &Box<dyn Fn(Vec<u8>) -> Option<pezpallet_xcm::Event<Runtime>>>,
|
|
) -> Outcome {
|
|
<pezframe_system::Pallet<Runtime>>::events()
|
|
.into_iter()
|
|
.filter_map(|e| unwrap_pallet_xcm_event(e.event.encode()))
|
|
.find_map(|e| match e {
|
|
pezpallet_xcm::Event::Attempted { outcome } => Some(outcome),
|
|
_ => None,
|
|
})
|
|
.expect("No `pezpallet_xcm::Event::Attempted(outcome)` event found!")
|
|
}
|
|
}
|
|
|
|
impl<
|
|
Runtime: pezframe_system::Config + pezcumulus_pallet_xcmp_queue::Config,
|
|
AllPalletsWithoutSystem,
|
|
> RuntimeHelper<Runtime, AllPalletsWithoutSystem>
|
|
{
|
|
pub fn xcmp_queue_message_sent(
|
|
unwrap_xcmp_queue_event: Box<
|
|
dyn Fn(Vec<u8>) -> Option<pezcumulus_pallet_xcmp_queue::Event<Runtime>>,
|
|
>,
|
|
) -> Option<XcmHash> {
|
|
<pezframe_system::Pallet<Runtime>>::events()
|
|
.into_iter()
|
|
.filter_map(|e| unwrap_xcmp_queue_event(e.event.encode()))
|
|
.find_map(|e| match e {
|
|
pezcumulus_pallet_xcmp_queue::Event::XcmpMessageSent { message_hash } =>
|
|
Some(message_hash),
|
|
_ => None,
|
|
})
|
|
}
|
|
}
|
|
|
|
pub fn assert_metadata<Fungibles, AccountId>(
|
|
asset_id: impl Into<Fungibles::AssetId> + Clone,
|
|
expected_name: &str,
|
|
expected_symbol: &str,
|
|
expected_decimals: u8,
|
|
) where
|
|
Fungibles: pezframe_support::traits::fungibles::metadata::Inspect<AccountId>
|
|
+ pezframe_support::traits::fungibles::Inspect<AccountId>,
|
|
{
|
|
assert_eq!(Fungibles::name(asset_id.clone().into()), Vec::from(expected_name),);
|
|
assert_eq!(Fungibles::symbol(asset_id.clone().into()), Vec::from(expected_symbol),);
|
|
assert_eq!(Fungibles::decimals(asset_id.into()), expected_decimals);
|
|
}
|
|
|
|
pub fn assert_total<Fungibles, AccountId>(
|
|
asset_id: impl Into<Fungibles::AssetId> + Clone,
|
|
expected_total_issuance: impl Into<Fungibles::Balance>,
|
|
expected_active_issuance: impl Into<Fungibles::Balance>,
|
|
) where
|
|
Fungibles: pezframe_support::traits::fungibles::metadata::Inspect<AccountId>
|
|
+ pezframe_support::traits::fungibles::Inspect<AccountId>,
|
|
{
|
|
assert_eq!(Fungibles::total_issuance(asset_id.clone().into()), expected_total_issuance.into());
|
|
assert_eq!(Fungibles::active_issuance(asset_id.into()), expected_active_issuance.into());
|
|
}
|
|
|
|
/// Helper function which emulates opening HRMP channel which is needed for `XcmpQueue` to pass.
|
|
///
|
|
/// Calls teyrchain-system's `create_inherent` in case the channel hasn't been opened before, and
|
|
/// thus requires additional parameters for validating it: latest included teyrchain head and
|
|
/// teyrchain AuRa-slot.
|
|
///
|
|
/// AuRa consensus hook expects pallets to be initialized, before calling this function make sure to
|
|
/// `run_to_block` at least once.
|
|
pub fn mock_open_hrmp_channel<
|
|
C: pezcumulus_pallet_teyrchain_system::Config,
|
|
T: ProvideInherent<Call = pezcumulus_pallet_teyrchain_system::Call<C>>,
|
|
>(
|
|
sender: ParaId,
|
|
recipient: ParaId,
|
|
included_head: HeaderFor<C>,
|
|
mut slot_digest: &[u8],
|
|
slot_durations: &SlotDurations,
|
|
) {
|
|
let slot = Slot::decode(&mut slot_digest).expect("failed to decode digest");
|
|
// Convert para slot to relay chain.
|
|
let timestamp = slot.saturating_mul(slot_durations.para.as_millis());
|
|
let relay_slot = Slot::from_timestamp(timestamp.into(), slot_durations.relay);
|
|
|
|
let n = 1_u32;
|
|
let mut sproof_builder = RelayStateSproofBuilder {
|
|
para_id: sender,
|
|
included_para_head: Some(HeadData(included_head.encode())),
|
|
hrmp_egress_channel_index: Some(vec![recipient]),
|
|
current_slot: relay_slot,
|
|
..Default::default()
|
|
};
|
|
sproof_builder.hrmp_channels.insert(
|
|
HrmpChannelId { sender, recipient },
|
|
AbridgedHrmpChannel {
|
|
max_capacity: 10,
|
|
max_total_size: 10_000_000_u32,
|
|
max_message_size: 10_000_000_u32,
|
|
msg_count: 0,
|
|
total_size: 0_u32,
|
|
mqc_head: None,
|
|
},
|
|
);
|
|
|
|
let (relay_parent_storage_root, relay_chain_state) = sproof_builder.into_state_root_and_proof();
|
|
let vfp = PersistedValidationData {
|
|
relay_parent_number: n as RelayChainBlockNumber,
|
|
relay_parent_storage_root,
|
|
..Default::default()
|
|
};
|
|
// It is insufficient to push the validation function params
|
|
// to storage; they must also be included in the inherent data.
|
|
let inherent_data = {
|
|
let mut inherent_data = InherentData::default();
|
|
let system_inherent_data = TeyrchainInherentData {
|
|
validation_data: vfp,
|
|
relay_chain_state,
|
|
downward_messages: Default::default(),
|
|
horizontal_messages: Default::default(),
|
|
relay_parent_descendants: Default::default(),
|
|
collator_peer_id: None,
|
|
};
|
|
inherent_data
|
|
.put_data(
|
|
pezcumulus_primitives_teyrchain_inherent::INHERENT_IDENTIFIER,
|
|
&system_inherent_data,
|
|
)
|
|
.expect("failed to put VFP inherent");
|
|
inherent_data
|
|
};
|
|
|
|
// execute the block
|
|
T::create_inherent(&inherent_data)
|
|
.expect("got an inherent")
|
|
.dispatch_bypass_filter(RawOrigin::None.into())
|
|
.expect("dispatch succeeded");
|
|
}
|
|
|
|
impl<HrmpChannelSource: pezcumulus_primitives_core::XcmpMessageSource, AllPalletsWithoutSystem>
|
|
RuntimeHelper<HrmpChannelSource, AllPalletsWithoutSystem>
|
|
{
|
|
pub fn take_xcm(sent_to_para_id: ParaId) -> Option<VersionedXcm<()>> {
|
|
match HrmpChannelSource::take_outbound_messages(10)[..] {
|
|
[(para_id, ref mut xcm_message_data)] if para_id.eq(&sent_to_para_id.into()) => {
|
|
let mut xcm_message_data = &xcm_message_data[..];
|
|
// decode
|
|
let _ = XcmpMessageFormat::decode(&mut xcm_message_data).expect("valid format");
|
|
VersionedXcm::<()>::decode_with_depth_limit(
|
|
MAX_XCM_DECODE_DEPTH,
|
|
&mut xcm_message_data,
|
|
)
|
|
.map(|x| Some(x))
|
|
.expect("result with xcm")
|
|
},
|
|
_ => return None,
|
|
}
|
|
}
|
|
}
|