diff --git a/Cargo.lock b/Cargo.lock
index 1fe17a5cf6..1c785e0d91 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6684,6 +6684,66 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
+[[package]]
+name = "penpal-runtime"
+version = "0.1.0"
+dependencies = [
+ "cumulus-pallet-aura-ext",
+ "cumulus-pallet-dmp-queue",
+ "cumulus-pallet-parachain-system",
+ "cumulus-pallet-session-benchmarking",
+ "cumulus-pallet-xcm",
+ "cumulus-pallet-xcmp-queue",
+ "cumulus-primitives-core",
+ "cumulus-primitives-timestamp",
+ "cumulus-primitives-utility",
+ "frame-benchmarking",
+ "frame-executive",
+ "frame-support",
+ "frame-system",
+ "frame-system-benchmarking",
+ "frame-system-rpc-runtime-api",
+ "frame-try-runtime",
+ "hex-literal",
+ "log",
+ "pallet-asset-tx-payment",
+ "pallet-assets",
+ "pallet-aura",
+ "pallet-authorship",
+ "pallet-balances",
+ "pallet-collator-selection",
+ "pallet-session",
+ "pallet-sudo",
+ "pallet-timestamp",
+ "pallet-transaction-payment",
+ "pallet-transaction-payment-rpc-runtime-api",
+ "pallet-xcm",
+ "parachain-info",
+ "parity-scale-codec",
+ "polkadot-parachain 0.9.25",
+ "polkadot-primitives",
+ "polkadot-runtime-common",
+ "scale-info",
+ "serde",
+ "smallvec",
+ "sp-api",
+ "sp-block-builder",
+ "sp-consensus-aura",
+ "sp-core",
+ "sp-inherents",
+ "sp-io",
+ "sp-offchain",
+ "sp-runtime",
+ "sp-session",
+ "sp-std",
+ "sp-transaction-pool",
+ "sp-version",
+ "substrate-wasm-builder",
+ "xcm",
+ "xcm-builder",
+ "xcm-executor",
+]
+
[[package]]
name = "percent-encoding"
version = "2.1.0"
@@ -7590,6 +7650,7 @@ dependencies = [
"pallet-transaction-payment-rpc",
"parachains-common",
"parity-scale-codec",
+ "penpal-runtime",
"polkadot-cli",
"polkadot-parachain 0.9.26",
"polkadot-primitives",
diff --git a/Cargo.toml b/Cargo.toml
index 9c6bd5af20..c21af466f0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -35,6 +35,7 @@ members = [
"parachains/runtimes/assets/statemine",
"parachains/runtimes/assets/westmint",
"parachains/runtimes/contracts/contracts-rococo",
+ "parachains/runtimes/testing/penpal",
"test/client",
"test/relay-sproof-builder",
"test/relay-validation-worker-provider",
diff --git a/parachains/runtimes/testing/penpal/Cargo.toml b/parachains/runtimes/testing/penpal/Cargo.toml
new file mode 100644
index 0000000000..65fdbc80fe
--- /dev/null
+++ b/parachains/runtimes/testing/penpal/Cargo.toml
@@ -0,0 +1,151 @@
+[package]
+name = "penpal-runtime"
+version = "0.1.0"
+authors = ["Anonymous"]
+description = "A parachain for communication back and forth with XCM of assets and uniques."
+license = "Unlicense"
+homepage = "https://substrate.io"
+repository = "https://github.com/paritytech/cumulus/"
+edition = "2021"
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
+
+[build-dependencies]
+substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "master" }
+
+[dependencies]
+codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] }
+hex-literal = { version = "0.3.4", optional = true }
+log = { version = "0.4.16", default-features = false }
+scale-info = { version = "2.1.1", default-features = false, features = ["derive"] }
+serde = { version = "1.0.132", optional = true, features = ["derive"] }
+smallvec = "1.6.1"
+
+# Substrate
+frame-benchmarking = { git = "https://github.com/paritytech/substrate", default-features = false, optional = true, branch = "master" }
+frame-executive = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+frame-system-benchmarking = { git = "https://github.com/paritytech/substrate", default-features = false, optional = true, branch = "master" }
+frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+frame-try-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, optional = true, branch = "master" }
+pallet-aura = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+pallet-authorship = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+pallet-balances = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+pallet-session = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+pallet-sudo = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+pallet-timestamp = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+pallet-asset-tx-payment = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+pallet-assets = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+sp-api = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+sp-block-builder = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+sp-consensus-aura = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+sp-inherents = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+sp-io = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+sp-offchain = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+sp-session = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+sp-transaction-pool = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+sp-version = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" }
+
+# Polkadot
+polkadot-primitives = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
+pallet-xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
+polkadot-parachain = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
+polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
+xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
+xcm-builder = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
+xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "master" }
+
+# Cumulus
+cumulus-pallet-aura-ext = { path = "../../../../pallets/aura-ext", default-features = false }
+cumulus-pallet-dmp-queue = { path = "../../../../pallets/dmp-queue", default-features = false }
+cumulus-pallet-parachain-system = { path = "../../../../pallets/parachain-system", default-features = false }
+cumulus-pallet-session-benchmarking = {path = "../../../../pallets/session-benchmarking", default-features = false, version = "3.0.0"}
+cumulus-pallet-xcm = { path = "../../../../pallets/xcm", default-features = false }
+cumulus-pallet-xcmp-queue = { path = "../../../../pallets/xcmp-queue", default-features = false }
+cumulus-primitives-core = { path = "../../../../primitives/core", default-features = false }
+cumulus-primitives-timestamp = { path = "../../../../primitives/timestamp", default-features = false }
+cumulus-primitives-utility = { path = "../../../../primitives/utility", default-features = false }
+pallet-collator-selection = { path = "../../../../pallets/collator-selection", default-features = false }
+parachain-info = { path = "../../../../parachains/pallets/parachain-info", default-features = false }
+
+[features]
+default = [
+ "std",
+]
+std = [
+ "codec/std",
+ "log/std",
+ "scale-info/std",
+ "serde",
+ "cumulus-pallet-aura-ext/std",
+ "cumulus-pallet-dmp-queue/std",
+ "cumulus-pallet-parachain-system/std",
+ "cumulus-pallet-xcm/std",
+ "cumulus-pallet-xcmp-queue/std",
+ "cumulus-primitives-core/std",
+ "cumulus-primitives-timestamp/std",
+ "cumulus-primitives-utility/std",
+ "frame-executive/std",
+ "frame-support/std",
+ "frame-system-rpc-runtime-api/std",
+ "frame-system/std",
+ "pallet-aura/std",
+ "pallet-authorship/std",
+ "pallet-balances/std",
+ "pallet-collator-selection/std",
+ "pallet-session/std",
+ "pallet-sudo/std",
+ "pallet-timestamp/std",
+ "pallet-transaction-payment-rpc-runtime-api/std",
+ "pallet-transaction-payment/std",
+ "pallet-assets/std",
+ "pallet-asset-tx-payment/std",
+ "pallet-xcm/std",
+ "polkadot-primitives/std",
+ "parachain-info/std",
+ "polkadot-parachain/std",
+ "polkadot-runtime-common/std",
+ "sp-api/std",
+ "sp-block-builder/std",
+ "sp-consensus-aura/std",
+ "sp-core/std",
+ "sp-inherents/std",
+ "sp-io/std",
+ "sp-offchain/std",
+ "sp-runtime/std",
+ "sp-session/std",
+ "sp-std/std",
+ "sp-transaction-pool/std",
+ "sp-version/std",
+ "xcm-builder/std",
+ "xcm-executor/std",
+ "xcm/std",
+]
+
+runtime-benchmarks = [
+ "hex-literal",
+ "frame-benchmarking/runtime-benchmarks",
+ "frame-support/runtime-benchmarks",
+ "frame-system-benchmarking",
+ "frame-system/runtime-benchmarks",
+ "pallet-balances/runtime-benchmarks",
+ "pallet-collator-selection/runtime-benchmarks",
+ "pallet-timestamp/runtime-benchmarks",
+ "pallet-xcm/runtime-benchmarks",
+ "sp-runtime/runtime-benchmarks",
+ "xcm-builder/runtime-benchmarks",
+ "cumulus-pallet-session-benchmarking/runtime-benchmarks",
+ "cumulus-pallet-xcmp-queue/runtime-benchmarks",
+]
+
+try-runtime = [
+ "frame-executive/try-runtime",
+ "frame-try-runtime",
+]
diff --git a/parachains/runtimes/testing/penpal/build.rs b/parachains/runtimes/testing/penpal/build.rs
new file mode 100644
index 0000000000..fe1a2ea911
--- /dev/null
+++ b/parachains/runtimes/testing/penpal/build.rs
@@ -0,0 +1,25 @@
+// Copyright 2019-2021 Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Substrate 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.
+
+// Substrate 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 Cumulus. If not, see .
+
+use substrate_wasm_builder::WasmBuilder;
+
+fn main() {
+ WasmBuilder::new()
+ .with_current_project()
+ .export_heap_base()
+ .import_memory()
+ .build()
+}
diff --git a/parachains/runtimes/testing/penpal/src/lib.rs b/parachains/runtimes/testing/penpal/src/lib.rs
new file mode 100644
index 0000000000..bd37a22011
--- /dev/null
+++ b/parachains/runtimes/testing/penpal/src/lib.rs
@@ -0,0 +1,774 @@
+// Copyright 2019-2022 Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus 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.
+
+// Cumulus 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 Cumulus. If not, see .
+
+//! The Penpal runtime is designed as a test runtime that can be created using an arbitrary parachain id.
+//! (and as such multiple parachains can be on the same relay node - though make sure you have enough relay
+//! nodes running to support this or you will get the not scheduled on a core error message.)
+//!
+//! The penpal runtime's primary use is as a partner when testing statemine/t with reserve asset transfers.
+#![cfg_attr(not(feature = "std"), no_std)]
+// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.
+#![recursion_limit = "256"]
+
+// Make the WASM binary available.
+#[cfg(feature = "std")]
+include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
+
+mod weights;
+pub mod xcm_config;
+
+use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases;
+use frame_support::{
+ construct_runtime, parameter_types,
+ traits::Everything,
+ weights::{
+ constants::WEIGHT_PER_SECOND, ConstantMultiplier, DispatchClass, Weight,
+ WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial,
+ },
+ PalletId,
+};
+use frame_system::{
+ limits::{BlockLength, BlockWeights},
+ EnsureRoot,
+};
+use smallvec::smallvec;
+use sp_api::impl_runtime_apis;
+pub use sp_consensus_aura::sr25519::AuthorityId as AuraId;
+use sp_core::{crypto::KeyTypeId, OpaqueMetadata};
+use sp_runtime::{
+ create_runtime_str, generic, impl_opaque_keys,
+ traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify},
+ transaction_validity::{TransactionSource, TransactionValidity},
+ ApplyExtrinsicResult, MultiSignature,
+};
+pub use sp_runtime::{traits::ConvertInto, MultiAddress, Perbill, Permill};
+use sp_std::prelude::*;
+#[cfg(feature = "std")]
+use sp_version::NativeVersion;
+use sp_version::RuntimeVersion;
+use xcm_config::{AssetsToBlockAuthor, XcmConfig, XcmOriginToTransactDispatchOrigin};
+
+#[cfg(any(feature = "std", test))]
+pub use sp_runtime::BuildStorage;
+
+// Polkadot imports
+use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate};
+
+use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight};
+
+// XCM Imports
+use xcm::latest::prelude::BodyId;
+use xcm_executor::XcmExecutor;
+
+/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
+pub type Signature = MultiSignature;
+
+/// Some way of identifying an account on the chain. We intentionally make it equivalent
+/// to the public key of our transaction signing scheme.
+pub type AccountId = <::Signer as IdentifyAccount>::AccountId;
+
+/// Balance of an account.
+pub type Balance = u128;
+
+/// Index of a transaction in the chain.
+pub type Index = u32;
+
+/// A hash of some data used by the chain.
+pub type Hash = sp_core::H256;
+
+/// An index to a block.
+pub type BlockNumber = u32;
+
+/// The address format for describing accounts.
+pub type Address = MultiAddress;
+
+/// Block header type as expected by this runtime.
+pub type Header = generic::Header;
+
+/// Block type as expected by this runtime.
+pub type Block = generic::Block;
+
+/// A Block signed with a Justification
+pub type SignedBlock = generic::SignedBlock;
+
+/// BlockId type as expected by this runtime.
+pub type BlockId = generic::BlockId;
+
+// Id used for identifying assets.
+pub type AssetId = u32;
+
+/// The SignedExtension to the basic transaction logic.
+pub type SignedExtra = (
+ frame_system::CheckNonZeroSender,
+ frame_system::CheckSpecVersion,
+ frame_system::CheckTxVersion,
+ frame_system::CheckGenesis,
+ frame_system::CheckEra,
+ frame_system::CheckNonce,
+ frame_system::CheckWeight,
+ pallet_asset_tx_payment::ChargeAssetTxPayment,
+);
+
+/// Unchecked extrinsic type as expected by this runtime.
+pub type UncheckedExtrinsic = generic::UncheckedExtrinsic;
+
+/// Extrinsic type that has already been checked.
+pub type CheckedExtrinsic = generic::CheckedExtrinsic;
+
+/// Executive: handles dispatch to the various modules.
+pub type Executive = frame_executive::Executive<
+ Runtime,
+ Block,
+ frame_system::ChainContext,
+ Runtime,
+ AllPalletsWithSystem,
+>;
+
+/// Handles converting a weight scalar to a fee value, based on the scale and granularity of the
+/// node's balance type.
+///
+/// This should typically create a mapping between the following ranges:
+/// - `[0, MAXIMUM_BLOCK_WEIGHT]`
+/// - `[Balance::min, Balance::max]`
+///
+/// Yet, it can be used for any other sort of change to weight-fee. Some examples being:
+/// - Setting it to `0` will essentially disable the weight fee.
+/// - Setting it to `1` will cause the literal `#[weight = x]` values to be charged.
+pub struct WeightToFee;
+impl WeightToFeePolynomial for WeightToFee {
+ type Balance = Balance;
+ fn polynomial() -> WeightToFeeCoefficients {
+ // in Rococo, extrinsic base weight (smallest non-zero weight) is mapped to 1 MILLIUNIT:
+ // in our template, we map to 1/10 of that, or 1/10 MILLIUNIT
+ let p = MILLIUNIT / 10;
+ let q = 100 * Balance::from(ExtrinsicBaseWeight::get());
+ smallvec![WeightToFeeCoefficient {
+ degree: 1,
+ negative: false,
+ coeff_frac: Perbill::from_rational(p % q, q),
+ coeff_integer: p / q,
+ }]
+ }
+}
+
+/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know
+/// the specifics of the runtime. They can then be made to be agnostic over specific formats
+/// of data like extrinsics, allowing for them to continue syncing the network through upgrades
+/// to even the core data structures.
+pub mod opaque {
+ use super::*;
+ use sp_runtime::{generic, traits::BlakeTwo256};
+
+ pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;
+ /// Opaque block header type.
+ pub type Header = generic::Header;
+ /// Opaque block type.
+ pub type Block = generic::Block;
+ /// Opaque block identifier type.
+ pub type BlockId = generic::BlockId;
+}
+
+impl_opaque_keys! {
+ pub struct SessionKeys {
+ pub aura: Aura,
+ }
+}
+
+#[sp_version::runtime_version]
+pub const VERSION: RuntimeVersion = RuntimeVersion {
+ spec_name: create_runtime_str!("penpal-parachain"),
+ impl_name: create_runtime_str!("penpal-parachain"),
+ authoring_version: 1,
+ spec_version: 1,
+ impl_version: 0,
+ apis: RUNTIME_API_VERSIONS,
+ transaction_version: 1,
+ state_version: 1,
+};
+
+/// This determines the average expected block time that we are targeting.
+/// Blocks will be produced at a minimum duration defined by `SLOT_DURATION`.
+/// `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked
+/// up by `pallet_aura` to implement `fn slot_duration()`.
+///
+/// Change this to adjust the block time.
+pub const MILLISECS_PER_BLOCK: u64 = 12000;
+
+// NOTE: Currently it is not possible to change the slot duration after the chain has started.
+// Attempting to do so will brick block production.
+pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;
+
+// Time is measured by number of blocks.
+pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);
+pub const HOURS: BlockNumber = MINUTES * 60;
+pub const DAYS: BlockNumber = HOURS * 24;
+
+// Unit = the base number of indivisible units for balances
+pub const UNIT: Balance = 1_000_000_000_000;
+pub const MILLIUNIT: Balance = 1_000_000_000;
+pub const MICROUNIT: Balance = 1_000_000;
+
+/// The existential deposit. Set to 1/10 of the Connected Relay Chain.
+pub const EXISTENTIAL_DEPOSIT: Balance = MILLIUNIT;
+
+/// We assume that ~5% of the block weight is consumed by `on_initialize` handlers. This is
+/// used to limit the maximal weight of a single extrinsic.
+const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5);
+
+/// We allow `Normal` extrinsics to fill up the block up to 75%, the rest can be used by
+/// `Operational` extrinsics.
+const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
+
+/// We allow for 0.5 of a second of compute with a 12 second average block time.
+const MAXIMUM_BLOCK_WEIGHT: Weight = WEIGHT_PER_SECOND / 2;
+
+/// The version information used to identify this runtime when compiled natively.
+#[cfg(feature = "std")]
+pub fn native_version() -> NativeVersion {
+ NativeVersion { runtime_version: VERSION, can_author_with: Default::default() }
+}
+
+parameter_types! {
+ pub const Version: RuntimeVersion = VERSION;
+
+ // This part is copied from Substrate's `bin/node/runtime/src/lib.rs`.
+ // The `RuntimeBlockLength` and `RuntimeBlockWeights` exist here because the
+ // `DeletionWeightLimit` and `DeletionQueueDepth` depend on those to parameterize
+ // the lazy contract deletion.
+ pub RuntimeBlockLength: BlockLength =
+ BlockLength::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);
+ pub RuntimeBlockWeights: BlockWeights = BlockWeights::builder()
+ .base_block(BlockExecutionWeight::get())
+ .for_class(DispatchClass::all(), |weights| {
+ weights.base_extrinsic = ExtrinsicBaseWeight::get();
+ })
+ .for_class(DispatchClass::Normal, |weights| {
+ weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT);
+ })
+ .for_class(DispatchClass::Operational, |weights| {
+ weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT);
+ // Operational transactions have some extra reserved space, so that they
+ // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`.
+ weights.reserved = Some(
+ MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT
+ );
+ })
+ .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO)
+ .build_or_panic();
+ pub const SS58Prefix: u16 = 42;
+}
+
+// Configure FRAME pallets to include in runtime.
+
+impl frame_system::Config for Runtime {
+ /// The identifier used to distinguish between accounts.
+ type AccountId = AccountId;
+ /// The aggregated dispatch type that is available for extrinsics.
+ type Call = Call;
+ /// The lookup mechanism to get account ID from whatever is passed in dispatchers.
+ type Lookup = AccountIdLookup;
+ /// The index type for storing how many extrinsics an account has signed.
+ type Index = Index;
+ /// The index type for blocks.
+ type BlockNumber = BlockNumber;
+ /// The type for hashing blocks and tries.
+ type Hash = Hash;
+ /// The hashing algorithm used.
+ type Hashing = BlakeTwo256;
+ /// The header type.
+ type Header = generic::Header;
+ /// The ubiquitous event type.
+ type Event = Event;
+ /// The ubiquitous origin type.
+ type Origin = Origin;
+ /// Maximum number of block number to block hash mappings to keep (oldest pruned first).
+ type BlockHashCount = BlockHashCount;
+ /// Runtime version.
+ type Version = Version;
+ /// Converts a module to an index of this module in the runtime.
+ type PalletInfo = PalletInfo;
+ /// The data to be stored in an account.
+ type AccountData = pallet_balances::AccountData;
+ /// What to do if a new account is created.
+ type OnNewAccount = ();
+ /// What to do if an account is fully reaped from the system.
+ type OnKilledAccount = ();
+ /// The weight of database operations that the runtime can invoke.
+ type DbWeight = RocksDbWeight;
+ /// The basic call filter to use in dispatchable.
+ type BaseCallFilter = Everything;
+ /// Weight information for the extrinsics of this pallet.
+ type SystemWeightInfo = ();
+ /// Block & extrinsics weights: base values and limits.
+ type BlockWeights = RuntimeBlockWeights;
+ /// The maximum length of a block (in bytes).
+ type BlockLength = RuntimeBlockLength;
+ /// This is used as an identifier of the chain. 42 is the generic substrate prefix.
+ type SS58Prefix = SS58Prefix;
+ /// The action to take on a Runtime Upgrade
+ type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode;
+ type MaxConsumers = frame_support::traits::ConstU32<16>;
+}
+
+parameter_types! {
+ pub const MinimumPeriod: u64 = SLOT_DURATION / 2;
+}
+
+impl pallet_timestamp::Config for Runtime {
+ /// A timestamp: milliseconds since the unix epoch.
+ type Moment = u64;
+ type OnTimestampSet = ();
+ type MinimumPeriod = MinimumPeriod;
+ type WeightInfo = ();
+}
+
+parameter_types! {
+ pub const UncleGenerations: u32 = 0;
+}
+
+impl pallet_authorship::Config for Runtime {
+ type FindAuthor = pallet_session::FindAccountFromAuthorIndex;
+ type UncleGenerations = UncleGenerations;
+ type FilterUncle = ();
+ type EventHandler = (CollatorSelection,);
+}
+
+parameter_types! {
+ pub const ExistentialDeposit: Balance = EXISTENTIAL_DEPOSIT;
+ pub const MaxLocks: u32 = 50;
+ pub const MaxReserves: u32 = 50;
+}
+
+impl pallet_balances::Config for Runtime {
+ type MaxLocks = MaxLocks;
+ /// The type for recording an account's balance.
+ type Balance = Balance;
+ /// The ubiquitous event type.
+ type Event = Event;
+ type DustRemoval = ();
+ type ExistentialDeposit = ExistentialDeposit;
+ type AccountStore = System;
+ type WeightInfo = pallet_balances::weights::SubstrateWeight;
+ type MaxReserves = MaxReserves;
+ type ReserveIdentifier = [u8; 8];
+}
+
+parameter_types! {
+ /// Relay Chain `TransactionByteFee` / 10
+ pub const TransactionByteFee: Balance = 10 * MICROUNIT;
+ pub const OperationalFeeMultiplier: u8 = 5;
+}
+
+impl pallet_transaction_payment::Config for Runtime {
+ type Event = Event;
+ type OnChargeTransaction = pallet_transaction_payment::CurrencyAdapter;
+ type WeightToFee = WeightToFee;
+ type LengthToFee = ConstantMultiplier;
+ type FeeMultiplierUpdate = SlowAdjustingFeeUpdate;
+ type OperationalFeeMultiplier = OperationalFeeMultiplier;
+}
+
+parameter_types! {
+ pub const AssetDeposit: Balance = 0;
+ pub const AssetAccountDeposit: Balance = 0;
+ pub const ApprovalDeposit: Balance = 0;
+ pub const AssetsStringLimit: u32 = 50;
+ pub const MetadataDepositBase: Balance = 0;
+ pub const MetadataDepositPerByte: Balance = 0;
+}
+
+// /// We allow root and the Relay Chain council to execute privileged asset operations.
+// pub type AssetsForceOrigin =
+// EnsureOneOf, EnsureXcm>>;
+
+impl pallet_assets::Config for Runtime {
+ type Event = Event;
+ type Balance = Balance;
+ type AssetId = AssetId;
+ type Currency = Balances;
+ type ForceOrigin = EnsureRoot;
+ type AssetDeposit = AssetDeposit;
+ type MetadataDepositBase = MetadataDepositBase;
+ type MetadataDepositPerByte = MetadataDepositPerByte;
+ type ApprovalDeposit = ApprovalDeposit;
+ type StringLimit = AssetsStringLimit;
+ type Freezer = ();
+ type Extra = ();
+ type WeightInfo = pallet_assets::weights::SubstrateWeight;
+ type AssetAccountDeposit = AssetAccountDeposit;
+}
+
+parameter_types! {
+ pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 4;
+ pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 4;
+}
+
+impl cumulus_pallet_parachain_system::Config for Runtime {
+ type Event = Event;
+ type OnSystemEvent = ();
+ type SelfParaId = parachain_info::Pallet;
+ type DmpMessageHandler = DmpQueue;
+ type ReservedDmpWeight = ReservedDmpWeight;
+ type OutboundXcmpMessageSource = XcmpQueue;
+ type XcmpMessageHandler = XcmpQueue;
+ type ReservedXcmpWeight = ReservedXcmpWeight;
+ type CheckAssociatedRelayNumber = RelayNumberStrictlyIncreases;
+}
+
+impl parachain_info::Config for Runtime {}
+
+impl cumulus_pallet_aura_ext::Config for Runtime {}
+
+impl cumulus_pallet_xcmp_queue::Config for Runtime {
+ type Event = Event;
+ type XcmExecutor = XcmExecutor;
+ type ChannelInfo = ParachainSystem;
+ type VersionWrapper = PolkadotXcm;
+ type ExecuteOverweightOrigin = EnsureRoot;
+ type ControllerOrigin = EnsureRoot;
+ type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin;
+ type WeightInfo = ();
+}
+
+impl cumulus_pallet_dmp_queue::Config for Runtime {
+ type Event = Event;
+ type XcmExecutor = XcmExecutor;
+ type ExecuteOverweightOrigin = EnsureRoot;
+}
+
+parameter_types! {
+ pub const Period: u32 = 6 * HOURS;
+ pub const Offset: u32 = 0;
+ pub const MaxAuthorities: u32 = 100_000;
+}
+
+impl pallet_session::Config for Runtime {
+ type Event = Event;
+ type ValidatorId = ::AccountId;
+ // we don't have stash and controller, thus we don't need the convert as well.
+ type ValidatorIdOf = pallet_collator_selection::IdentityCollator;
+ type ShouldEndSession = pallet_session::PeriodicSessions;
+ type NextSessionRotation = pallet_session::PeriodicSessions;
+ type SessionManager = CollatorSelection;
+ // Essentially just Aura, but lets be pedantic.
+ type SessionHandler = ::KeyTypeIdProviders;
+ type Keys = SessionKeys;
+ type WeightInfo = ();
+}
+
+impl pallet_aura::Config for Runtime {
+ type AuthorityId = AuraId;
+ type DisabledValidators = ();
+ type MaxAuthorities = MaxAuthorities;
+}
+
+parameter_types! {
+ pub const PotId: PalletId = PalletId(*b"PotStake");
+ pub const MaxCandidates: u32 = 1000;
+ pub const MinCandidates: u32 = 5;
+ pub const SessionLength: BlockNumber = 6 * HOURS;
+ pub const MaxInvulnerables: u32 = 100;
+ pub const ExecutiveBody: BodyId = BodyId::Executive;
+}
+
+// We allow root only to execute privileged collator selection operations.
+pub type CollatorSelectionUpdateOrigin = EnsureRoot;
+
+impl pallet_collator_selection::Config for Runtime {
+ type Event = Event;
+ type Currency = Balances;
+ type UpdateOrigin = CollatorSelectionUpdateOrigin;
+ type PotId = PotId;
+ type MaxCandidates = MaxCandidates;
+ type MinCandidates = MinCandidates;
+ type MaxInvulnerables = MaxInvulnerables;
+ // should be a multiple of session or things will get inconsistent
+ type KickThreshold = Period;
+ type ValidatorId = ::AccountId;
+ type ValidatorIdOf = pallet_collator_selection::IdentityCollator;
+ type ValidatorRegistration = Session;
+ type WeightInfo = ();
+}
+
+impl pallet_asset_tx_payment::Config for Runtime {
+ type Fungibles = Assets;
+ type OnChargeAssetTransaction = pallet_asset_tx_payment::FungiblesAdapter<
+ pallet_assets::BalanceToAssetBalance,
+ AssetsToBlockAuthor,
+ >;
+}
+
+impl pallet_sudo::Config for Runtime {
+ type Event = Event;
+ type Call = Call;
+}
+
+// Create the runtime by composing the FRAME pallets that were previously configured.
+construct_runtime!(
+ pub enum Runtime where
+ Block = Block,
+ NodeBlock = opaque::Block,
+ UncheckedExtrinsic = UncheckedExtrinsic,
+ {
+ // System support stuff.
+ System: frame_system::{Pallet, Call, Config, Storage, Event} = 0,
+ ParachainSystem: cumulus_pallet_parachain_system::{
+ Pallet, Call, Config, Storage, Inherent, Event, ValidateUnsigned,
+ } = 1,
+ Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent} = 2,
+ ParachainInfo: parachain_info::{Pallet, Storage, Config} = 3,
+
+ // Monetary stuff.
+ Balances: pallet_balances::{Pallet, Call, Storage, Config, Event} = 10,
+ TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event} = 11,
+ AssetTxPayment: pallet_asset_tx_payment::{Pallet} = 12,
+
+ // Collator support. The order of these 4 are important and shall not change.
+ Authorship: pallet_authorship::{Pallet, Call, Storage} = 20,
+ CollatorSelection: pallet_collator_selection::{Pallet, Call, Storage, Event, Config} = 21,
+ Session: pallet_session::{Pallet, Call, Storage, Event, Config} = 22,
+ Aura: pallet_aura::{Pallet, Storage, Config} = 23,
+ AuraExt: cumulus_pallet_aura_ext::{Pallet, Storage, Config} = 24,
+
+ // XCM helpers.
+ XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Call, Storage, Event} = 30,
+ PolkadotXcm: pallet_xcm::{Pallet, Call, Event, Origin, Config} = 31,
+ CumulusXcm: cumulus_pallet_xcm::{Pallet, Event, Origin} = 32,
+ DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event} = 33,
+
+ // The main stage.
+ Assets: pallet_assets::{Pallet, Call, Storage, Event} = 50,
+
+ Sudo: pallet_sudo::{Pallet, Call, Storage, Event, Config} = 255,
+ }
+);
+
+#[cfg(feature = "runtime-benchmarks")]
+#[macro_use]
+extern crate frame_benchmarking;
+
+#[cfg(feature = "runtime-benchmarks")]
+mod benches {
+ define_benchmarks!(
+ [frame_system, SystemBench::]
+ [pallet_balances, Balances]
+ [pallet_session, SessionBench::]
+ [pallet_timestamp, Timestamp]
+ [pallet_collator_selection, CollatorSelection]
+ [cumulus_pallet_xcmp_queue, XcmpQueue]
+ );
+}
+
+impl_runtime_apis! {
+ impl sp_consensus_aura::AuraApi for Runtime {
+ fn slot_duration() -> sp_consensus_aura::SlotDuration {
+ sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration())
+ }
+
+ fn authorities() -> Vec {
+ Aura::authorities().into_inner()
+ }
+ }
+
+ impl sp_api::Core for Runtime {
+ fn version() -> RuntimeVersion {
+ VERSION
+ }
+
+ fn execute_block(block: Block) {
+ Executive::execute_block(block)
+ }
+
+ fn initialize_block(header: &::Header) {
+ Executive::initialize_block(header)
+ }
+ }
+
+ impl sp_api::Metadata for Runtime {
+ fn metadata() -> OpaqueMetadata {
+ OpaqueMetadata::new(Runtime::metadata().into())
+ }
+ }
+
+ impl sp_block_builder::BlockBuilder for Runtime {
+ fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyExtrinsicResult {
+ Executive::apply_extrinsic(extrinsic)
+ }
+
+ fn finalize_block() -> ::Header {
+ Executive::finalize_block()
+ }
+
+ fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<::Extrinsic> {
+ data.create_extrinsics()
+ }
+
+ fn check_inherents(
+ block: Block,
+ data: sp_inherents::InherentData,
+ ) -> sp_inherents::CheckInherentsResult {
+ data.check_extrinsics(&block)
+ }
+ }
+
+ impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime {
+ fn validate_transaction(
+ source: TransactionSource,
+ tx: ::Extrinsic,
+ block_hash: ::Hash,
+ ) -> TransactionValidity {
+ Executive::validate_transaction(source, tx, block_hash)
+ }
+ }
+
+ impl sp_offchain::OffchainWorkerApi for Runtime {
+ fn offchain_worker(header: &::Header) {
+ Executive::offchain_worker(header)
+ }
+ }
+
+ impl sp_session::SessionKeys for Runtime {
+ fn generate_session_keys(seed: Option>) -> Vec {
+ SessionKeys::generate(seed)
+ }
+
+ fn decode_session_keys(
+ encoded: Vec,
+ ) -> Option, KeyTypeId)>> {
+ SessionKeys::decode_into_raw_public_keys(&encoded)
+ }
+ }
+
+ impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime {
+ fn account_nonce(account: AccountId) -> Index {
+ System::account_nonce(account)
+ }
+ }
+
+ impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime {
+ fn query_info(
+ uxt: ::Extrinsic,
+ len: u32,
+ ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo {
+ TransactionPayment::query_info(uxt, len)
+ }
+ fn query_fee_details(
+ uxt: ::Extrinsic,
+ len: u32,
+ ) -> pallet_transaction_payment::FeeDetails {
+ TransactionPayment::query_fee_details(uxt, len)
+ }
+ }
+
+ impl cumulus_primitives_core::CollectCollationInfo for Runtime {
+ fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo {
+ ParachainSystem::collect_collation_info(header)
+ }
+ }
+
+ #[cfg(feature = "try-runtime")]
+ impl frame_try_runtime::TryRuntime for Runtime {
+ fn on_runtime_upgrade() -> (Weight, Weight) {
+ log::info!("try-runtime::on_runtime_upgrade parachain-template.");
+ let weight = Executive::try_runtime_upgrade().unwrap();
+ (weight, RuntimeBlockWeights::get().max_block)
+ }
+
+ fn execute_block_no_check(block: Block) -> Weight {
+ Executive::execute_block_no_check(block)
+ }
+ }
+
+ #[cfg(feature = "runtime-benchmarks")]
+ impl frame_benchmarking::Benchmark for Runtime {
+ fn benchmark_metadata(extra: bool) -> (
+ Vec,
+ Vec,
+ ) {
+ use frame_benchmarking::{Benchmarking, BenchmarkList};
+ use frame_support::traits::StorageInfoTrait;
+ use frame_system_benchmarking::Pallet as SystemBench;
+ use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
+
+ let mut list = Vec::::new();
+ list_benchmarks!(list, extra);
+
+ let storage_info = AllPalletsWithSystem::storage_info();
+ return (list, storage_info)
+ }
+
+ fn dispatch_benchmark(
+ config: frame_benchmarking::BenchmarkConfig
+ ) -> Result, sp_runtime::RuntimeString> {
+ use frame_benchmarking::{Benchmarking, BenchmarkBatch, TrackedStorageKey};
+
+ use frame_system_benchmarking::Pallet as SystemBench;
+ impl frame_system_benchmarking::Config for Runtime {}
+
+ use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
+ impl cumulus_pallet_session_benchmarking::Config for Runtime {}
+
+ let whitelist: Vec = vec![
+ // Block Number
+ hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(),
+ // Total Issuance
+ hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec().into(),
+ // Execution Phase
+ hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(),
+ // Event Count
+ hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(),
+ // System Events
+ hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(),
+ ];
+
+ let mut batches = Vec::::new();
+ let params = (&config, &whitelist);
+ add_benchmarks!(params, batches);
+
+ if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) }
+ Ok(batches)
+ }
+ }
+}
+
+struct CheckInherents;
+
+impl cumulus_pallet_parachain_system::CheckInherents for CheckInherents {
+ fn check_inherents(
+ block: &Block,
+ relay_state_proof: &cumulus_pallet_parachain_system::RelayChainStateProof,
+ ) -> sp_inherents::CheckInherentsResult {
+ let relay_chain_slot = relay_state_proof
+ .read_slot()
+ .expect("Could not read the relay chain slot from the proof");
+
+ let inherent_data =
+ cumulus_primitives_timestamp::InherentDataProvider::from_relay_chain_slot_and_duration(
+ relay_chain_slot,
+ sp_std::time::Duration::from_secs(6),
+ )
+ .create_inherent_data()
+ .expect("Could not create the timestamp inherent data");
+
+ inherent_data.check_extrinsics(block)
+ }
+}
+
+cumulus_pallet_parachain_system::register_validate_block! {
+ Runtime = Runtime,
+ BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::,
+ CheckInherents = CheckInherents,
+}
diff --git a/parachains/runtimes/testing/penpal/src/weights/block_weights.rs b/parachains/runtimes/testing/penpal/src/weights/block_weights.rs
new file mode 100644
index 0000000000..4db90f0c02
--- /dev/null
+++ b/parachains/runtimes/testing/penpal/src/weights/block_weights.rs
@@ -0,0 +1,46 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2022 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.
+
+pub mod constants {
+ use frame_support::{
+ parameter_types,
+ weights::{constants, Weight},
+ };
+
+ parameter_types! {
+ /// Importing a block with 0 Extrinsics.
+ pub const BlockExecutionWeight: Weight = 5_000_000 * constants::WEIGHT_PER_NANOS;
+ }
+
+ #[cfg(test)]
+ mod test_weights {
+ use frame_support::weights::constants;
+
+ /// Checks that the weight exists and is sane.
+ // NOTE: If this test fails but you are sure that the generated values are fine,
+ // you can delete it.
+ #[test]
+ fn sane() {
+ let w = super::constants::BlockExecutionWeight::get();
+
+ // At least 100 µs.
+ assert!(w >= 100 * constants::WEIGHT_PER_MICROS, "Weight should be at least 100 µs.");
+ // At most 50 ms.
+ assert!(w <= 50 * constants::WEIGHT_PER_MILLIS, "Weight should be at most 50 ms.");
+ }
+ }
+}
diff --git a/parachains/runtimes/testing/penpal/src/weights/extrinsic_weights.rs b/parachains/runtimes/testing/penpal/src/weights/extrinsic_weights.rs
new file mode 100644
index 0000000000..158ba99c6a
--- /dev/null
+++ b/parachains/runtimes/testing/penpal/src/weights/extrinsic_weights.rs
@@ -0,0 +1,46 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2022 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.
+
+pub mod constants {
+ use frame_support::{
+ parameter_types,
+ weights::{constants, Weight},
+ };
+
+ parameter_types! {
+ /// Executing a NO-OP `System::remarks` Extrinsic.
+ pub const ExtrinsicBaseWeight: Weight = 125_000 * constants::WEIGHT_PER_NANOS;
+ }
+
+ #[cfg(test)]
+ mod test_weights {
+ use frame_support::weights::constants;
+
+ /// Checks that the weight exists and is sane.
+ // NOTE: If this test fails but you are sure that the generated values are fine,
+ // you can delete it.
+ #[test]
+ fn sane() {
+ let w = super::constants::ExtrinsicBaseWeight::get();
+
+ // At least 10 µs.
+ assert!(w >= 10 * constants::WEIGHT_PER_MICROS, "Weight should be at least 10 µs.");
+ // At most 1 ms.
+ assert!(w <= constants::WEIGHT_PER_MILLIS, "Weight should be at most 1 ms.");
+ }
+ }
+}
diff --git a/parachains/runtimes/testing/penpal/src/weights/mod.rs b/parachains/runtimes/testing/penpal/src/weights/mod.rs
new file mode 100644
index 0000000000..ed0b4dbcd4
--- /dev/null
+++ b/parachains/runtimes/testing/penpal/src/weights/mod.rs
@@ -0,0 +1,28 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2022 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.
+
+//! Expose the auto generated weight files.
+
+pub mod block_weights;
+pub mod extrinsic_weights;
+pub mod paritydb_weights;
+pub mod rocksdb_weights;
+
+pub use block_weights::constants::BlockExecutionWeight;
+pub use extrinsic_weights::constants::ExtrinsicBaseWeight;
+pub use paritydb_weights::constants::ParityDbWeight;
+pub use rocksdb_weights::constants::RocksDbWeight;
diff --git a/parachains/runtimes/testing/penpal/src/weights/paritydb_weights.rs b/parachains/runtimes/testing/penpal/src/weights/paritydb_weights.rs
new file mode 100644
index 0000000000..843823c1bf
--- /dev/null
+++ b/parachains/runtimes/testing/penpal/src/weights/paritydb_weights.rs
@@ -0,0 +1,63 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2022 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.
+
+pub mod constants {
+ use frame_support::{
+ parameter_types,
+ weights::{constants, RuntimeDbWeight},
+ };
+
+ parameter_types! {
+ /// `ParityDB` can be enabled with a feature flag, but is still experimental. These weights
+ /// are available for brave runtime engineers who may want to try this out as default.
+ pub const ParityDbWeight: RuntimeDbWeight = RuntimeDbWeight {
+ read: 8_000 * constants::WEIGHT_PER_NANOS,
+ write: 50_000 * constants::WEIGHT_PER_NANOS,
+ };
+ }
+
+ #[cfg(test)]
+ mod test_db_weights {
+ use super::constants::ParityDbWeight as W;
+ use frame_support::weights::constants;
+
+ /// Checks that all weights exist and have sane values.
+ // NOTE: If this test fails but you are sure that the generated values are fine,
+ // you can delete it.
+ #[test]
+ fn sane() {
+ // At least 1 µs.
+ assert!(
+ W::get().reads(1) >= constants::WEIGHT_PER_MICROS,
+ "Read weight should be at least 1 µs."
+ );
+ assert!(
+ W::get().writes(1) >= constants::WEIGHT_PER_MICROS,
+ "Write weight should be at least 1 µs."
+ );
+ // At most 1 ms.
+ assert!(
+ W::get().reads(1) <= constants::WEIGHT_PER_MILLIS,
+ "Read weight should be at most 1 ms."
+ );
+ assert!(
+ W::get().writes(1) <= constants::WEIGHT_PER_MILLIS,
+ "Write weight should be at most 1 ms."
+ );
+ }
+ }
+}
diff --git a/parachains/runtimes/testing/penpal/src/weights/rocksdb_weights.rs b/parachains/runtimes/testing/penpal/src/weights/rocksdb_weights.rs
new file mode 100644
index 0000000000..05e06b0eab
--- /dev/null
+++ b/parachains/runtimes/testing/penpal/src/weights/rocksdb_weights.rs
@@ -0,0 +1,63 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2022 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.
+
+pub mod constants {
+ use frame_support::{
+ parameter_types,
+ weights::{constants, RuntimeDbWeight},
+ };
+
+ parameter_types! {
+ /// By default, Substrate uses `RocksDB`, so this will be the weight used throughout
+ /// the runtime.
+ pub const RocksDbWeight: RuntimeDbWeight = RuntimeDbWeight {
+ read: 25_000 * constants::WEIGHT_PER_NANOS,
+ write: 100_000 * constants::WEIGHT_PER_NANOS,
+ };
+ }
+
+ #[cfg(test)]
+ mod test_db_weights {
+ use super::constants::RocksDbWeight as W;
+ use frame_support::weights::constants;
+
+ /// Checks that all weights exist and have sane values.
+ // NOTE: If this test fails but you are sure that the generated values are fine,
+ // you can delete it.
+ #[test]
+ fn sane() {
+ // At least 1 µs.
+ assert!(
+ W::get().reads(1) >= constants::WEIGHT_PER_MICROS,
+ "Read weight should be at least 1 µs."
+ );
+ assert!(
+ W::get().writes(1) >= constants::WEIGHT_PER_MICROS,
+ "Write weight should be at least 1 µs."
+ );
+ // At most 1 ms.
+ assert!(
+ W::get().reads(1) <= constants::WEIGHT_PER_MILLIS,
+ "Read weight should be at most 1 ms."
+ );
+ assert!(
+ W::get().writes(1) <= constants::WEIGHT_PER_MILLIS,
+ "Write weight should be at most 1 ms."
+ );
+ }
+ }
+}
diff --git a/parachains/runtimes/testing/penpal/src/xcm_config.rs b/parachains/runtimes/testing/penpal/src/xcm_config.rs
new file mode 100644
index 0000000000..3fb2192cd7
--- /dev/null
+++ b/parachains/runtimes/testing/penpal/src/xcm_config.rs
@@ -0,0 +1,378 @@
+// Copyright 2019-2022 Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus 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.
+
+// Cumulus 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 Cumulus. If not, see .
+
+//! Holds the XCM specific configuration that would otherwise be in lib.rs
+//!
+//! This configuration dictates how the Penpal chain will communicate with other chains.
+//!
+//! One of the main uses of the penpal chain will be to be a benefactor of reserve asset transfers
+//! with statemine as the reserve. At present no derivative tokens are minted on receipt of a
+//! ReserveAssetTransferDeposited message but that will but the intension will be to support this soon.
+use super::{
+ AccountId, AssetId as AssetIdPalletAssets, Assets, Balance, Balances, Call, Event, Origin,
+ ParachainInfo, ParachainSystem, PolkadotXcm, Runtime, WeightToFee, XcmpQueue,
+};
+use core::marker::PhantomData;
+use frame_support::{
+ log, match_types, parameter_types,
+ traits::{
+ fungibles::{self, Balanced, CreditOf},
+ Contains, Everything, Get, Nothing,
+ },
+ weights::Weight,
+};
+use pallet_asset_tx_payment::HandleCredit;
+use pallet_xcm::XcmPassthrough;
+use polkadot_parachain::primitives::Sibling;
+use polkadot_runtime_common::impls::ToAuthor;
+use sp_runtime::traits::Zero;
+use xcm::latest::prelude::*;
+use xcm_builder::{
+ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
+ AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex,
+ ConvertedConcreteAssetId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds,
+ FungiblesAdapter, IsConcrete, LocationInverter, NativeAsset, ParentIsPreset,
+ RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia,
+ SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,
+ UsingComponents,
+};
+use xcm_executor::{
+ traits::{FilterAssetLocation, JustTry, ShouldExecute},
+ XcmExecutor,
+};
+
+parameter_types! {
+ pub const RelayLocation: MultiLocation = MultiLocation::parent();
+ pub const RelayNetwork: NetworkId = NetworkId::Any;
+ pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into();
+ pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into();
+}
+
+/// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used
+/// when determining ownership of accounts for asset transacting and when attempting to use XCM
+/// `Transact` in order to determine the dispatch Origin.
+pub type LocationToAccountId = (
+ // The parent (Relay-chain) origin converts to the parent `AccountId`.
+ ParentIsPreset,
+ // Sibling parachain origins convert to AccountId via the `ParaId::into`.
+ SiblingParachainConvertsVia,
+ // Straight up local `AccountId32` origins just alias directly to `AccountId`.
+ AccountId32Aliases,
+);
+
+/// Means for transacting assets on this chain.
+pub type CurrencyTransactor = CurrencyAdapter<
+ // Use this currency:
+ Balances,
+ // Use this currency when it is a fungible asset matching the given location or name:
+ IsConcrete,
+ // Do a simple punn to convert an AccountId32 MultiLocation into a native chain account ID:
+ LocationToAccountId,
+ // Our chain's account ID type (we can't get away without mentioning it explicitly):
+ AccountId,
+ // We don't track any teleports.
+ (),
+>;
+
+/// Means for transacting assets besides the native currency on this chain.
+pub type FungiblesTransactor = FungiblesAdapter<
+ // Use this fungibles implementation:
+ Assets,
+ // Use this currency when it is a fungible asset matching the given location or name:
+ ConvertedConcreteAssetId<
+ AssetIdPalletAssets,
+ Balance,
+ AsPrefixedGeneralIndex,
+ JustTry,
+ >,
+ // Convert an XCM MultiLocation into a local account id:
+ LocationToAccountId,
+ // Our chain's account ID type (we can't get away without mentioning it explicitly):
+ AccountId,
+ // We only want to allow teleports of known assets. We use non-zero issuance as an indication
+ // that this asset is known.
+ NonZeroIssuance,
+ // The account to use for tracking teleports.
+ CheckingAccount,
+>;
+
+/// Means for transacting assets on this chain.
+pub type AssetTransactors = (CurrencyTransactor, FungiblesTransactor);
+
+/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance,
+/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can
+/// biases the kind of local `Origin` it will become.
+pub type XcmOriginToTransactDispatchOrigin = (
+ // Sovereign account converter; this attempts to derive an `AccountId` from the origin location
+ // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for
+ // foreign chains who want to have a local sovereign account on this chain which they control.
+ SovereignSignedViaLocation,
+ // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when
+ // recognized.
+ RelayChainAsNative,
+ // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when
+ // recognized.
+ SiblingParachainAsNative,
+ // Native signed account converter; this just converts an `AccountId32` origin into a normal
+ // `Origin::Signed` origin of the same 32-byte value.
+ SignedAccountId32AsNative,
+ // Xcm origins can be represented natively under the Xcm pallet's Xcm origin.
+ XcmPassthrough,
+);
+
+parameter_types! {
+ // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate.
+ pub UnitWeightCost: Weight = 1_000_000_000;
+ pub const MaxInstructions: u32 = 100;
+}
+
+match_types! {
+ pub type ParentOrParentsExecutivePlurality: impl Contains = {
+ MultiLocation { parents: 1, interior: Here } |
+ MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Executive, .. }) }
+ };
+ pub type CommonGoodAssetsParachain: impl Contains = {
+ MultiLocation { parents: 1, interior: X1(Parachain(1000)) }
+ };
+}
+
+//TODO: move DenyThenTry to polkadot's xcm module.
+/// Deny executing the xcm message if it matches any of the Deny filter regardless of anything else.
+/// If it passes the Deny, and matches one of the Allow cases then it is let through.
+pub struct DenyThenTry(PhantomData, PhantomData)
+where
+ Deny: ShouldExecute,
+ Allow: ShouldExecute;
+
+impl ShouldExecute for DenyThenTry
+where
+ Deny: ShouldExecute,
+ Allow: ShouldExecute,
+{
+ fn should_execute(
+ origin: &MultiLocation,
+ message: &mut Xcm,
+ max_weight: Weight,
+ weight_credit: &mut Weight,
+ ) -> Result<(), ()> {
+ Deny::should_execute(origin, message, max_weight, weight_credit)?;
+ Allow::should_execute(origin, message, max_weight, weight_credit)
+ }
+}
+
+// See issue #5233
+pub struct DenyReserveTransferToRelayChain;
+impl ShouldExecute for DenyReserveTransferToRelayChain {
+ fn should_execute(
+ origin: &MultiLocation,
+ message: &mut Xcm,
+ _max_weight: Weight,
+ _weight_credit: &mut Weight,
+ ) -> Result<(), ()> {
+ if message.0.iter().any(|inst| {
+ matches!(
+ inst,
+ InitiateReserveWithdraw {
+ reserve: MultiLocation { parents: 1, interior: Here },
+ ..
+ } | DepositReserveAsset { dest: MultiLocation { parents: 1, interior: Here }, .. } |
+ TransferReserveAsset {
+ dest: MultiLocation { parents: 1, interior: Here },
+ ..
+ }
+ )
+ }) {
+ return Err(()) // Deny
+ }
+
+ // allow reserve transfers to arrive from relay chain
+ if matches!(origin, MultiLocation { parents: 1, interior: Here }) &&
+ message.0.iter().any(|inst| matches!(inst, ReserveAssetDeposited { .. }))
+ {
+ log::warn!(
+ target: "xcm::barriers",
+ "Unexpected ReserveAssetDeposited from the relay chain",
+ );
+ }
+ // Permit everything else
+ Ok(())
+ }
+}
+
+pub type Barrier = DenyThenTry<
+ DenyReserveTransferToRelayChain,
+ (
+ TakeWeightCredit,
+ AllowTopLevelPaidExecutionFrom,
+ // Parent and its exec plurality get free execution
+ AllowUnpaidExecutionFrom,
+ // Assets Common Good parachain gets free execution
+ AllowUnpaidExecutionFrom,
+ // Expected responses are OK.
+ AllowKnownQueryResponses,
+ // Subscriptions for version tracking are OK.
+ AllowSubscriptionsFrom,
+ ),
+>;
+
+/// Type alias to conveniently refer to `frame_system`'s `Config::AccountId`.
+pub type AccountIdOf = ::AccountId;
+
+/// Asset filter that allows all assets from a certain location.
+pub struct AssetsFrom(PhantomData);
+impl> FilterAssetLocation for AssetsFrom {
+ fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool {
+ let loc = T::get();
+ &loc == origin &&
+ matches!(asset, MultiAsset { id: AssetId::Concrete(asset_loc), fun: Fungible(_a) }
+ if asset_loc.match_and_split(&loc).is_some())
+ }
+}
+
+/// Allow checking in assets that have issuance > 0.
+pub struct NonZeroIssuance(PhantomData<(AccountId, Assets)>);
+impl Contains<>::AssetId>
+ for NonZeroIssuance
+where
+ Assets: fungibles::Inspect,
+{
+ fn contains(id: &>::AssetId) -> bool {
+ !Assets::total_issuance(*id).is_zero()
+ }
+}
+
+/// A `HandleCredit` implementation that naively transfers the fees to the block author.
+/// Will drop and burn the assets in case the transfer fails.
+pub struct AssetsToBlockAuthor(PhantomData);
+impl HandleCredit, pallet_assets::Pallet> for AssetsToBlockAuthor
+where
+ R: pallet_authorship::Config + pallet_assets::Config,
+ AccountIdOf:
+ From + Into,
+{
+ fn handle_credit(credit: CreditOf, pallet_assets::Pallet>) {
+ if let Some(author) = pallet_authorship::Pallet::::author() {
+ // In case of error: Will drop the result triggering the `OnDrop` of the imbalance.
+ let _ = pallet_assets::Pallet::::resolve(&author, credit);
+ }
+ }
+}
+
+pub trait Reserve {
+ /// Returns assets reserve location.
+ fn reserve(&self) -> Option;
+}
+
+// Takes the chain part of a MultiAsset
+impl Reserve for MultiAsset {
+ fn reserve(&self) -> Option {
+ if let AssetId::Concrete(location) = self.id.clone() {
+ let first_interior = location.first_interior();
+ let parents = location.parent_count();
+ match (parents, first_interior.clone()) {
+ (0, Some(Parachain(id))) => Some(MultiLocation::new(0, X1(Parachain(id.clone())))),
+ (1, Some(Parachain(id))) => Some(MultiLocation::new(1, X1(Parachain(id.clone())))),
+ (1, _) => Some(MultiLocation::parent()),
+ _ => None,
+ }
+ } else {
+ None
+ }
+ }
+}
+
+/// A `FilterAssetLocation` implementation. Filters multi native assets whose
+/// reserve is same with `origin`.
+pub struct MultiNativeAsset;
+impl FilterAssetLocation for MultiNativeAsset {
+ fn filter_asset_location(asset: &MultiAsset, origin: &MultiLocation) -> bool {
+ if let Some(ref reserve) = asset.reserve() {
+ if reserve == origin {
+ return true
+ }
+ }
+ false
+ }
+}
+
+parameter_types! {
+ pub CommonGoodAssetsLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(1000)));
+ // ALWAYS ensure that the index in PalletInstance stays up-to-date with
+ // Statemint's Assets pallet index
+ pub CommonGoodAssetsPalletLocation: MultiLocation =
+ MultiLocation::new(1, X2(Parachain(1000), PalletInstance(50)));
+ pub CheckingAccount: AccountId = PolkadotXcm::check_account();
+}
+
+pub type Reserves = (NativeAsset, AssetsFrom);
+
+pub struct XcmConfig;
+impl xcm_executor::Config for XcmConfig {
+ type Call = Call;
+ type XcmSender = XcmRouter;
+ // How to withdraw and deposit an asset.
+ type AssetTransactor = AssetTransactors;
+ type OriginConverter = XcmOriginToTransactDispatchOrigin;
+ type IsReserve = MultiNativeAsset; // TODO: maybe needed to be replaced by Reserves
+ type IsTeleporter = NativeAsset;
+ type LocationInverter = LocationInverter;
+ type Barrier = Barrier;
+ type Weigher = FixedWeightBounds;
+ type Trader =
+ UsingComponents>;
+ type ResponseHandler = PolkadotXcm;
+ type AssetTrap = PolkadotXcm;
+ type AssetClaims = PolkadotXcm;
+ type SubscriptionService = PolkadotXcm;
+}
+
+/// No local origins on this chain are allowed to dispatch XCM sends/executions.
+pub type LocalOriginToLocation = SignedToAccountId32;
+
+/// The means for routing XCM messages which are not for local execution into the right message
+/// queues.
+pub type XcmRouter = (
+ // Two routers - use UMP to communicate with the relay chain:
+ cumulus_primitives_utility::ParentAsUmp,
+ // ..and XCMP to communicate with the sibling chains.
+ XcmpQueue,
+);
+
+impl pallet_xcm::Config for Runtime {
+ type Event = Event;
+ type SendXcmOrigin = EnsureXcmOrigin;
+ type XcmRouter = XcmRouter;
+ type ExecuteXcmOrigin = EnsureXcmOrigin;
+ type XcmExecuteFilter = Nothing;
+ // ^ Disable dispatchable execute on the XCM pallet.
+ // Needs to be `Everything` for local testing.
+ type XcmExecutor = XcmExecutor;
+ type XcmTeleportFilter = Everything;
+ type XcmReserveTransferFilter = Everything;
+ type Weigher = FixedWeightBounds;
+ type LocationInverter = LocationInverter;
+ type Origin = Origin;
+ type Call = Call;
+
+ const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
+ // ^ Override for AdvertisedXcmVersion default
+ type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
+}
+
+impl cumulus_pallet_xcm::Config for Runtime {
+ type Event = Event;
+ type XcmExecutor = XcmExecutor;
+}
diff --git a/polkadot-parachain/Cargo.toml b/polkadot-parachain/Cargo.toml
index d677df57d7..78a44a198d 100644
--- a/polkadot-parachain/Cargo.toml
+++ b/polkadot-parachain/Cargo.toml
@@ -23,6 +23,7 @@ statemint-runtime = { path = "../parachains/runtimes/assets/statemint" }
statemine-runtime = { path = "../parachains/runtimes/assets/statemine" }
westmint-runtime = { path = "../parachains/runtimes/assets/westmint" }
contracts-rococo-runtime = { path = "../parachains/runtimes/contracts/contracts-rococo" }
+penpal-runtime = { path = "../parachains/runtimes/testing/penpal" }
jsonrpsee = { version = "0.14.0", features = ["server"] }
parachains-common = { path = "../parachains/common" }
diff --git a/polkadot-parachain/src/chain_spec/mod.rs b/polkadot-parachain/src/chain_spec/mod.rs
index f5e442f5e2..fe26ce2a66 100644
--- a/polkadot-parachain/src/chain_spec/mod.rs
+++ b/polkadot-parachain/src/chain_spec/mod.rs
@@ -24,6 +24,7 @@ use sp_core::{crypto::UncheckedInto, sr25519, Pair, Public};
use sp_runtime::traits::{IdentifyAccount, Verify};
pub mod contracts;
+pub mod penpal;
pub mod seedling;
pub mod shell;
pub mod statemint;
diff --git a/polkadot-parachain/src/chain_spec/penpal.rs b/polkadot-parachain/src/chain_spec/penpal.rs
new file mode 100644
index 0000000000..fb127e5168
--- /dev/null
+++ b/polkadot-parachain/src/chain_spec/penpal.rs
@@ -0,0 +1,136 @@
+// Copyright 2019-2021 Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus 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.
+
+// Cumulus 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 Cumulus. If not, see .
+
+use crate::chain_spec::{get_account_id_from_seed, Extensions};
+use cumulus_primitives_core::ParaId;
+// use rococo_parachain_runtime::{AuraId};
+use crate::chain_spec::{get_collator_keys_from_seed, SAFE_XCM_VERSION};
+use sc_service::ChainType;
+use sp_core::sr25519;
+/// Specialized `ChainSpec` for the normal parachain runtime.
+pub type PenpalChainSpec = sc_service::GenericChainSpec;
+
+pub fn get_penpal_chain_spec(id: ParaId, relay_chain: &str) -> PenpalChainSpec {
+ // Give your base currency a unit name and decimal places
+ let mut properties = sc_chain_spec::Properties::new();
+ properties.insert("tokenSymbol".into(), "UNIT".into());
+ properties.insert("tokenDecimals".into(), 12u32.into());
+ properties.insert("ss58Format".into(), 42u32.into());
+
+ PenpalChainSpec::from_genesis(
+ // Name
+ "Penpal Parachain",
+ // ID
+ &format!("penpal-{}", relay_chain.replace("-local", "")),
+ ChainType::Development,
+ move || {
+ penpal_testnet_genesis(
+ // initial collators.
+ vec![
+ (
+ get_account_id_from_seed::("Alice"),
+ get_collator_keys_from_seed::("Alice"),
+ ),
+ (
+ get_account_id_from_seed::("Bob"),
+ get_collator_keys_from_seed::("Bob"),
+ ),
+ ],
+ vec![
+ get_account_id_from_seed::("Alice"),
+ get_account_id_from_seed::("Bob"),
+ get_account_id_from_seed::("Charlie"),
+ get_account_id_from_seed::("Dave"),
+ get_account_id_from_seed::("Eve"),
+ get_account_id_from_seed::("Ferdie"),
+ get_account_id_from_seed::("Alice//stash"),
+ get_account_id_from_seed::("Bob//stash"),
+ get_account_id_from_seed::("Charlie//stash"),
+ get_account_id_from_seed::("Dave//stash"),
+ get_account_id_from_seed::("Eve//stash"),
+ get_account_id_from_seed::("Ferdie//stash"),
+ ],
+ id,
+ )
+ },
+ Vec::new(),
+ None,
+ None,
+ None,
+ None,
+ Extensions {
+ relay_chain: relay_chain.into(), // You MUST set this to the correct network!
+ para_id: id.into(),
+ },
+ )
+}
+
+fn penpal_testnet_genesis(
+ invulnerables: Vec<(penpal_runtime::AccountId, penpal_runtime::AuraId)>,
+ endowed_accounts: Vec,
+ id: ParaId,
+) -> penpal_runtime::GenesisConfig {
+ penpal_runtime::GenesisConfig {
+ system: penpal_runtime::SystemConfig {
+ code: penpal_runtime::WASM_BINARY
+ .expect("WASM binary was not build, please build it!")
+ .to_vec(),
+ },
+ balances: penpal_runtime::BalancesConfig {
+ balances: endowed_accounts
+ .iter()
+ .cloned()
+ .map(|k| (k, penpal_runtime::EXISTENTIAL_DEPOSIT * 4096))
+ .collect(),
+ },
+ parachain_info: penpal_runtime::ParachainInfoConfig { parachain_id: id },
+ collator_selection: penpal_runtime::CollatorSelectionConfig {
+ invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(),
+ candidacy_bond: penpal_runtime::EXISTENTIAL_DEPOSIT * 16,
+ ..Default::default()
+ },
+ session: penpal_runtime::SessionConfig {
+ keys: invulnerables
+ .into_iter()
+ .map(|(acc, aura)| {
+ (
+ acc.clone(), // account id
+ acc, // validator id
+ penpal_session_keys(aura), // session keys
+ )
+ })
+ .collect(),
+ },
+ // no need to pass anything to aura, in fact it will panic if we do. Session will take care
+ // of this.
+ aura: Default::default(),
+ aura_ext: Default::default(),
+ parachain_system: Default::default(),
+ polkadot_xcm: penpal_runtime::PolkadotXcmConfig {
+ safe_xcm_version: Some(SAFE_XCM_VERSION),
+ },
+ sudo: penpal_runtime::SudoConfig {
+ key: Some(get_account_id_from_seed::("Alice")),
+ },
+ }
+}
+
+/// Generate the session keys from individual elements.
+///
+/// The input must be a tuple of individual keys (a single arg for now since we have just one key).
+pub fn penpal_session_keys(keys: penpal_runtime::AuraId) -> penpal_runtime::SessionKeys {
+ penpal_runtime::SessionKeys { aura: keys }
+}
diff --git a/polkadot-parachain/src/command.rs b/polkadot-parachain/src/command.rs
index ab325744c3..ac76e20d48 100644
--- a/polkadot-parachain/src/command.rs
+++ b/polkadot-parachain/src/command.rs
@@ -14,8 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Cumulus. If not, see .
-use std::net::SocketAddr;
-
use codec::Encode;
use cumulus_client_cli::generate_genesis_block;
use cumulus_primitives_core::ParaId;
@@ -32,6 +30,7 @@ use sc_service::{
};
use sp_core::hexdisplay::HexDisplay;
use sp_runtime::traits::{AccountIdConversion, Block as BlockT};
+use std::net::SocketAddr;
use crate::{
chain_spec,
@@ -50,6 +49,7 @@ enum Runtime {
Statemint,
Statemine,
Westmint,
+ Penpal(ParaId),
ContractsRococo,
}
@@ -73,6 +73,7 @@ impl ChainType
}
fn runtime(id: &str) -> Runtime {
+ let (_, id, para_id) = extract_parachain_id(id);
if id.starts_with("shell") {
Runtime::Shell
} else if id.starts_with("seedling") {
@@ -83,6 +84,8 @@ fn runtime(id: &str) -> Runtime {
Runtime::Statemine
} else if id.starts_with("westmint") {
Runtime::Westmint
+ } else if id.starts_with("penpal") {
+ Runtime::Penpal(para_id.unwrap_or(ParaId::new(0)))
} else if id.starts_with("contracts-rococo") {
Runtime::ContractsRococo
} else {
@@ -91,6 +94,7 @@ fn runtime(id: &str) -> Runtime {
}
fn load_spec(id: &str) -> std::result::Result, String> {
+ let (id, _, para_id) = extract_parachain_id(id);
Ok(match id {
"staging" => Box::new(chain_spec::staging_test_net()),
"tick" => Box::new(chain_spec::ChainSpec::from_json_bytes(
@@ -142,6 +146,14 @@ fn load_spec(id: &str) -> std::result::Result, String> {
)?),
// -- Fallback (generic chainspec)
"" => Box::new(chain_spec::get_chain_spec()),
+ "penpal-kusama" => Box::new(chain_spec::penpal::get_penpal_chain_spec(
+ para_id.expect("Must specify parachain id"),
+ "kusama-local",
+ )),
+ "penpal-polkadot" => Box::new(chain_spec::penpal::get_penpal_chain_spec(
+ para_id.expect("Must specify parachain id"),
+ "polkadot-local",
+ )),
// -- Loading a specific spec from disk
path => {
let chain_spec = chain_spec::ChainSpec::from_json_file(path.into())?;
@@ -161,12 +173,36 @@ fn load_spec(id: &str) -> std::result::Result, String> {
Runtime::ContractsRococo => Box::new(
chain_spec::contracts::ContractsRococoChainSpec::from_json_file(path.into())?,
),
+ Runtime::Penpal(_para_id) =>
+ Box::new(chain_spec::penpal::PenpalChainSpec::from_json_file(path.into())?),
Runtime::Generic => Box::new(chain_spec),
}
},
})
}
+/// Extracts the normalized chain id and parachain id from the input chain id.
+/// (H/T to Phala for the idea)
+/// E.g. "penpal-kusama-2004" yields ("penpal-kusama", Some(2004))
+fn extract_parachain_id(id: &str) -> (&str, &str, Option) {
+ const KUSAMA_TEST_PARA_PREFIX: &str = "penpal-kusama-";
+ const POLKADOT_TEST_PARA_PREFIX: &str = "penpal-polkadot-";
+
+ let (norm_id, orig_id, para) = if id.starts_with(KUSAMA_TEST_PARA_PREFIX) {
+ let suffix = &id[KUSAMA_TEST_PARA_PREFIX.len()..];
+ let para_id: u32 = suffix.parse().expect("Invalid parachain-id suffix");
+ (&id[..KUSAMA_TEST_PARA_PREFIX.len() - 1], id, Some(para_id))
+ } else if id.starts_with(POLKADOT_TEST_PARA_PREFIX) {
+ let suffix = &id[POLKADOT_TEST_PARA_PREFIX.len()..];
+ let para_id: u32 = suffix.parse().expect("Invalid parachain-id suffix");
+ (&id[..POLKADOT_TEST_PARA_PREFIX.len() - 1], id, Some(para_id))
+ } else {
+ (id, id, None)
+ };
+
+ (norm_id, orig_id, para.map(Into::into))
+}
+
impl SubstrateCli for Cli {
fn impl_name() -> String {
"Polkadot parachain".into()
@@ -210,6 +246,7 @@ impl SubstrateCli for Cli {
Runtime::Shell => &shell_runtime::VERSION,
Runtime::Seedling => &seedling_runtime::VERSION,
Runtime::ContractsRococo => &contracts_rococo_runtime::VERSION,
+ Runtime::Penpal(_) => &penpal_runtime::VERSION,
Runtime::Generic => &rococo_parachain_runtime::VERSION,
}
}
@@ -349,7 +386,7 @@ macro_rules! construct_async_run {
{ $( $code )* }.map(|v| (v, task_manager))
})
},
- Runtime::Generic => {
+ Runtime::Penpal(_) | Runtime::Generic => {
runner.async_run(|$config| {
let $components = new_partial::<
rococo_parachain_runtime::RuntimeApi,
@@ -591,16 +628,17 @@ pub fn run() -> Result<()> {
.await
.map(|r| r.0)
.map_err(Into::into),
- Runtime::Generic => crate::service::start_rococo_parachain_node(
- config,
- polkadot_config,
- collator_options,
- id,
- hwbench,
- )
- .await
- .map(|r| r.0)
- .map_err(Into::into),
+ Runtime::Penpal(_) | Runtime::Generic =>
+ crate::service::start_rococo_parachain_node(
+ config,
+ polkadot_config,
+ collator_options,
+ id,
+ hwbench,
+ )
+ .await
+ .map(|r| r.0)
+ .map_err(Into::into),
}
})
},