parachain.rs 14.8 KiB
Newer Older
// Copyright (C) Parity Technologies (UK) Ltd.
Shaun W's avatar
Shaun W committed
// 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/>.

//! Parachain runtime mock.

use codec::{Decode, Encode};
use core::marker::PhantomData;
Shaun W's avatar
Shaun W committed
use frame_support::{
	construct_runtime, derive_impl, parameter_types,
	traits::{ContainsPair, EnsureOrigin, EnsureOriginWithArg, Everything, EverythingBut, Nothing},
	weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight},
Shaun W's avatar
Shaun W committed
};

use frame_system::EnsureRoot;
use sp_core::{ConstU32, H256};
Shaun W's avatar
Shaun W committed
use sp_runtime::{
	traits::{Get, Hash, IdentityLookup},
Shaun W's avatar
Shaun W committed
	AccountId32,
};
use sp_std::prelude::*;
Shaun W's avatar
Shaun W committed

use pallet_xcm::XcmPassthrough;
use polkadot_core_primitives::BlockNumber as RelayBlockNumber;
use polkadot_parachain_primitives::primitives::{
Shaun W's avatar
Shaun W committed
	DmpMessageHandler, Id as ParaId, Sibling, XcmpMessageFormat, XcmpMessageHandler,
};
Gavin Wood's avatar
Gavin Wood committed
use xcm::{latest::prelude::*, VersionedXcm};
Shaun W's avatar
Shaun W committed
use xcm_builder::{
Gavin Wood's avatar
Gavin Wood committed
	Account32Hash, AccountId32Aliases, AllowUnpaidExecutionFrom, ConvertedConcreteId,
	EnsureDecodableXcm, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds,
	FrameTransactionalProcessor, FungibleAdapter, IsConcrete, NativeAsset, NoChecking,
	NonFungiblesAdapter, ParentIsPreset, SiblingParachainConvertsVia, SignedAccountId32AsNative,
	SignedToAccountId32, SovereignSignedViaLocation,
Gavin Wood's avatar
Gavin Wood committed
};
use xcm_executor::{
	traits::{ConvertLocation, JustTry},
Gavin Wood's avatar
Gavin Wood committed
	Config, XcmExecutor,
Shaun W's avatar
Shaun W committed
};
Gavin Wood's avatar
Gavin Wood committed

pub type SovereignAccountOf = (
Gavin Wood's avatar
Gavin Wood committed
	AccountId32Aliases<RelayNetwork, AccountId>,
	ParentIsPreset<AccountId>,
);
Shaun W's avatar
Shaun W committed

pub type AccountId = AccountId32;
pub type Balance = u128;

parameter_types! {
	pub const BlockHashCount: u64 = 250;
}

#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
Shaun W's avatar
Shaun W committed
impl frame_system::Config for Runtime {
Sergej Sakac's avatar
Sergej Sakac committed
	type RuntimeOrigin = RuntimeOrigin;
	type RuntimeCall = RuntimeCall;
	type Nonce = u64;
Shaun W's avatar
Shaun W committed
	type Hash = H256;
	type Hashing = ::sp_runtime::traits::BlakeTwo256;
	type AccountId = AccountId;
	type Lookup = IdentityLookup<Self::AccountId>;
	type RuntimeEvent = RuntimeEvent;
Shaun W's avatar
Shaun W committed
	type BlockHashCount = BlockHashCount;
	type BlockWeights = ();
	type BlockLength = ();
	type Version = ();
	type PalletInfo = PalletInfo;
	type AccountData = pallet_balances::AccountData<Balance>;
	type OnNewAccount = ();
	type OnKilledAccount = ();
	type DbWeight = ();
	type BaseCallFilter = Everything;
Shaun W's avatar
Shaun W committed
	type SystemWeightInfo = ();
	type SS58Prefix = ();
	type OnSetCode = ();
	type MaxConsumers = ConstU32<16>;
Shaun W's avatar
Shaun W committed
}

parameter_types! {
	pub ExistentialDeposit: Balance = 1;
	pub const MaxLocks: u32 = 50;
	pub const MaxReserves: u32 = 50;
}

impl pallet_balances::Config for Runtime {
	type MaxLocks = MaxLocks;
	type Balance = Balance;
	type RuntimeEvent = RuntimeEvent;
Shaun W's avatar
Shaun W committed
	type DustRemoval = ();
	type ExistentialDeposit = ExistentialDeposit;
	type AccountStore = System;
	type WeightInfo = ();
	type MaxReserves = MaxReserves;
	type ReserveIdentifier = [u8; 8];
	type RuntimeHoldReason = RuntimeHoldReason;
	type RuntimeFreezeReason = RuntimeFreezeReason;
	type FreezeIdentifier = ();
	type MaxFreezes = ConstU32<0>;
Shaun W's avatar
Shaun W committed
}

Gavin Wood's avatar
Gavin Wood committed
#[cfg(feature = "runtime-benchmarks")]
pub struct UniquesHelper;
#[cfg(feature = "runtime-benchmarks")]
Francisco Aguirre's avatar
Francisco Aguirre committed
impl pallet_uniques::BenchmarkHelper<Location, AssetInstance> for UniquesHelper {
	fn collection(i: u16) -> Location {
Gavin Wood's avatar
Gavin Wood committed
		GeneralIndex(i as u128).into()
	}
	fn item(i: u16) -> AssetInstance {
		AssetInstance::Index(i as u128)
	}
}

impl pallet_uniques::Config for Runtime {
	type RuntimeEvent = RuntimeEvent;
Francisco Aguirre's avatar
Francisco Aguirre committed
	type CollectionId = Location;
Gavin Wood's avatar
Gavin Wood committed
	type ItemId = AssetInstance;
	type Currency = Balances;
	type CreateOrigin = ForeignCreators;
	type ForceOrigin = frame_system::EnsureRoot<AccountId>;
	type CollectionDeposit = frame_support::traits::ConstU128<1_000>;
	type ItemDeposit = frame_support::traits::ConstU128<1_000>;
	type MetadataDepositBase = frame_support::traits::ConstU128<1_000>;
	type AttributeDepositBase = frame_support::traits::ConstU128<1_000>;
	type DepositPerByte = frame_support::traits::ConstU128<1>;
	type StringLimit = ConstU32<64>;
	type KeyLimit = ConstU32<64>;
	type ValueLimit = ConstU32<128>;
Gavin Wood's avatar
Gavin Wood committed
	type Locker = ();
	type WeightInfo = ();
	#[cfg(feature = "runtime-benchmarks")]
	type Helper = UniquesHelper;
}

// `EnsureOriginWithArg` impl for `CreateOrigin` which allows only XCM origins
// which are locations containing the class location.
pub struct ForeignCreators;
Francisco Aguirre's avatar
Francisco Aguirre committed
impl EnsureOriginWithArg<RuntimeOrigin, Location> for ForeignCreators {
Gavin Wood's avatar
Gavin Wood committed
	type Success = AccountId;

	fn try_origin(
		o: RuntimeOrigin,
Francisco Aguirre's avatar
Francisco Aguirre committed
		a: &Location,
Gavin Wood's avatar
Gavin Wood committed
	) -> sp_std::result::Result<Self::Success, RuntimeOrigin> {
		let origin_location = pallet_xcm::EnsureXcm::<Everything>::try_origin(o.clone())?;
		if !a.starts_with(&origin_location) {
			return Err(o)
		}
		SovereignAccountOf::convert_location(&origin_location).ok_or(o)
Gavin Wood's avatar
Gavin Wood committed
	}

	#[cfg(feature = "runtime-benchmarks")]
Francisco Aguirre's avatar
Francisco Aguirre committed
	fn try_successful_origin(a: &Location) -> Result<RuntimeOrigin, ()> {
		Ok(pallet_xcm::Origin::Xcm(a.clone()).into())
Gavin Wood's avatar
Gavin Wood committed
	}
}

Shaun W's avatar
Shaun W committed
parameter_types! {
	pub const ReservedXcmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0);
	pub const ReservedDmpWeight: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(4), 0);
Shaun W's avatar
Shaun W committed
}

parameter_types! {
Francisco Aguirre's avatar
Francisco Aguirre committed
	pub const KsmLocation: Location = Location::parent();
Shaun W's avatar
Shaun W committed
	pub const RelayNetwork: NetworkId = NetworkId::Kusama;
	pub UniversalLocation: InteriorLocation = [GlobalConsensus(RelayNetwork::get()), Parachain(MsgQueue::parachain_id().into())].into();
Shaun W's avatar
Shaun W committed
}

pub type LocationToAccountId = (
Shaun W's avatar
Shaun W committed
	SiblingParachainConvertsVia<Sibling, AccountId>,
	AccountId32Aliases<RelayNetwork, AccountId>,
Gavin Wood's avatar
Gavin Wood committed
	Account32Hash<(), AccountId>,
Shaun W's avatar
Shaun W committed
);

pub type XcmOriginToCallOrigin = (
Sergej Sakac's avatar
Sergej Sakac committed
	SovereignSignedViaLocation<LocationToAccountId, RuntimeOrigin>,
	SignedAccountId32AsNative<RelayNetwork, RuntimeOrigin>,
	XcmPassthrough<RuntimeOrigin>,
Shaun W's avatar
Shaun W committed
);

parameter_types! {
Gavin Wood's avatar
Gavin Wood committed
	pub const UnitWeightCost: Weight = Weight::from_parts(1, 1);
Francisco Aguirre's avatar
Francisco Aguirre committed
	pub KsmPerSecondPerByte: (AssetId, u128, u128) = (AssetId(Parent.into()), 1, 1);
Gavin Wood's avatar
Gavin Wood committed
	pub const MaxAssetsIntoHolding: u32 = 64;
Francisco Aguirre's avatar
Francisco Aguirre committed
	pub ForeignPrefix: Location = (Parent,).into();
Shaun W's avatar
Shaun W committed
}

Gavin Wood's avatar
Gavin Wood committed
pub type LocalAssetTransactor = (
	FungibleAdapter<Balances, IsConcrete<KsmLocation>, LocationToAccountId, AccountId, ()>,
Gavin Wood's avatar
Gavin Wood committed
	NonFungiblesAdapter<
		ForeignUniques,
Francisco Aguirre's avatar
Francisco Aguirre committed
		ConvertedConcreteId<Location, AssetInstance, JustTry, JustTry>,
Gavin Wood's avatar
Gavin Wood committed
		SovereignAccountOf,
		AccountId,
		NoChecking,
		(),
	>,
);
Shaun W's avatar
Shaun W committed

pub type XcmRouter = EnsureDecodableXcm<super::ParachainXcmRouter<MsgQueue>>;
pub type Barrier = AllowUnpaidExecutionFrom<Everything>;
Shaun W's avatar
Shaun W committed

Gavin Wood's avatar
Gavin Wood committed
parameter_types! {
Francisco Aguirre's avatar
Francisco Aguirre committed
	pub NftCollectionOne: AssetFilter
		= Wild(AllOf { fun: WildNonFungible, id: AssetId((Parent, GeneralIndex(1)).into()) });
	pub NftCollectionOneForRelay: (AssetFilter, Location)
Gavin Wood's avatar
Gavin Wood committed
		= (NftCollectionOne::get(), (Parent,).into());
}
pub type TrustedTeleporters = xcm_builder::Case<NftCollectionOneForRelay>;
pub type TrustedReserves = EverythingBut<xcm_builder::Case<NftCollectionOneForRelay>>;

Shaun W's avatar
Shaun W committed
pub struct XcmConfig;
impl Config for XcmConfig {
	type RuntimeCall = RuntimeCall;
Shaun W's avatar
Shaun W committed
	type XcmSender = XcmRouter;
	type AssetTransactor = LocalAssetTransactor;
	type OriginConverter = XcmOriginToCallOrigin;
Gavin Wood's avatar
Gavin Wood committed
	type IsReserve = (NativeAsset, TrustedReserves);
	type IsTeleporter = TrustedTeleporters;
	type UniversalLocation = UniversalLocation;
Shaun W's avatar
Shaun W committed
	type Barrier = Barrier;
	type Weigher = FixedWeightBounds<UnitWeightCost, RuntimeCall, MaxInstructions>;
Gavin Wood's avatar
Gavin Wood committed
	type Trader = FixedRateOfFungible<KsmPerSecondPerByte, ()>;
Shaun W's avatar
Shaun W committed
	type ResponseHandler = ();
	type AssetLocker = PolkadotXcm;
Gavin Wood's avatar
Gavin Wood committed
	type AssetExchanger = ();
	type SubscriptionService = ();
Gavin Wood's avatar
Gavin Wood committed
	type PalletInstancesInfo = ();
	type FeeManager = ();
	type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
	type MessageExporter = ();
	type UniversalAliases = Nothing;
	type CallDispatcher = RuntimeCall;
	type SafeCallFilter = Everything;
	type Aliasers = Nothing;
	type TransactionalProcessor = FrameTransactionalProcessor;
	type HrmpNewChannelOpenRequestHandler = ();
	type HrmpChannelAcceptedHandler = ();
	type HrmpChannelClosingHandler = ();
Shaun W's avatar
Shaun W committed
}

#[frame_support::pallet]
pub mod mock_msg_queue {
	use super::*;
	use frame_support::pallet_prelude::*;

	#[pallet::config]
	pub trait Config: frame_system::Config {
		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
		type XcmExecutor: ExecuteXcm<Self::RuntimeCall>;
Shaun W's avatar
Shaun W committed
	}

	#[pallet::call]
	impl<T: Config> Pallet<T> {}

	#[pallet::pallet]
	#[pallet::without_storage_info]
Shaun W's avatar
Shaun W committed
	pub struct Pallet<T>(_);

	#[pallet::storage]
	#[pallet::getter(fn parachain_id)]
	pub(super) type ParachainId<T: Config> = StorageValue<_, ParaId, ValueQuery>;

	#[pallet::storage]
	#[pallet::getter(fn received_dmp)]
	/// A queue of received DMP messages
	pub(super) type ReceivedDmp<T: Config> = StorageValue<_, Vec<Xcm<T::RuntimeCall>>, ValueQuery>;
Shaun W's avatar
Shaun W committed
	impl<T: Config> Get<ParaId> for Pallet<T> {
		fn get() -> ParaId {
			Self::parachain_id()
		}
	}

	pub type MessageId = [u8; 32];

	#[pallet::event]
	#[pallet::generate_deposit(pub(super) fn deposit_event)]
	pub enum Event<T: Config> {
		// XCMP
		/// Some XCM was executed OK.
Shaun W's avatar
Shaun W committed
		Success(Option<T::Hash>),
		/// Some XCM failed.
		Fail(Option<T::Hash>, XcmError),
		/// Bad XCM version used.
		BadVersion(Option<T::Hash>),
		/// Bad XCM format used.
		BadFormat(Option<T::Hash>),

		// DMP
		/// Downward message is invalid XCM.
		InvalidFormat(MessageId),
		/// Downward message is unsupported version of XCM.
		UnsupportedVersion(MessageId),
		/// Downward message executed with the given outcome.
		ExecutedDownward(MessageId, Outcome),
	}

	impl<T: Config> Pallet<T> {
		pub fn set_para_id(para_id: ParaId) {
			ParachainId::<T>::put(para_id);
		}

		fn handle_xcmp_message(
			sender: ParaId,
			_sent_at: RelayBlockNumber,
			xcm: VersionedXcm<T::RuntimeCall>,
Shaun W's avatar
Shaun W committed
			max_weight: Weight,
		) -> Result<Weight, XcmError> {
			let hash = Encode::using_encoded(&xcm, T::Hashing::hash);
Francisco Aguirre's avatar
Francisco Aguirre committed
			let mut message_hash = Encode::using_encoded(&xcm, sp_io::hashing::blake2_256);
			let (result, event) = match Xcm::<T::RuntimeCall>::try_from(xcm) {
Shaun W's avatar
Shaun W committed
				Ok(xcm) => {
Gavin Wood's avatar
Gavin Wood committed
					let location = (Parent, Parachain(sender.into()));
Francisco Aguirre's avatar
Francisco Aguirre committed
					match T::XcmExecutor::prepare_and_execute(
						location,
						xcm,
						&mut message_hash,
						max_weight,
						Weight::zero(),
					) {
						Outcome::Error { error } => (Err(error), Event::Fail(Some(hash), error)),
						Outcome::Complete { used } => (Ok(used), Event::Success(Some(hash))),
Shaun W's avatar
Shaun W committed
						// As far as the caller is concerned, this was dispatched without error, so
						// we just report the weight used.
Francisco Aguirre's avatar
Francisco Aguirre committed
						Outcome::Incomplete { used, error } =>
							(Ok(used), Event::Fail(Some(hash), error)),
Shaun W's avatar
Shaun W committed
					}
				},
				Err(()) => (Err(XcmError::UnhandledXcmVersion), Event::BadVersion(Some(hash))),
			};
			Self::deposit_event(event);
			result
		}
	}

	impl<T: Config> XcmpMessageHandler for Pallet<T> {
		fn handle_xcmp_messages<'a, I: Iterator<Item = (ParaId, RelayBlockNumber, &'a [u8])>>(
			iter: I,
			max_weight: Weight,
		) -> Weight {
			for (sender, sent_at, data) in iter {
				let mut data_ref = data;
				let _ = XcmpMessageFormat::decode(&mut data_ref)
					.expect("Simulator encodes with versioned xcm format; qed");

				let mut remaining_fragments = data_ref;
Shaun W's avatar
Shaun W committed
				while !remaining_fragments.is_empty() {
					if let Ok(xcm) =
						VersionedXcm::<T::RuntimeCall>::decode(&mut remaining_fragments)
					{
Shaun W's avatar
Shaun W committed
						let _ = Self::handle_xcmp_message(sender, sent_at, xcm, max_weight);
					} else {
						debug_assert!(false, "Invalid incoming XCMP message data");
					}
				}
			}
			max_weight
		}
	}

	impl<T: Config> DmpMessageHandler for Pallet<T> {
		fn handle_dmp_messages(
			iter: impl Iterator<Item = (RelayBlockNumber, Vec<u8>)>,
			limit: Weight,
		) -> Weight {
			for (_i, (_sent_at, data)) in iter.enumerate() {
Francisco Aguirre's avatar
Francisco Aguirre committed
				let mut id = sp_io::hashing::blake2_256(&data[..]);
Gavin Wood's avatar
Gavin Wood committed
				let maybe_versioned = VersionedXcm::<T::RuntimeCall>::decode(&mut &data[..]);
				match maybe_versioned {
Shaun W's avatar
Shaun W committed
					Err(_) => {
						Self::deposit_event(Event::InvalidFormat(id));
					},
Gavin Wood's avatar
Gavin Wood committed
					Ok(versioned) => match Xcm::try_from(versioned) {
						Err(()) => Self::deposit_event(Event::UnsupportedVersion(id)),
						Ok(x) => {
Francisco Aguirre's avatar
Francisco Aguirre committed
							let outcome = T::XcmExecutor::prepare_and_execute(
								Parent,
								x.clone(),
								&mut id,
								limit,
								Weight::zero(),
							);
Gavin Wood's avatar
Gavin Wood committed
							<ReceivedDmp<T>>::append(x);
							Self::deposit_event(Event::ExecutedDownward(id, outcome));
						},
Shaun W's avatar
Shaun W committed
					},
				}
			}
			limit
		}
	}
}

impl mock_msg_queue::Config for Runtime {
	type RuntimeEvent = RuntimeEvent;
Shaun W's avatar
Shaun W committed
	type XcmExecutor = XcmExecutor<XcmConfig>;
}

Sergej Sakac's avatar
Sergej Sakac committed
pub type LocalOriginToLocation = SignedToAccountId32<RuntimeOrigin, AccountId, RelayNetwork>;
Shaun W's avatar
Shaun W committed

pub struct TrustedLockerCase<T>(PhantomData<T>);
Francisco Aguirre's avatar
Francisco Aguirre committed
impl<T: Get<(Location, AssetFilter)>> ContainsPair<Location, Asset> for TrustedLockerCase<T> {
	fn contains(origin: &Location, asset: &Asset) -> bool {
		let (o, a) = T::get();
		a.matches(asset) && &o == origin
	}
}

parameter_types! {
Francisco Aguirre's avatar
Francisco Aguirre committed
	pub RelayTokenForRelay: (Location, AssetFilter) = (Parent.into(), Wild(AllOf { id: AssetId(Parent.into()), fun: WildFungible }));
}

pub type TrustedLockers = TrustedLockerCase<RelayTokenForRelay>;

Shaun W's avatar
Shaun W committed
impl pallet_xcm::Config for Runtime {
	type RuntimeEvent = RuntimeEvent;
Sergej Sakac's avatar
Sergej Sakac committed
	type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
Shaun W's avatar
Shaun W committed
	type XcmRouter = XcmRouter;
Sergej Sakac's avatar
Sergej Sakac committed
	type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
	type XcmExecuteFilter = Everything;
Shaun W's avatar
Shaun W committed
	type XcmExecutor = XcmExecutor<XcmConfig>;
	type XcmTeleportFilter = Nothing;
	type XcmReserveTransferFilter = Everything;
	type Weigher = FixedWeightBounds<UnitWeightCost, RuntimeCall, MaxInstructions>;
Gavin Wood's avatar
Gavin Wood committed
	type UniversalLocation = UniversalLocation;
Sergej Sakac's avatar
Sergej Sakac committed
	type RuntimeOrigin = RuntimeOrigin;
	type RuntimeCall = RuntimeCall;
	const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
	type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
Gavin Wood's avatar
Gavin Wood committed
	type Currency = Balances;
	type CurrencyMatcher = ();
	type TrustedLockers = TrustedLockers;
Gavin Wood's avatar
Gavin Wood committed
	type SovereignAccountOf = LocationToAccountId;
	type MaxLockers = ConstU32<8>;
	type MaxRemoteLockConsumers = ConstU32<0>;
	type RemoteLockConsumerIdentifier = ();
Gavin Wood's avatar
Gavin Wood committed
	type WeightInfo = pallet_xcm::TestWeightInfo;
	type AdminOrigin = EnsureRoot<AccountId>;
Shaun W's avatar
Shaun W committed
}

type Block = frame_system::mocking::MockBlock<Runtime>;

construct_runtime!(
Shaun W's avatar
Shaun W committed
	{
		System: frame_system,
		Balances: pallet_balances,
		MsgQueue: mock_msg_queue,
		PolkadotXcm: pallet_xcm,
		ForeignUniques: pallet_uniques,
Shaun W's avatar
Shaun W committed
	}
);