diff --git a/cumulus/pallets/collator-selection/Cargo.toml b/cumulus/pallets/collator-selection/Cargo.toml index eef9e701357c9..6c24c20917a98 100644 --- a/cumulus/pallets/collator-selection/Cargo.toml +++ b/cumulus/pallets/collator-selection/Cargo.toml @@ -30,6 +30,7 @@ sp-runtime = { workspace = true } sp-staking = { workspace = true } frame-benchmarking = { optional = true, workspace = true } +cumulus-pallet-session-benchmarking = { default-features = false, optional = true, workspace = true } [dev-dependencies] pallet-aura = { workspace = true, default-features = true } @@ -42,6 +43,7 @@ sp-tracing = { workspace = true, default-features = true } [features] default = ["std"] runtime-benchmarks = [ + "cumulus-pallet-session-benchmarking/runtime-benchmarks", "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", @@ -52,6 +54,7 @@ runtime-benchmarks = [ ] std = [ "codec/std", + "cumulus-pallet-session-benchmarking?/std", "frame-benchmarking/std", "frame-support/std", "frame-system/std", diff --git a/cumulus/pallets/collator-selection/src/benchmarking.rs b/cumulus/pallets/collator-selection/src/benchmarking.rs index 706695106ba69..53a522e3d5b9c 100644 --- a/cumulus/pallets/collator-selection/src/benchmarking.rs +++ b/cumulus/pallets/collator-selection/src/benchmarking.rs @@ -24,6 +24,7 @@ use crate::Pallet as CollatorSelection; use alloc::vec::Vec; use codec::Decode; use core::cmp; +use cumulus_pallet_session_benchmarking as session_benchmarking; use frame_benchmarking::{account, v2::*, whitelisted_caller, BenchmarkError}; use frame_support::traits::{Currency, EnsureOrigin, Get, ReservableCurrency}; use frame_system::{pallet_prelude::BlockNumberFor, EventRecord, RawOrigin}; @@ -31,11 +32,11 @@ use pallet_authorship::EventHandler; use pallet_session::{self as session, SessionManager}; pub type BalanceOf = - <::Currency as Currency<::AccountId>>::Balance; + <::Currency as Currency<::AccountId>>::Balance; const SEED: u32 = 0; -fn assert_last_event(generic_event: ::RuntimeEvent) { +fn assert_last_event(generic_event: ::RuntimeEvent) { let events = frame_system::Pallet::::events(); let system_event: ::RuntimeEvent = generic_event.into(); // compare to the last event record @@ -54,35 +55,23 @@ fn create_funded_user( user } -fn keys(c: u32) -> ::Keys { - use rand::{RngCore, SeedableRng}; +fn validator( + c: u32, +) -> (T::AccountId, ::Keys, Vec) { + let validator = create_funded_user::("candidate", c, 1000); + let (keys, proof) = T::generate_session_keys_and_proof(validator.clone()); - let keys = { - let mut keys = [0u8; 128]; - - if c > 0 { - let mut rng = rand::rngs::StdRng::seed_from_u64(c as u64); - rng.fill_bytes(&mut keys); - } - - keys - }; - - Decode::decode(&mut &keys[..]).unwrap() + (validator, keys, proof) } -fn validator(c: u32) -> (T::AccountId, ::Keys) { - (create_funded_user::("candidate", c, 1000), keys::(c)) -} - -fn register_validators(count: u32) -> Vec { +fn register_validators(count: u32) -> Vec { let validators = (0..count).map(|c| validator::(c)).collect::>(); - for (who, keys) in validators.clone() { - >::set_keys(RawOrigin::Signed(who).into(), keys, Vec::new()).unwrap(); + for (who, keys, proof) in validators.clone() { + >::set_keys(RawOrigin::Signed(who).into(), keys, proof).unwrap(); } - validators.into_iter().map(|(who, _)| who).collect() + validators.into_iter().map(|(who, _, _)| who).collect() } fn register_candidates(count: u32) { @@ -113,7 +102,7 @@ fn min_invulnerables() -> u32 { min_collators.saturating_sub(candidates_length) } -#[benchmarks(where T: pallet_authorship::Config + session::Config)] +#[benchmarks(where T: pallet_authorship::Config + session_benchmarking::Config)] mod benchmarks { use super::*; @@ -154,15 +143,14 @@ mod benchmarks { // add one more to the list. should not be in `b` (invulnerables) because it's the account // we will _add_ to invulnerables. we want it to be in `candidates` because we need the // weight associated with removing it. - let (new_invulnerable, new_invulnerable_keys) = validator::(b.max(c) + 1); - candidates.push((new_invulnerable.clone(), new_invulnerable_keys)); + let (new_invulnerable, new_invulnerable_keys, new_proof) = validator::(b.max(c) + 1); + candidates.push((new_invulnerable.clone(), new_invulnerable_keys, new_proof)); // set their keys ... - for (who, keys) in candidates.clone() { - >::set_keys(RawOrigin::Signed(who).into(), keys, Vec::new()) - .unwrap(); + for (who, keys, proof) in candidates.clone() { + >::set_keys(RawOrigin::Signed(who).into(), keys, proof).unwrap(); } // ... and register them. - for (who, _) in candidates.iter() { + for (who, _, _) in candidates.iter() { let deposit = CandidacyBond::::get(); ::Currency::make_free_balance_be(who, deposit * 1000_u32.into()); CandidateList::::try_mutate(|list| { @@ -297,13 +285,10 @@ mod benchmarks { let caller: T::AccountId = whitelisted_caller(); let bond: BalanceOf = ::Currency::minimum_balance() * 2u32.into(); ::Currency::make_free_balance_be(&caller, bond); + let (keys, proof) = T::generate_session_keys_and_proof(caller.clone()); - >::set_keys( - RawOrigin::Signed(caller.clone()).into(), - keys::(c + 1), - Vec::new(), - ) - .unwrap(); + >::set_keys(RawOrigin::Signed(caller.clone()).into(), keys, proof) + .unwrap(); #[extrinsic_call] _(RawOrigin::Signed(caller.clone())); @@ -325,12 +310,10 @@ mod benchmarks { let bond: BalanceOf = ::Currency::minimum_balance() * 10u32.into(); ::Currency::make_free_balance_be(&caller, bond); - >::set_keys( - RawOrigin::Signed(caller.clone()).into(), - keys::(c + 1), - Vec::new(), - ) - .unwrap(); + let (keys, proof) = T::generate_session_keys_and_proof(caller.clone()); + + >::set_keys(RawOrigin::Signed(caller.clone()).into(), keys, proof) + .unwrap(); let target = CandidateList::::get().iter().last().unwrap().who.clone(); diff --git a/cumulus/pallets/collator-selection/src/mock.rs b/cumulus/pallets/collator-selection/src/mock.rs index 034ef2189deab..6c2365ad11e1e 100644 --- a/cumulus/pallets/collator-selection/src/mock.rs +++ b/cumulus/pallets/collator-selection/src/mock.rs @@ -174,6 +174,17 @@ impl Config for Test { type WeightInfo = (); } +#[cfg(feature = "runtime-benchmarks")] +impl cumulus_pallet_session_benchmarking::Config for Test { + fn generate_session_keys_and_proof(owner: Self::AccountId) -> (Self::Keys, Vec) { + use codec::Encode; + + let keys = MockSessionKeys::generate(&owner.encode(), None); + + (keys.keys, keys.proof.encode()) + } +} + pub fn new_test_ext() -> sp_io::TestExternalities { sp_tracing::try_init_simple(); let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); diff --git a/cumulus/pallets/collator-selection/src/tests.rs b/cumulus/pallets/collator-selection/src/tests.rs index e8b2a4e146a2f..3ef5f45ff243f 100644 --- a/cumulus/pallets/collator-selection/src/tests.rs +++ b/cumulus/pallets/collator-selection/src/tests.rs @@ -18,6 +18,7 @@ use crate::{ mock::*, CandidacyBond, CandidateInfo, CandidateList, DesiredCandidates, Error, Invulnerables, LastAuthoredBlock, }; +use codec::Encode; use frame_support::{ assert_noop, assert_ok, traits::{Currency, OnInitialize}, @@ -124,7 +125,12 @@ fn invulnerable_limit_works() { if ii > 5 { Balances::make_free_balance_be(&ii, 100); let key = MockSessionKeys { aura: UintAuthorityId(ii) }; - Session::set_keys(RuntimeOrigin::signed(ii).into(), key, Vec::new()).unwrap(); + Session::set_keys( + RuntimeOrigin::signed(ii).into(), + key.clone(), + key.create_ownership_proof(&ii.encode()).unwrap().encode(), + ) + .unwrap(); } assert_eq!(Balances::free_balance(ii), 100); if ii < 21 { diff --git a/cumulus/pallets/session-benchmarking/src/inner.rs b/cumulus/pallets/session-benchmarking/src/inner.rs index 6c5188921362e..7e35dd22a0501 100644 --- a/cumulus/pallets/session-benchmarking/src/inner.rs +++ b/cumulus/pallets/session-benchmarking/src/inner.rs @@ -16,14 +16,21 @@ //! Benchmarking setup for pallet-session. #![cfg(feature = "runtime-benchmarks")] -use alloc::{vec, vec::Vec}; +use alloc::vec::Vec; use codec::Decode; use frame_benchmarking::v2::*; use frame_system::RawOrigin; use pallet_session::*; pub struct Pallet(pallet_session::Pallet); -pub trait Config: pallet_session::Config {} +pub trait Config: pallet_session::Config { + /// Generate a session key and a proof of ownership. + /// + /// The given `owner` is the account that will call `set_keys` using the returned session keys + /// and proof. This means that the proof should prove the ownership of `owner` over the private + /// keys associated to the session keys. + fn generate_session_keys_and_proof(owner: Self::AccountId) -> (Self::Keys, Vec); +} #[benchmarks] mod benchmarks { @@ -34,7 +41,7 @@ mod benchmarks { let caller: T::AccountId = whitelisted_caller(); frame_system::Pallet::::inc_providers(&caller); let keys = T::Keys::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()).unwrap(); - let proof: Vec = vec![0, 1, 2, 3]; + let (keys, proof) = T::generate_session_keys_and_proof(caller.clone()); #[extrinsic_call] _(RawOrigin::Signed(caller), keys, proof); @@ -47,7 +54,7 @@ mod benchmarks { let caller: T::AccountId = whitelisted_caller(); frame_system::Pallet::::inc_providers(&caller); let keys = T::Keys::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()).unwrap(); - let proof: Vec = vec![0, 1, 2, 3]; + let (keys, proof) = T::generate_session_keys_and_proof(caller.clone()); let _t = pallet_session::Pallet::::set_keys( RawOrigin::Signed(caller.clone()).into(), keys, diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index c26c17fbb7adf..a169a54a153af 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -48,6 +48,7 @@ use sp_runtime::{ transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, Permill, }; +use sp_session::OpaqueGeneratedSessionKeys; use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; #[cfg(feature = "std")] @@ -1433,8 +1434,8 @@ impl_runtime_apis! { } impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) + fn generate_session_keys(owner: Vec, seed: Option>) -> OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() } fn decode_session_keys( @@ -1699,7 +1700,12 @@ impl_runtime_apis! { } use cumulus_pallet_session_benchmarking::Pallet as SessionBench; - impl cumulus_pallet_session_benchmarking::Config for Runtime {} + impl cumulus_pallet_session_benchmarking::Config for Runtime { + fn generate_session_keys_and_proof(owner: Self::AccountId) -> (Self::Keys, Vec) { + let keys = SessionKeys::generate(&owner.encode(), None); + (keys.keys, keys.proof.encode()) + } + } use pallet_xcm_bridge_hub_router::benchmarking::{ Pallet as XcmBridgeHubRouterBench, diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 46094e233b1c7..d8a299b35bd24 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -1795,8 +1795,8 @@ pallet_revive::impl_runtime_apis_plus_revive!( } impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) + fn generate_session_keys(owner: Vec, seed: Option>) -> sp_session::OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() } fn decode_session_keys( @@ -2192,6 +2192,12 @@ pallet_revive::impl_runtime_apis_plus_revive!( use cumulus_pallet_session_benchmarking::Pallet as SessionBench; use xcm_config::{MaxAssetsIntoHolding, WestendLocation}; + impl cumulus_pallet_session_benchmarking::Config for Runtime { + fn generate_session_keys_and_proof(owner: Self::AccountId) -> (Self::Keys, Vec) { + let keys = SessionKeys::generate(&owner.encode(), None); + (keys.keys, keys.proof.encode()) + } + } impl cumulus_pallet_session_benchmarking::Config for Runtime {} use testnet_parachains_constants::westend::locations::{PeopleParaId, PeopleLocation}; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 2d951af2d2cde..285d988de41aa 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -807,8 +807,8 @@ impl_runtime_apis! { } impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) + fn generate_session_keys(owner: Vec, seed: Option>) -> sp_session::OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() } fn decode_session_keys( @@ -1097,6 +1097,7 @@ impl_runtime_apis! { ) -> Result, alloc::string::String> { use frame_benchmarking::{BenchmarkBatch, BenchmarkError}; use sp_storage::TrackedStorageKey; + use codec::Encode; use frame_system_benchmarking::Pallet as SystemBench; use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; @@ -1112,7 +1113,12 @@ impl_runtime_apis! { } use cumulus_pallet_session_benchmarking::Pallet as SessionBench; - impl cumulus_pallet_session_benchmarking::Config for Runtime {} + impl cumulus_pallet_session_benchmarking::Config for Runtime { + fn generate_session_keys_and_proof(owner: Self::AccountId) -> (Self::Keys, Vec) { + let keys = SessionKeys::generate(&owner.encode(), None); + (keys.keys, keys.proof.encode()) + } + } use xcm::latest::prelude::*; use xcm_config::TokenLocation; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index 99742ceed289f..c359f396fea7a 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -50,6 +50,8 @@ use sp_runtime::{ transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, }; + +use sp_session::OpaqueGeneratedSessionKeys; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; @@ -758,8 +760,8 @@ impl_runtime_apis! { } impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) + fn generate_session_keys(owner: Vec, seed: Option>) -> OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() } fn decode_session_keys( @@ -1037,6 +1039,7 @@ impl_runtime_apis! { ) -> Result, alloc::string::String> { use frame_benchmarking::{BenchmarkBatch, BenchmarkError}; use sp_storage::TrackedStorageKey; + use codec::Encode; use frame_system_benchmarking::Pallet as SystemBench; use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; @@ -1052,7 +1055,12 @@ impl_runtime_apis! { } use cumulus_pallet_session_benchmarking::Pallet as SessionBench; - impl cumulus_pallet_session_benchmarking::Config for Runtime {} + impl cumulus_pallet_session_benchmarking::Config for Runtime { + fn generate_session_keys_and_proof(owner: Self::AccountId) -> (Self::Keys, Vec) { + let keys = SessionKeys::generate(&owner.encode(), None); + (keys.keys, keys.proof.encode()) + } + } use xcm::latest::prelude::*; use xcm_config::WestendLocation; diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs index 97198c0a226eb..1b2e857f4946a 100644 --- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs @@ -946,8 +946,8 @@ impl_runtime_apis! { } impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) + fn generate_session_keys(owner: Vec, seed: Option>) -> sp_session::OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() } fn decode_session_keys( @@ -1154,7 +1154,12 @@ impl_runtime_apis! { } use cumulus_pallet_session_benchmarking::Pallet as SessionBench; - impl cumulus_pallet_session_benchmarking::Config for Runtime {} + impl cumulus_pallet_session_benchmarking::Config for Runtime { + fn generate_session_keys_and_proof(owner: Self::AccountId) -> (Self::Keys, Vec) { + let keys = SessionKeys::generate(&owner.encode(), None); + (keys.keys, keys.proof.encode()) + } + } use xcm_config::WndLocation; use testnet_parachains_constants::westend::locations::{AssetHubParaId, AssetHubLocation}; diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs new file mode 100644 index 0000000000000..c78b1c07beb68 --- /dev/null +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs @@ -0,0 +1,885 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// +// This program 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. +// +// This program 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 this program. If not, see . + +//! # Contracts Parachain +//! +//! A parachain for using FRAME's `pallet-contracts` and ink! contracts. + +#![cfg_attr(not(feature = "std"), no_std)] +#![recursion_limit = "256"] + +// Make the WASM binary available. +#[cfg(feature = "std")] +include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); + +mod contracts; +mod weights; +mod xcm_config; + +extern crate alloc; + +use alloc::{vec, vec::Vec}; +use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; +use cumulus_primitives_core::AggregateMessageOrigin; +use sp_api::impl_runtime_apis; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + traits::Block as BlockT, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, Perbill, +}; + +#[cfg(feature = "std")] +use sp_version::NativeVersion; +use sp_version::RuntimeVersion; + +use frame_support::{ + construct_runtime, derive_impl, + dispatch::DispatchClass, + genesis_builder_helper::{build_state, get_preset}, + parameter_types, + traits::{ConstBool, ConstU32, ConstU64, ConstU8}, + weights::{ConstantMultiplier, Weight, WeightToFee as _}, + PalletId, +}; +use frame_system::limits::{BlockLength, BlockWeights}; +pub use parachains_common as common; +use parachains_common::{ + impls::DealWithFees, message_queue::*, AccountId, BlockNumber, Hash, Header, Nonce, Signature, + AVERAGE_ON_INITIALIZE_RATIO, NORMAL_DISPATCH_RATIO, +}; +pub use parachains_common::{AuraId, Balance}; +use testnet_parachains_constants::rococo::{consensus::*, currency::*, fee::WeightToFee, time::*}; +use xcm::prelude::*; +use xcm_config::CollatorSelectionUpdateOrigin; +use xcm_runtime_apis::{ + dry_run::{CallDryRunEffects, Error as XcmDryRunApiError, XcmDryRunEffects}, + fees::Error as XcmPaymentApiError, +}; + +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; + +// Polkadot imports +use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate}; + +use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; + +/// The address format for describing accounts. +pub type Address = sp_runtime::MultiAddress; +/// 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; +/// 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_transaction_payment::ChargeTransactionPayment, + cumulus_primitives_storage_weight_reclaim::StorageWeightReclaim, +); +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic; + +/// Migrations to apply on runtime upgrade. +pub type Migrations = ( + pallet_collator_selection::migration::v1::MigrateToV1, + pallet_collator_selection::migration::v2::MigrationToV2, + cumulus_pallet_parachain_system::migration::Migration, + cumulus_pallet_xcmp_queue::migration::v2::MigrationToV2, + cumulus_pallet_xcmp_queue::migration::v3::MigrationToV3, + pallet_contracts::Migration, + // unreleased + cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, + cumulus_pallet_xcmp_queue::migration::v5::MigrateV4ToV5, + // permanent + pallet_xcm::migration::MigrateToLatestXcmVersion, +); + +type EventRecord = frame_system::EventRecord< + ::RuntimeEvent, + ::Hash, +>; + +/// Executive: handles dispatch to the various modules. +pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, + Migrations, +>; + +impl_opaque_keys! { + pub struct SessionKeys { + pub aura: Aura, + } +} + +#[sp_version::runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("contracts-rococo"), + impl_name: create_runtime_str!("contracts-rococo"), + authoring_version: 1, + spec_version: 1_015_000, + impl_version: 0, + apis: RUNTIME_API_VERSIONS, + transaction_version: 7, + state_version: 1, +}; + +/// 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; + 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: u8 = 42; +} + +// Configure FRAME pallets to include in runtime. +#[derive_impl(frame_system::config_preludes::ParaChainDefaultConfig)] +impl frame_system::Config for Runtime { + type BlockWeights = RuntimeBlockWeights; + type BlockLength = RuntimeBlockLength; + type AccountId = AccountId; + type Nonce = Nonce; + type Hash = Hash; + type Block = Block; + type BlockHashCount = BlockHashCount; + type DbWeight = RocksDbWeight; + type Version = Version; + type AccountData = pallet_balances::AccountData; + type SystemWeightInfo = frame_system::weights::SubstrateWeight; + type SS58Prefix = SS58Prefix; + type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; + type MaxConsumers = ConstU32<16>; +} + +impl pallet_timestamp::Config for Runtime { + /// A timestamp: milliseconds since the unix epoch. + type Moment = u64; + type OnTimestampSet = Aura; + type MinimumPeriod = ConstU64<0>; + type WeightInfo = pallet_timestamp::weights::SubstrateWeight; +} + +impl pallet_authorship::Config for Runtime { + type FindAuthor = pallet_session::FindAccountFromAuthorIndex; + type EventHandler = (CollatorSelection,); +} + +parameter_types! { + pub const ExistentialDeposit: Balance = EXISTENTIAL_DEPOSIT; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = ConstU32<50>; + /// The type for recording an account's balance. + type Balance = Balance; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = pallet_balances::weights::SubstrateWeight; + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<0>; +} + +parameter_types! { + pub const TransactionByteFee: Balance = MILLICENTS; +} + +impl pallet_transaction_payment::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = + pallet_transaction_payment::FungibleAdapter>; + type WeightToFee = WeightToFee; + /// Relay Chain `TransactionByteFee` / 10 + type LengthToFee = ConstantMultiplier; + type FeeMultiplierUpdate = SlowAdjustingFeeUpdate; + type OperationalFeeMultiplier = ConstU8<5>; +} + +parameter_types! { + // One storage item; key size is 32; value is size 4+4+16+32 bytes = 56 bytes. + pub const DepositBase: Balance = deposit(1, 88); + // Additional storage item size of 32 bytes. + pub const DepositFactor: Balance = deposit(0, 32); +} + +impl pallet_multisig::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type DepositBase = DepositBase; + type DepositFactor = DepositFactor; + type MaxSignatories = ConstU32<100>; + type WeightInfo = pallet_multisig::weights::SubstrateWeight; +} + +impl pallet_utility::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = pallet_utility::weights::SubstrateWeight; +} + +parameter_types! { + pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); + pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent; +} + +impl cumulus_pallet_parachain_system::Config for Runtime { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type OnSystemEvent = (); + type SelfParaId = parachain_info::Pallet; + type DmpQueue = frame_support::traits::EnqueueWithOrigin; + type ReservedDmpWeight = ReservedDmpWeight; + type OutboundXcmpMessageSource = XcmpQueue; + type XcmpMessageHandler = XcmpQueue; + type ReservedXcmpWeight = ReservedXcmpWeight; + type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; + type ConsensusHook = ConsensusHook; +} + +type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< + Runtime, + RELAY_CHAIN_SLOT_DURATION_MILLIS, + BLOCK_PROCESSING_VELOCITY, + UNINCLUDED_SEGMENT_CAPACITY, +>; + +impl pallet_insecure_randomness_collective_flip::Config for Runtime {} + +impl parachain_info::Config for Runtime {} + +parameter_types! { + pub MessageQueueServiceWeight: Weight = Perbill::from_percent(35) * RuntimeBlockWeights::get().max_block; +} + +impl pallet_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor< + cumulus_primitives_core::AggregateMessageOrigin, + >; + #[cfg(not(feature = "runtime-benchmarks"))] + type MessageProcessor = xcm_builder::ProcessXcmMessage< + AggregateMessageOrigin, + xcm_executor::XcmExecutor, + RuntimeCall, + >; + type Size = u32; + // The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin: + type QueueChangeHandler = NarrowOriginToSibling; + type QueuePausedQuery = NarrowOriginToSibling; + type HeapSize = sp_core::ConstU32<{ 103 * 1024 }>; + type MaxStale = sp_core::ConstU32<8>; + type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = MessageQueueServiceWeight; +} + +impl cumulus_pallet_aura_ext::Config for Runtime {} + +parameter_types! { + pub const Period: u32 = 10 * MINUTES; + pub const Offset: u32 = 0; +} + +impl pallet_session::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + 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 let's be pedantic. + type SessionHandler = ::KeyTypeIdProviders; + type Keys = SessionKeys; + type WeightInfo = pallet_session::weights::SubstrateWeight; +} + +impl pallet_aura::Config for Runtime { + type AuthorityId = AuraId; + type DisabledValidators = (); + type MaxAuthorities = ConstU32<100_000>; + type AllowMultipleBlocksPerSlot = ConstBool; + type SlotDuration = ConstU64; +} + +parameter_types! { + pub const PotId: PalletId = PalletId(*b"PotStake"); +} + +impl pallet_collator_selection::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type UpdateOrigin = CollatorSelectionUpdateOrigin; + type PotId = PotId; + type MaxCandidates = ConstU32<100>; + type MinEligibleCollators = ConstU32<1>; + type MaxInvulnerables = ConstU32<20>; + // 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 = pallet_collator_selection::weights::SubstrateWeight; +} + +impl pallet_sudo::Config for Runtime { + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_sudo::weights::SubstrateWeight; +} + +// Create the runtime by composing the FRAME pallets that were previously configured. +construct_runtime!( + pub enum Runtime + { + // System support stuff. + System: frame_system = 0, + ParachainSystem: cumulus_pallet_parachain_system = 1, + RandomnessCollectiveFlip: pallet_insecure_randomness_collective_flip = 2, + Timestamp: pallet_timestamp = 3, + ParachainInfo: parachain_info = 4, + + // Monetary stuff. + Balances: pallet_balances = 10, + TransactionPayment: pallet_transaction_payment = 11, + + // Collator support. The order of these 5 are important and shall not change. + Authorship: pallet_authorship = 20, + CollatorSelection: pallet_collator_selection = 21, + Session: pallet_session = 22, + Aura: pallet_aura = 23, + AuraExt: cumulus_pallet_aura_ext = 24, + + // XCM helpers. + XcmpQueue: cumulus_pallet_xcmp_queue = 30, + PolkadotXcm: pallet_xcm = 31, + CumulusXcm: cumulus_pallet_xcm = 32, + MessageQueue: pallet_message_queue = 34, + + // Smart Contracts. + Contracts: pallet_contracts = 40, + + // Handy utilities. + Utility: pallet_utility = 50, + Multisig: pallet_multisig = 51, + + // Sudo + Sudo: pallet_sudo = 100, + } +); + +#[cfg(feature = "runtime-benchmarks")] +mod benches { + frame_benchmarking::define_benchmarks!( + [frame_system, SystemBench::] + [pallet_balances, Balances] + [pallet_message_queue, MessageQueue] + [pallet_multisig, Multisig] + [pallet_session, SessionBench::] + [pallet_utility, Utility] + [pallet_sudo, Sudo] + [pallet_timestamp, Timestamp] + [pallet_collator_selection, CollatorSelection] + [cumulus_pallet_parachain_system, ParachainSystem] + [pallet_contracts, Contracts] + [pallet_xcm, PalletXcmExtrinsicsBenchmark::] + ); +} + +impl_runtime_apis! { + impl sp_consensus_aura::AuraApi for Runtime { + fn slot_duration() -> sp_consensus_aura::SlotDuration { + sp_consensus_aura::SlotDuration::from_millis(SLOT_DURATION) + } + + fn authorities() -> Vec { + pallet_aura::Authorities::::get().into_inner() + } + } + + impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { + fn can_build_upon( + included_hash: ::Hash, + slot: cumulus_primitives_aura::Slot, + ) -> bool { + ConsensusHook::can_build_upon(included_hash, slot) + } + } + + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block) + } + + fn initialize_block(header: &::Header) -> sp_runtime::ExtrinsicInclusionMode { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + + fn metadata_at_version(version: u32) -> Option { + Runtime::metadata_at_version(version) + } + + fn metadata_versions() -> alloc::vec::Vec { + Runtime::metadata_versions() + } + } + + 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(owner: Vec, seed: Option>) -> sp_session::OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() + } + + 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) -> Nonce { + 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) + } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi + for Runtime + { + fn query_call_info( + call: RuntimeCall, + len: u32, + ) -> pallet_transaction_payment::RuntimeDispatchInfo { + TransactionPayment::query_call_info(call, len) + } + fn query_call_fee_details( + call: RuntimeCall, + len: u32, + ) -> pallet_transaction_payment::FeeDetails { + TransactionPayment::query_call_fee_details(call, len) + } + fn query_weight_to_fee(weight: Weight) -> Balance { + TransactionPayment::weight_to_fee(weight) + } + fn query_length_to_fee(length: u32) -> Balance { + TransactionPayment::length_to_fee(length) + } + } + + impl xcm_runtime_apis::fees::XcmPaymentApi for Runtime { + fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result, XcmPaymentApiError> { + let acceptable_assets = vec![AssetId(xcm_config::RelayLocation::get())]; + PolkadotXcm::query_acceptable_payment_assets(xcm_version, acceptable_assets) + } + + fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result { + match asset.try_as::() { + Ok(asset_id) if asset_id.0 == xcm_config::RelayLocation::get() => { + // for native token + Ok(WeightToFee::weight_to_fee(&weight)) + }, + Ok(asset_id) => { + log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - unhandled asset_id: {asset_id:?}!"); + Err(XcmPaymentApiError::AssetNotFound) + }, + Err(_) => { + log::trace!(target: "xcm::xcm_runtime_apis", "query_weight_to_asset_fee - failed to convert asset: {asset:?}!"); + Err(XcmPaymentApiError::VersionedConversionFailed) + } + } + } + + fn query_xcm_weight(message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_xcm_weight(message) + } + + fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result { + PolkadotXcm::query_delivery_fees(destination, message) + } + } + + impl xcm_runtime_apis::dry_run::DryRunApi for Runtime { + fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_call::(origin, call) + } + + fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm) -> Result, XcmDryRunApiError> { + PolkadotXcm::dry_run_xcm::(origin_location, xcm) + } + } + + impl xcm_runtime_apis::conversions::LocationToAccountApi for Runtime { + fn convert_location(location: VersionedLocation) -> Result< + AccountId, + xcm_runtime_apis::conversions::Error + > { + xcm_runtime_apis::conversions::LocationToAccountHelper::< + AccountId, + xcm_config::LocationToAccountId + >::convert_location(location) + } + } + + impl cumulus_primitives_core::CollectCollationInfo for Runtime { + fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { + ParachainSystem::collect_collation_info(header) + } + } + + impl pallet_contracts::ContractsApi for Runtime { + fn call( + origin: AccountId, + dest: AccountId, + value: Balance, + gas_limit: Option, + storage_deposit_limit: Option, + input_data: Vec, + ) -> pallet_contracts::ContractExecResult { + let gas_limit = gas_limit.unwrap_or(RuntimeBlockWeights::get().max_block); + Contracts::bare_call( + origin, + dest, + value, + gas_limit, + storage_deposit_limit, + input_data, + contracts::CONTRACTS_DEBUG_OUTPUT, + pallet_contracts::CollectEvents::UnsafeCollect, + pallet_contracts::Determinism::Enforced, + ) + } + + fn instantiate( + origin: AccountId, + value: Balance, + gas_limit: Option, + storage_deposit_limit: Option, + code: pallet_contracts::Code, + data: Vec, + salt: Vec, + ) -> pallet_contracts::ContractInstantiateResult { + let gas_limit = gas_limit.unwrap_or(RuntimeBlockWeights::get().max_block); + Contracts::bare_instantiate( + origin, + value, + gas_limit, + storage_deposit_limit, + code, + data, + salt, + contracts::CONTRACTS_DEBUG_OUTPUT, + pallet_contracts::CollectEvents::UnsafeCollect, + ) + } + + fn upload_code( + origin: AccountId, + code: Vec, + storage_deposit_limit: Option, + determinism: pallet_contracts::Determinism, + ) -> pallet_contracts::CodeUploadResult { + Contracts::bare_upload_code( + origin, + code, + storage_deposit_limit, + determinism, + ) + } + + fn get_storage( + address: AccountId, + key: Vec, + ) -> pallet_contracts::GetStorageResult { + Contracts::get_storage(address, key) + } + } + + #[cfg(feature = "try-runtime")] + impl frame_try_runtime::TryRuntime for Runtime { + fn on_runtime_upgrade(checks: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { + let weight = Executive::try_runtime_upgrade(checks).unwrap(); + (weight, RuntimeBlockWeights::get().max_block) + } + + fn execute_block( + block: Block, + state_root_check: bool, + signature_check: bool, + select: frame_try_runtime::TryStateSelect, + ) -> Weight { + // NOTE: intentional unwrap: we don't want to propagate the error backwards, and want to + // have a backtrace here. + Executive::try_execute_block(block, state_root_check, signature_check, select).unwrap() + } + } + + #[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; + use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; + + let mut list = Vec::::new(); + list_benchmarks!(list, extra); + + let storage_info = AllPalletsWithSystem::storage_info(); + (list, storage_info) + } + + fn dispatch_benchmark( + config: frame_benchmarking::BenchmarkConfig + ) -> Result, sp_runtime::RuntimeString> { + use frame_benchmarking::{Benchmarking, BenchmarkBatch, BenchmarkError}; + use sp_storage::TrackedStorageKey; + use codec::Encode; + + use frame_system_benchmarking::Pallet as SystemBench; + impl frame_system_benchmarking::Config for Runtime { + fn setup_set_code_requirements(code: &alloc::vec::Vec) -> Result<(), BenchmarkError> { + ParachainSystem::initialize_for_set_code_benchmark(code.len() as u32); + Ok(()) + } + + fn verify_set_code() { + System::assert_last_event(cumulus_pallet_parachain_system::Event::::ValidationFunctionStored.into()); + } + } + + use cumulus_pallet_session_benchmarking::Pallet as SessionBench; + impl cumulus_pallet_session_benchmarking::Config for Runtime { + fn generate_session_keys_and_proof(owner: Self::AccountId) -> (Self::Keys, Vec) { + let keys = SessionKeys::generate(&owner.encode(), None); + (keys.keys, keys.proof.encode()) + } + } + + parameter_types! { + pub ExistentialDepositAsset: Option = Some(( + xcm_config::RelayLocation::get(), + ExistentialDeposit::get() + ).into()); + } + + use xcm::latest::prelude::*; + use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; + impl pallet_xcm::benchmarking::Config for Runtime { + type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper< + xcm_config::XcmConfig, + ExistentialDepositAsset, + xcm_config::PriceForParentDelivery, + >; + + fn reachable_dest() -> Option { + Some(Parent.into()) + } + + fn teleportable_asset_and_dest() -> Option<(Asset, Location)> { + // Relay/native token can be teleported between Contracts-System-Para and Relay. + Some(( + Asset { + fun: Fungible(ExistentialDeposit::get()), + id: AssetId(Parent.into()) + }, + Parent.into(), + )) + } + + fn reserve_transferable_asset_and_dest() -> Option<(Asset, Location)> { + // Reserve transfers are disabled on Contracts-System-Para. + None + } + + fn set_up_complex_asset_transfer( + ) -> Option<(Assets, u32, Location, alloc::boxed::Box)> { + // Contracts-System-Para only supports teleports to system parachain. + // Relay/native token can be teleported between Contracts-System-Para and Relay. + let native_location = Parent.into(); + let dest = Parent.into(); + pallet_xcm::benchmarking::helpers::native_teleport_as_asset_transfer::( + native_location, + dest + ) + } + + fn get_asset() -> Asset { + Asset { + id: AssetId(Location::parent()), + fun: Fungible(EXISTENTIAL_DEPOSIT), + } + } + } + + 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) + } + } + + impl sp_genesis_builder::GenesisBuilder for Runtime { + fn build_state(config: Vec) -> sp_genesis_builder::Result { + build_state::(config) + } + + fn get_preset(id: &Option) -> Option> { + get_preset::(id, |_| None) + } + + fn preset_names() -> Vec { + vec![] + } + } +} + +cumulus_pallet_parachain_system::register_validate_block! { + Runtime = Runtime, + BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, +} diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs index 69ce1e0d5aeda..3bfa1940f5126 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs @@ -73,6 +73,7 @@ use sp_runtime::{ transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, DispatchError, MultiAddress, Perbill, RuntimeDebug, }; +use sp_session::OpaqueGeneratedSessionKeys; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; @@ -787,8 +788,8 @@ impl_runtime_apis! { } impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) + fn generate_session_keys(owner: Vec, seed: Option>) -> OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() } fn decode_session_keys( @@ -961,6 +962,7 @@ impl_runtime_apis! { ) -> Result, alloc::string::String> { use frame_benchmarking::{BenchmarkBatch, BenchmarkError}; use sp_storage::TrackedStorageKey; + use codec::Encode; use frame_system_benchmarking::Pallet as SystemBench; impl frame_system_benchmarking::Config for Runtime { @@ -975,7 +977,12 @@ impl_runtime_apis! { } use cumulus_pallet_session_benchmarking::Pallet as SessionBench; - impl cumulus_pallet_session_benchmarking::Config for Runtime {} + impl cumulus_pallet_session_benchmarking::Config for Runtime { + fn generate_session_keys_and_proof(owner: Self::AccountId) -> (Self::Keys, Vec) { + let keys = SessionKeys::generate(&owner.encode(), None); + (keys.keys, keys.proof.encode()) + } + } use xcm::latest::prelude::*; use xcm_config::RocRelayLocation; diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs index 15adbad1cd740..7597b59936eab 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs @@ -73,6 +73,7 @@ use sp_runtime::{ transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, DispatchError, MultiAddress, Perbill, RuntimeDebug, }; +use sp_session::OpaqueGeneratedSessionKeys; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; @@ -787,8 +788,8 @@ impl_runtime_apis! { } impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) + fn generate_session_keys(owner: Vec, seed: Option>) -> OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() } fn decode_session_keys( @@ -985,6 +986,7 @@ impl_runtime_apis! { ) -> Result, alloc::string::String> { use frame_benchmarking::{BenchmarkBatch, BenchmarkError}; use sp_storage::TrackedStorageKey; + use codec::Encode; use frame_system_benchmarking::Pallet as SystemBench; impl frame_system_benchmarking::Config for Runtime { @@ -999,7 +1001,12 @@ impl_runtime_apis! { } use cumulus_pallet_session_benchmarking::Pallet as SessionBench; - impl cumulus_pallet_session_benchmarking::Config for Runtime {} + impl cumulus_pallet_session_benchmarking::Config for Runtime { + fn generate_session_keys_and_proof(owner: Self::AccountId) -> (Self::Keys, Vec) { + let keys = SessionKeys::generate(&owner.encode(), None); + (keys.keys, keys.proof.encode()) + } + } use xcm::latest::prelude::*; use xcm_config::TokenRelayLocation; diff --git a/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs b/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs index 3bfd427febb57..f49001bfc1b76 100644 --- a/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/glutton/glutton-westend/src/lib.rs @@ -61,6 +61,7 @@ use sp_runtime::{ transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, }; +use sp_session::OpaqueGeneratedSessionKeys; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; @@ -419,8 +420,8 @@ impl_runtime_apis! { } impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) + fn generate_session_keys(owner: Vec, seed: Option>) -> OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() } fn decode_session_keys( diff --git a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs index 259706b52cf2f..dad02abfc6ed3 100644 --- a/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-rococo/src/lib.rs @@ -65,6 +65,7 @@ use sp_runtime::{ ApplyExtrinsicResult, }; pub use sp_runtime::{MultiAddress, Perbill, Permill, RuntimeDebug}; +use sp_session::OpaqueGeneratedSessionKeys; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; @@ -741,8 +742,8 @@ impl_runtime_apis! { } impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) + fn generate_session_keys(owner: Vec, seed: Option>) -> OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() } fn decode_session_keys( @@ -909,6 +910,7 @@ impl_runtime_apis! { ) -> Result, alloc::string::String> { use frame_benchmarking::{BenchmarkBatch, BenchmarkError}; use sp_storage::TrackedStorageKey; + use codec::Encode; use frame_system_benchmarking::Pallet as SystemBench; impl frame_system_benchmarking::Config for Runtime { @@ -923,7 +925,12 @@ impl_runtime_apis! { } use cumulus_pallet_session_benchmarking::Pallet as SessionBench; - impl cumulus_pallet_session_benchmarking::Config for Runtime {} + impl cumulus_pallet_session_benchmarking::Config for Runtime { + fn generate_session_keys_and_proof(owner: Self::AccountId) -> (Self::Keys, Vec) { + let keys = SessionKeys::generate(&owner.encode(), None); + (keys.keys, keys.proof.encode()) + } + } use testnet_parachains_constants::rococo::locations::{AssetHubParaId, AssetHubLocation}; use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark; diff --git a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs index b0430eff34259..f55bce2c09b3a 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs @@ -69,6 +69,8 @@ use sp_statement_store::{ runtime_api::{InvalidStatement, StatementSource, ValidStatement}, SignatureVerificationResult, Statement, }; +use sp_session::OpaqueGeneratedSessionKeys; + #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; @@ -743,8 +745,8 @@ impl_runtime_apis! { } impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) + fn generate_session_keys(owner: Vec, seed: Option>) -> OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() } fn decode_session_keys( @@ -935,6 +937,7 @@ impl_runtime_apis! { ) -> Result, alloc::string::String> { use frame_benchmarking::{BenchmarkBatch, BenchmarkError}; use sp_storage::TrackedStorageKey; + use codec::Encode; use frame_system_benchmarking::Pallet as SystemBench; impl frame_system_benchmarking::Config for Runtime { @@ -949,7 +952,12 @@ impl_runtime_apis! { } use cumulus_pallet_session_benchmarking::Pallet as SessionBench; - impl cumulus_pallet_session_benchmarking::Config for Runtime {} + impl cumulus_pallet_session_benchmarking::Config for Runtime { + fn generate_session_keys_and_proof(owner: Self::AccountId) -> (Self::Keys, Vec) { + let keys = SessionKeys::generate(&owner.encode(), None); + (keys.keys, keys.proof.encode()) + } + } use xcm_config::RelayLocation; use testnet_parachains_constants::westend::locations::{AssetHubParaId, AssetHubLocation}; diff --git a/cumulus/parachains/runtimes/starters/seedling/src/lib.rs b/cumulus/parachains/runtimes/starters/seedling/src/lib.rs new file mode 100644 index 0000000000000..9f7a655b6d071 --- /dev/null +++ b/cumulus/parachains/runtimes/starters/seedling/src/lib.rs @@ -0,0 +1,395 @@ +// Copyright (C) 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 . + +//! # Seedling Runtime +//! +//! Seedling is a parachain meant to help parachain auction winners migrate a blockchain from +//! another consensus system into the consensus system of a given Relay Chain. + +#![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")); + +extern crate alloc; + +use alloc::{vec, vec::Vec}; +use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; +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}, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, +}; +#[cfg(feature = "std")] +use sp_version::NativeVersion; +use sp_version::RuntimeVersion; + +// A few exports that help ease life for downstream crates. +pub use frame_support::{ + construct_runtime, derive_impl, + dispatch::DispatchClass, + genesis_builder_helper::{build_state, get_preset}, + parameter_types, + traits::{ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, IsInVec, Randomness}, + weights::{ + constants::{ + BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND, + }, + IdentityFee, Weight, + }, + StorageValue, +}; +use frame_system::limits::{BlockLength, BlockWeights}; +use parachains_common::{AccountId, Signature}; +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; +pub use sp_runtime::{Perbill, Permill}; + +impl_opaque_keys! { + pub struct SessionKeys { + pub aura: Aura, + } +} + +/// This runtime version. +#[sp_version::runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("seedling"), + impl_name: create_runtime_str!("seedling"), + authoring_version: 1, + spec_version: 1, + impl_version: 0, + apis: RUNTIME_API_VERSIONS, + transaction_version: 2, + state_version: 0, +}; + +/// 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() } +} + +/// Maximum number of blocks simultaneously accepted by the Runtime, not yet included +/// into the relay chain. +const UNINCLUDED_SEGMENT_CAPACITY: u32 = 1; +/// How many parachain blocks are processed by the relay chain per parent. Limits the +/// number of blocks authored per slot. +const BLOCK_PROCESSING_VELOCITY: u32 = 1; +/// Relay chain slot duration, in milliseconds. +const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000; + +/// We assume that ~10% 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(10); +/// 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 .5 seconds of compute with a 12 second average block time. +const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( + WEIGHT_REF_TIME_PER_SECOND.saturating_div(2), + cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64, +); + +parameter_types! { + pub const BlockHashCount: BlockNumber = 250; + pub const Version: RuntimeVersion = VERSION; + 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: u8 = 42; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +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 RuntimeCall = RuntimeCall; + /// 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 Nonce = Nonce; + /// The type for hashing blocks and tries. + type Hash = Hash; + /// The hashing algorithm used. + type Hashing = BlakeTwo256; + /// The block type. + type Block = Block; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + /// The ubiquitous origin type. + type RuntimeOrigin = RuntimeOrigin; + /// 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; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = frame_support::traits::Everything; + type SystemWeightInfo = (); + type BlockWeights = RuntimeBlockWeights; + type BlockLength = RuntimeBlockLength; + type SS58Prefix = SS58Prefix; + type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl pallet_sudo::Config for Runtime { + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = pallet_sudo::weights::SubstrateWeight; +} + +impl cumulus_pallet_solo_to_para::Config for Runtime { + type RuntimeEvent = RuntimeEvent; +} + +impl cumulus_pallet_parachain_system::Config for Runtime { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type OnSystemEvent = cumulus_pallet_solo_to_para::Pallet; + type SelfParaId = parachain_info::Pallet; + type OutboundXcmpMessageSource = (); + // Ignore all DMP messages by enqueueing them into `()`: + type DmpQueue = frame_support::traits::EnqueueWithOrigin<(), sp_core::ConstU8<0>>; + type ReservedDmpWeight = (); + type XcmpMessageHandler = (); + type ReservedXcmpWeight = (); + type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; + type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< + Runtime, + RELAY_CHAIN_SLOT_DURATION_MILLIS, + BLOCK_PROCESSING_VELOCITY, + UNINCLUDED_SEGMENT_CAPACITY, + >; +} + +impl parachain_info::Config for Runtime {} + +impl cumulus_pallet_aura_ext::Config for Runtime {} + +impl pallet_aura::Config for Runtime { + type AuthorityId = AuraId; + type DisabledValidators = (); + type MaxAuthorities = ConstU32<100_000>; + type AllowMultipleBlocksPerSlot = ConstBool; + type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; +} + +impl pallet_timestamp::Config for Runtime { + type Moment = u64; + type OnTimestampSet = Aura; + type MinimumPeriod = ConstU64<0>; + type WeightInfo = (); +} + +construct_runtime! { + pub enum Runtime + { + System: frame_system, + Sudo: pallet_sudo, + Timestamp: pallet_timestamp, + + ParachainSystem: cumulus_pallet_parachain_system, + ParachainInfo: parachain_info, + SoloToPara: cumulus_pallet_solo_to_para, + Aura: pallet_aura, + AuraExt: cumulus_pallet_aura_ext, + } +} + +/// Index of a transaction in the chain. +pub type Nonce = 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 = sp_runtime::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; +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = ( + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + pallet_sudo::CheckOnlySudoAccount, +); +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic; + +/// Executive: handles dispatch to the various modules. +pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, +>; + +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 { + pallet_aura::Authorities::::get().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) -> sp_runtime::ExtrinsicInclusionMode { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + + fn metadata_at_version(version: u32) -> Option { + Runtime::metadata_at_version(version) + } + + fn metadata_versions() -> alloc::vec::Vec { + Runtime::metadata_versions() + } + } + + 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(_: Vec, _: Option>) -> sp_session::OpaqueGeneratedSessionKeys { + sp_session::OpaqueGeneratedSessionKeys { + keys: Vec::new(), + proof: Vec::new(), + } + } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + + impl cumulus_primitives_core::CollectCollationInfo for Runtime { + fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { + ParachainSystem::collect_collation_info(header) + } + } + + impl sp_genesis_builder::GenesisBuilder for Runtime { + fn build_state(config: Vec) -> sp_genesis_builder::Result { + build_state::(config) + } + + fn get_preset(id: &Option) -> Option> { + get_preset::(id, |_| None) + } + + fn preset_names() -> Vec { + vec![] + } + } +} + +cumulus_pallet_parachain_system::register_validate_block! { + Runtime = Runtime, + BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, +} diff --git a/cumulus/parachains/runtimes/starters/shell/src/lib.rs b/cumulus/parachains/runtimes/starters/shell/src/lib.rs new file mode 100644 index 0000000000000..1c7992a544bd7 --- /dev/null +++ b/cumulus/parachains/runtimes/starters/shell/src/lib.rs @@ -0,0 +1,450 @@ +// Copyright (C) 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 . + +//! # Shell Runtime +//! +//! The Shell runtime defines a minimal parachain. It can listen for a downward message authorizing +//! an upgrade into another parachain. +//! +//! Generally (so far) only used as the first parachain on a Relay. + +#![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")); + +pub mod xcm_config; + +extern crate alloc; + +use alloc::{vec, vec::Vec}; +use codec::{Decode, Encode}; +use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; +use cumulus_primitives_core::AggregateMessageOrigin; +use frame_support::unsigned::TransactionValidityError; +use scale_info::TypeInfo; +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, DispatchInfoOf}, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, +}; +#[cfg(feature = "std")] +use sp_version::NativeVersion; +use sp_version::RuntimeVersion; + +// A few exports that help ease life for downstream crates. +pub use frame_support::{ + construct_runtime, derive_impl, + dispatch::DispatchClass, + genesis_builder_helper::{build_state, get_preset}, + parameter_types, + traits::{ConstBool, ConstU32, ConstU64, ConstU8, EitherOfDiverse, IsInVec, Randomness}, + weights::{ + constants::{ + BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND, + }, + IdentityFee, Weight, + }, + StorageValue, +}; +use frame_system::limits::{BlockLength, BlockWeights}; +use parachains_common::{AccountId, Signature}; +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; +pub use sp_runtime::{Perbill, Permill}; + +impl_opaque_keys! { + pub struct SessionKeys { + pub aura: Aura, + } +} + +/// This runtime version. +#[sp_version::runtime_version] +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("shell"), + impl_name: create_runtime_str!("shell"), + authoring_version: 1, + spec_version: 2, + impl_version: 0, + apis: RUNTIME_API_VERSIONS, + transaction_version: 1, + state_version: 0, +}; + +/// 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() } +} + +/// Maximum number of blocks simultaneously accepted by the Runtime, not yet included +/// into the relay chain. +const UNINCLUDED_SEGMENT_CAPACITY: u32 = 1; +/// How many parachain blocks are processed by the relay chain per parent. Limits the +/// number of blocks authored per slot. +const BLOCK_PROCESSING_VELOCITY: u32 = 1; +/// Relay chain slot duration, in milliseconds. +const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000; + +/// We assume that ~10% 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(10); +/// 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 .5 seconds of compute with a 12 second average block time. +const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts( + WEIGHT_REF_TIME_PER_SECOND.saturating_div(2), + cumulus_primitives_core::relay_chain::MAX_POV_SIZE as u64, +); + +parameter_types! { + pub const BlockHashCount: BlockNumber = 250; + pub const Version: RuntimeVersion = VERSION; + 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: u8 = 42; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +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 RuntimeCall = RuntimeCall; + /// 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 Nonce = Nonce; + /// The type for hashing blocks and tries. + type Hash = Hash; + /// The hashing algorithm used. + type Hashing = BlakeTwo256; + /// The block type. + type Block = Block; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + /// The ubiquitous origin type. + type RuntimeOrigin = RuntimeOrigin; + /// 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; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = frame_support::traits::Everything; + type SystemWeightInfo = (); + type BlockWeights = RuntimeBlockWeights; + type BlockLength = RuntimeBlockLength; + type SS58Prefix = SS58Prefix; + type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode; + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent; + pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); +} + +impl cumulus_pallet_parachain_system::Config for Runtime { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type OnSystemEvent = (); + type SelfParaId = parachain_info::Pallet; + type OutboundXcmpMessageSource = (); + type DmpQueue = frame_support::traits::EnqueueWithOrigin; + type ReservedDmpWeight = ReservedDmpWeight; + type XcmpMessageHandler = (); + type ReservedXcmpWeight = (); + type CheckAssociatedRelayNumber = RelayNumberMonotonicallyIncreases; + type ConsensusHook = cumulus_pallet_aura_ext::FixedVelocityConsensusHook< + Runtime, + RELAY_CHAIN_SLOT_DURATION_MILLIS, + BLOCK_PROCESSING_VELOCITY, + UNINCLUDED_SEGMENT_CAPACITY, + >; +} + +impl parachain_info::Config for Runtime {} + +parameter_types! { + pub MessageQueueServiceWeight: Weight = Perbill::from_percent(35) * RuntimeBlockWeights::get().max_block; +} + +impl pallet_message_queue::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor< + cumulus_primitives_core::AggregateMessageOrigin, + >; + #[cfg(not(feature = "runtime-benchmarks"))] + type MessageProcessor = xcm_builder::ProcessXcmMessage< + AggregateMessageOrigin, + xcm_executor::XcmExecutor, + RuntimeCall, + >; + type Size = u32; + // These need to be configured to the XCMP pallet - if it is deployed. + type QueueChangeHandler = (); + type QueuePausedQuery = (); + type HeapSize = sp_core::ConstU32<{ 103 * 1024 }>; + type MaxStale = sp_core::ConstU32<8>; + type ServiceWeight = MessageQueueServiceWeight; + type IdleMaxServiceWeight = MessageQueueServiceWeight; +} + +impl cumulus_pallet_aura_ext::Config for Runtime {} + +impl pallet_aura::Config for Runtime { + type AuthorityId = AuraId; + type DisabledValidators = (); + type MaxAuthorities = ConstU32<100_000>; + type AllowMultipleBlocksPerSlot = ConstBool; + type SlotDuration = pallet_aura::MinimumPeriodTimesTwo; +} + +impl pallet_timestamp::Config for Runtime { + type Moment = u64; + type OnTimestampSet = Aura; + type MinimumPeriod = ConstU64<0>; + type WeightInfo = (); +} + +construct_runtime! { + pub enum Runtime + { + System: frame_system, + Timestamp: pallet_timestamp, + + ParachainSystem: cumulus_pallet_parachain_system, + ParachainInfo: parachain_info, + + CumulusXcm: cumulus_pallet_xcm, + MessageQueue: pallet_message_queue, + + Aura: pallet_aura, + AuraExt: cumulus_pallet_aura_ext, + } +} + +/// Simple implementation which fails any transaction which is signed. +#[derive(Eq, PartialEq, Clone, Default, sp_core::RuntimeDebug, Encode, Decode, TypeInfo)] +pub struct DisallowSigned; +impl sp_runtime::traits::SignedExtension for DisallowSigned { + const IDENTIFIER: &'static str = "DisallowSigned"; + type AccountId = AccountId; + type Call = RuntimeCall; + type AdditionalSigned = (); + type Pre = (); + fn additional_signed( + &self, + ) -> core::result::Result<(), sp_runtime::transaction_validity::TransactionValidityError> { + Ok(()) + } + fn pre_dispatch( + self, + who: &Self::AccountId, + call: &Self::Call, + info: &DispatchInfoOf, + len: usize, + ) -> Result { + self.validate(who, call, info, len).map(|_| ()) + } + fn validate( + &self, + _who: &Self::AccountId, + _call: &Self::Call, + _info: &sp_runtime::traits::DispatchInfoOf, + _len: usize, + ) -> TransactionValidity { + let i = sp_runtime::transaction_validity::InvalidTransaction::BadProof; + Err(sp_runtime::transaction_validity::TransactionValidityError::Invalid(i)) + } +} + +/// Index of a transaction in the chain. +pub type Nonce = 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 = sp_runtime::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; +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = DisallowSigned; +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic; +/// Executive: handles dispatch to the various modules. +pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, +>; + +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 { + pallet_aura::Authorities::::get().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) -> sp_runtime::ExtrinsicInclusionMode { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + + fn metadata_at_version(version: u32) -> Option { + Runtime::metadata_at_version(version) + } + + fn metadata_versions() -> alloc::vec::Vec { + Runtime::metadata_versions() + } + } + + 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(_: Vec, _: Option>) -> sp_session::OpaqueGeneratedSessionKeys { + sp_session::OpaqueGeneratedSessionKeys { keys: Vec::new(), proof: Vec::new() } + } + + fn decode_session_keys( + encoded: Vec, + ) -> Option, KeyTypeId)>> { + SessionKeys::decode_into_raw_public_keys(&encoded) + } + } + + impl cumulus_primitives_core::CollectCollationInfo for Runtime { + fn collect_collation_info(header: &::Header) -> cumulus_primitives_core::CollationInfo { + ParachainSystem::collect_collation_info(header) + } + } + + impl sp_genesis_builder::GenesisBuilder for Runtime { + fn build_state(config: Vec) -> sp_genesis_builder::Result { + build_state::(config) + } + + fn get_preset(id: &Option) -> Option> { + get_preset::(id, |_| None) + } + + fn preset_names() -> Vec { + vec![] + } + } +} + +cumulus_pallet_parachain_system::register_validate_block! { + Runtime = Runtime, + BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, +} diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index ff2f538bb3619..8971a89e79b14 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -1013,8 +1013,8 @@ impl_runtime_apis! { } impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) + fn generate_session_keys(owner: Vec, seed: Option>) -> sp_session::OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() } fn decode_session_keys( @@ -1199,13 +1199,19 @@ impl_runtime_apis! { ) -> Result, alloc::string::String> { use frame_benchmarking::BenchmarkBatch; use sp_storage::TrackedStorageKey; + use codec::Encode; use frame_system_benchmarking::Pallet as SystemBench; use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; impl frame_system_benchmarking::Config for Runtime {} use cumulus_pallet_session_benchmarking::Pallet as SessionBench; - impl cumulus_pallet_session_benchmarking::Config for Runtime {} + impl cumulus_pallet_session_benchmarking::Config for Runtime { + fn generate_session_keys_and_proof(owner: Self::AccountId) -> (Self::Keys, Vec) { + let keys = SessionKeys::generate(&owner.encode(), None); + (keys.keys, keys.proof.encode()) + } + } use frame_support::traits::WhitelistedStorageKeys; let whitelist: Vec = AllPalletsWithSystem::whitelisted_storage_keys(); diff --git a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs index 0c2a379d35e4c..8d93b8ac3b4cd 100644 --- a/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/rococo-parachain/src/lib.rs @@ -781,8 +781,8 @@ impl_runtime_apis! { SessionKeys::decode_into_raw_public_keys(&encoded) } - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) + fn generate_session_keys(owner: Vec, seed: Option>) -> sp_session::OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() } } diff --git a/cumulus/parachains/runtimes/testing/yet-another-parachain/src/lib.rs b/cumulus/parachains/runtimes/testing/yet-another-parachain/src/lib.rs index 2eace1d860a03..1c23254168bab 100644 --- a/cumulus/parachains/runtimes/testing/yet-another-parachain/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/yet-another-parachain/src/lib.rs @@ -575,8 +575,8 @@ impl_runtime_apis! { SessionKeys::decode_into_raw_public_keys(&encoded) } - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) + fn generate_session_keys(owner: Vec, seed: Option>) -> sp_session::OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() } } diff --git a/cumulus/polkadot-omni-node/lib/src/fake_runtime_api/utils.rs b/cumulus/polkadot-omni-node/lib/src/fake_runtime_api/utils.rs index 1354dbbfb00a0..29a15b81d9a81 100644 --- a/cumulus/polkadot-omni-node/lib/src/fake_runtime_api/utils.rs +++ b/cumulus/polkadot-omni-node/lib/src/fake_runtime_api/utils.rs @@ -124,7 +124,7 @@ macro_rules! impl_node_runtime_apis { } impl sp_session::SessionKeys<$block> for $runtime { - fn generate_session_keys(_: Option>) -> Vec { + fn generate_session_keys(_: Vec, _: Option>) -> sp_session::OpaqueGeneratedSessionKeys { unimplemented!() } diff --git a/cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs b/cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs new file mode 100644 index 0000000000000..aa4a2a2500804 --- /dev/null +++ b/cumulus/polkadot-parachain/src/fake_runtime_api/asset_hub_polkadot_aura.rs @@ -0,0 +1,199 @@ +// Copyright (C) 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 . + +//! These are used to provide a type that implements these runtime APIs without requiring to import +//! the native runtimes. + +use frame_support::weights::Weight; +use parachains_common::{AccountId, AssetHubPolkadotAuraId, Balance, Nonce}; +use polkadot_primitives::Block; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_runtime::{ + traits::Block as BlockT, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, +}; +use sp_session::OpaqueGeneratedSessionKeys; + +pub struct Runtime; + +sp_api::impl_runtime_apis! { + impl sp_api::Core for Runtime { + fn version() -> sp_version::RuntimeVersion { + unimplemented!() + } + + fn execute_block(_: Block) { + unimplemented!() + } + + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { + unimplemented!() + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + unimplemented!() + } + + fn metadata_at_version(_: u32) -> Option { + unimplemented!() + } + + fn metadata_versions() -> Vec { + unimplemented!() + } + } + + impl sp_consensus_aura::AuraApi for Runtime { + fn slot_duration() -> sp_consensus_aura::SlotDuration { + unimplemented!() + } + + fn authorities() -> Vec { + unimplemented!() + } + } + + impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { + fn can_build_upon( + _: ::Hash, + _: cumulus_primitives_aura::Slot, + ) -> bool { + unimplemented!() + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(_: ::Extrinsic) -> ApplyExtrinsicResult { + unimplemented!() + } + + fn finalize_block() -> ::Header { + unimplemented!() + } + + fn inherent_extrinsics(_: sp_inherents::InherentData) -> Vec<::Extrinsic> { + unimplemented!() + } + + fn check_inherents(_: Block, _: sp_inherents::InherentData) -> sp_inherents::CheckInherentsResult { + unimplemented!() + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + _: TransactionSource, + _: ::Extrinsic, + _: ::Hash, + ) -> TransactionValidity { + unimplemented!() + } + } + + impl sp_session::SessionKeys for Runtime { + fn generate_session_keys(_: Vec, _: Option>) -> OpaqueGeneratedSessionKeys { + unimplemented!() + } + + fn decode_session_keys( + _: Vec, + ) -> Option, KeyTypeId)>> { + unimplemented!() + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { + fn query_info( + _: ::Extrinsic, + _: u32, + ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { + unimplemented!() + } + fn query_fee_details( + _: ::Extrinsic, + _: u32, + ) -> pallet_transaction_payment::FeeDetails { + unimplemented!() + } + fn query_weight_to_fee(_: Weight) -> Balance { + unimplemented!() + } + fn query_length_to_fee(_: u32) -> Balance { + unimplemented!() + } + } + + impl cumulus_primitives_core::CollectCollationInfo for Runtime { + fn collect_collation_info(_: &::Header) -> cumulus_primitives_core::CollationInfo { + unimplemented!() + } + } + + #[cfg(feature = "try-runtime")] + impl frame_try_runtime::TryRuntime for Runtime { + fn on_runtime_upgrade(_: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { + unimplemented!() + } + + fn execute_block( + _: Block, + _: bool, + _: bool, + _: frame_try_runtime::TryStateSelect, + ) -> Weight { + unimplemented!() + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(_: AccountId) -> Nonce { + unimplemented!() + } + } + + #[cfg(feature = "runtime-benchmarks")] + impl frame_benchmarking::Benchmark for Runtime { + fn benchmark_metadata(_: bool) -> ( + Vec, + Vec, + ) { + unimplemented!() + } + + fn dispatch_benchmark( + _: frame_benchmarking::BenchmarkConfig + ) -> Result, sp_runtime::RuntimeString> { + unimplemented!() + } + } + + impl sp_genesis_builder::GenesisBuilder for Runtime { + fn build_state(_: Vec) -> sp_genesis_builder::Result { + unimplemented!() + } + + fn get_preset(_id: &Option) -> Option> { + unimplemented!() + } + + fn preset_names() -> Vec { + unimplemented!() + } + } +} diff --git a/cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs b/cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs new file mode 100644 index 0000000000000..1b852e9227dbd --- /dev/null +++ b/cumulus/polkadot-parachain/src/fake_runtime_api/aura.rs @@ -0,0 +1,199 @@ +// Copyright (C) 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 . + +//! These are used to provide a type that implements these runtime APIs without requiring to import +//! the native runtimes. + +use frame_support::weights::Weight; +use parachains_common::{AccountId, AuraId, Balance, Nonce}; +use polkadot_primitives::Block; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_runtime::{ + traits::Block as BlockT, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, +}; +use sp_session::OpaqueGeneratedSessionKeys; + +pub struct Runtime; + +sp_api::impl_runtime_apis! { + impl sp_api::Core for Runtime { + fn version() -> sp_version::RuntimeVersion { + unimplemented!() + } + + fn execute_block(_: Block) { + unimplemented!() + } + + fn initialize_block(_: &::Header) -> sp_runtime::ExtrinsicInclusionMode { + unimplemented!() + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + unimplemented!() + } + + fn metadata_at_version(_: u32) -> Option { + unimplemented!() + } + + fn metadata_versions() -> Vec { + unimplemented!() + } + } + + impl sp_consensus_aura::AuraApi for Runtime { + fn slot_duration() -> sp_consensus_aura::SlotDuration { + unimplemented!() + } + + fn authorities() -> Vec { + unimplemented!() + } + } + + impl cumulus_primitives_aura::AuraUnincludedSegmentApi for Runtime { + fn can_build_upon( + _: ::Hash, + _: cumulus_primitives_aura::Slot, + ) -> bool { + unimplemented!() + } + } + + impl sp_block_builder::BlockBuilder for Runtime { + fn apply_extrinsic(_: ::Extrinsic) -> ApplyExtrinsicResult { + unimplemented!() + } + + fn finalize_block() -> ::Header { + unimplemented!() + } + + fn inherent_extrinsics(_: sp_inherents::InherentData) -> Vec<::Extrinsic> { + unimplemented!() + } + + fn check_inherents(_: Block, _: sp_inherents::InherentData) -> sp_inherents::CheckInherentsResult { + unimplemented!() + } + } + + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { + fn validate_transaction( + _: TransactionSource, + _: ::Extrinsic, + _: ::Hash, + ) -> TransactionValidity { + unimplemented!() + } + } + + impl sp_session::SessionKeys for Runtime { + fn generate_session_keys(_: Vec, _: Option>) -> OpaqueGeneratedSessionKeys { + unimplemented!() + } + + fn decode_session_keys( + _: Vec, + ) -> Option, KeyTypeId)>> { + unimplemented!() + } + } + + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi for Runtime { + fn query_info( + _: ::Extrinsic, + _: u32, + ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo { + unimplemented!() + } + fn query_fee_details( + _: ::Extrinsic, + _: u32, + ) -> pallet_transaction_payment::FeeDetails { + unimplemented!() + } + fn query_weight_to_fee(_: Weight) -> Balance { + unimplemented!() + } + fn query_length_to_fee(_: u32) -> Balance { + unimplemented!() + } + } + + impl cumulus_primitives_core::CollectCollationInfo for Runtime { + fn collect_collation_info(_: &::Header) -> cumulus_primitives_core::CollationInfo { + unimplemented!() + } + } + + #[cfg(feature = "try-runtime")] + impl frame_try_runtime::TryRuntime for Runtime { + fn on_runtime_upgrade(_: frame_try_runtime::UpgradeCheckSelect) -> (Weight, Weight) { + unimplemented!() + } + + fn execute_block( + _: Block, + _: bool, + _: bool, + _: frame_try_runtime::TryStateSelect, + ) -> Weight { + unimplemented!() + } + } + + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { + fn account_nonce(_: AccountId) -> Nonce { + unimplemented!() + } + } + + #[cfg(feature = "runtime-benchmarks")] + impl frame_benchmarking::Benchmark for Runtime { + fn benchmark_metadata(_: bool) -> ( + Vec, + Vec, + ) { + unimplemented!() + } + + fn dispatch_benchmark( + _: frame_benchmarking::BenchmarkConfig + ) -> Result, sp_runtime::RuntimeString> { + unimplemented!() + } + } + + impl sp_genesis_builder::GenesisBuilder for Runtime { + fn build_state(_: Vec) -> sp_genesis_builder::Result { + unimplemented!() + } + + fn get_preset(_id: &Option) -> Option> { + unimplemented!() + } + + fn preset_names() -> Vec { + unimplemented!() + } + } +} diff --git a/cumulus/test/runtime/src/lib.rs b/cumulus/test/runtime/src/lib.rs index f00665def12bf..cd4c7a1dbd340 100644 --- a/cumulus/test/runtime/src/lib.rs +++ b/cumulus/test/runtime/src/lib.rs @@ -634,8 +634,8 @@ impl_runtime_apis! { SessionKeys::decode_into_raw_public_keys(&encoded) } - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) + fn generate_session_keys(owner: Vec, seed: Option>) -> sp_session::OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() } } diff --git a/polkadot/node/service/src/fake_runtime_api.rs b/polkadot/node/service/src/fake_runtime_api.rs index 4e31c72d334f7..56edb1df938b5 100644 --- a/polkadot/node/service/src/fake_runtime_api.rs +++ b/polkadot/node/service/src/fake_runtime_api.rs @@ -389,7 +389,7 @@ sp_api::impl_runtime_apis! { } impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(_: Option>) -> Vec { + fn generate_session_keys(_: Vec, _: Option>) -> sp_session::OpaqueGeneratedSessionKeys { unimplemented!() } diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index 7a7c046555d65..6c9b97f05a967 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -2411,8 +2411,11 @@ sp_api::impl_runtime_apis! { } impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) + fn generate_session_keys( + owner: Vec, + seed: Option>, + ) -> sp_session::OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() } fn decode_session_keys( diff --git a/polkadot/runtime/test-runtime/src/lib.rs b/polkadot/runtime/test-runtime/src/lib.rs index 2223ca42d8bf3..dc4fd9756f8ed 100644 --- a/polkadot/runtime/test-runtime/src/lib.rs +++ b/polkadot/runtime/test-runtime/src/lib.rs @@ -1301,8 +1301,8 @@ sp_api::impl_runtime_apis! { } impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) + fn generate_session_keys(owner: Vec, seed: Option>) -> sp_session::OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() } fn decode_session_keys( diff --git a/polkadot/runtime/westend/src/lib.rs b/polkadot/runtime/westend/src/lib.rs index c3107ab3db8a3..6bba8e6d17644 100644 --- a/polkadot/runtime/westend/src/lib.rs +++ b/polkadot/runtime/westend/src/lib.rs @@ -2643,8 +2643,8 @@ sp_api::impl_runtime_apis! { } impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) + fn generate_session_keys(owner: Vec, seed: Option>) -> sp_session::OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() } fn decode_session_keys( @@ -2867,7 +2867,13 @@ sp_api::impl_runtime_apis! { use frame_system_benchmarking::extensions::Pallet as SystemExtensionsBench; use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench; - impl pallet_session_benchmarking::Config for Runtime {} + impl pallet_session_benchmarking::Config for Runtime { + fn generate_session_keys_and_proof(owner: Self::AccountId) -> (Self::Keys, Vec) { + let keys = SessionKeys::generate(&owner.encode(), None); + (keys.keys, keys.proof.encode()) + } + } + impl pallet_offences_benchmarking::Config for Runtime {} impl pallet_election_provider_support_benchmarking::Config for Runtime {} diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 4ea04b736c02f..417c789050502 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -3616,8 +3616,8 @@ pallet_revive::impl_runtime_apis_plus_revive!( } impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) + fn generate_session_keys(owner: Vec, seed: Option>) -> sp_session::OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() } fn decode_session_keys( @@ -3701,7 +3701,12 @@ pallet_revive::impl_runtime_apis_plus_revive!( use baseline::Pallet as BaselineBench; use pallet_nomination_pools_benchmarking::Pallet as NominationPoolsBench; - impl pallet_session_benchmarking::Config for Runtime {} + impl pallet_session_benchmarking::Config for Runtime { + fn generate_session_keys_and_proof(owner: Self::AccountId) -> (Self::Keys, Vec) { + let keys = SessionKeys::generate(&owner.encode(), None); + (keys.keys, keys.proof.encode()) + } + } impl pallet_offences_benchmarking::Config for Runtime {} impl pallet_election_provider_support_benchmarking::Config for Runtime {} impl frame_system_benchmarking::Config for Runtime {} diff --git a/substrate/client/rpc-api/src/author/error.rs b/substrate/client/rpc-api/src/author/error.rs index 0ccd82fc0b64c..201b417e4d000 100644 --- a/substrate/client/rpc-api/src/author/error.rs +++ b/substrate/client/rpc-api/src/author/error.rs @@ -48,6 +48,9 @@ pub enum Error { /// Invalid session keys encoding. #[error("Session keys are not encoded correctly")] InvalidSessionKeys, + /// `SessionKeys` runtime api missing. + #[error("`SessionKeys` runtime api not present in the runtime")] + MissingSessionKeysApi, /// Call to an unsafe RPC was denied. #[error(transparent)] UnsafeRpcCalled(#[from] crate::policy::UnsafeRpcError), diff --git a/substrate/client/rpc-api/src/author/mod.rs b/substrate/client/rpc-api/src/author/mod.rs index 7406dd94c72e1..89691198cd7cb 100644 --- a/substrate/client/rpc-api/src/author/mod.rs +++ b/substrate/client/rpc-api/src/author/mod.rs @@ -18,14 +18,28 @@ //! Substrate block-author/full-node API. -pub mod error; -pub mod hash; - use error::Error; use jsonrpsee::proc_macros::rpc; use sc_transaction_pool_api::TransactionStatus; use sp_core::Bytes; +pub mod error; +pub mod hash; + +/// Output of [`AuthorApiServer::rotate_keys_with_owner`]. +#[derive(serde::Serialize, serde::Deserialize, Clone)] +pub struct GeneratedSessionKeys { + /// The public session keys for registering them on chain. + pub keys: Bytes, + + /// The `proof` for verifying ownership of the generated session keys. + /// + /// This will be `None` iff the chain doesn't support generating the `proof`. + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + pub proof: Option, +} + /// Substrate authoring RPC API #[rpc(client, server)] pub trait AuthorApi { @@ -41,6 +55,15 @@ pub trait AuthorApi { #[method(name = "author_rotateKeys", with_extensions)] fn rotate_keys(&self) -> Result; + /// Generate new session keys and returns the corresponding public keys. + /// + /// The `owner` should be something that can be used on chain for verifying the ownership of the + /// generated keys using the returned `proof`. For example `owner` could be set to the account + /// id of the account registering the returned public session keys. The actual data to pass for + /// `owner` depends on the runtime logic verifying the `proof`. + #[method(name = "author_rotateKeysWithOwner")] + fn rotate_keys_with_owner(&self, owner: Bytes) -> Result; + /// Checks if the keystore has private keys for the given session public keys. /// /// `session_keys` is the SCALE encoded session keys object from the runtime. diff --git a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs index 3ec5e805ecd5a..dc3f579cceef5 100644 --- a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs +++ b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs @@ -342,7 +342,7 @@ async fn follow_with_runtime() { \"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",5],\ [\"0x37e397fc7c91f5e4\",2],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",6],\ [\"0xbc9d89904f5b923f\",1],[\"0xc6e9a76309f39b09\",2],[\"0xdd718d5cc53262d4\",1],\ - [\"0xcbca25e39f142387\",2],[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],\ + [\"0xcbca25e39f142387\",2],[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",2],\ [\"0xed99c5acb25eedf5\",3],[\"0xfbc577b9d747efd6\",1]],\"transactionVersion\":1,\"systemVersion\":1}"; let runtime: RuntimeVersion = serde_json::from_str(runtime_str).unwrap(); diff --git a/substrate/client/rpc/src/author/mod.rs b/substrate/client/rpc/src/author/mod.rs index 7ace9e5ebe191..a07f608561b02 100644 --- a/substrate/client/rpc/src/author/mod.rs +++ b/substrate/client/rpc/src/author/mod.rs @@ -68,6 +68,46 @@ impl Author { } } +impl Author +where + P: TransactionPool + Sync + Send + 'static, + Client: HeaderBackend + ProvideRuntimeApi + Send + Sync + 'static, + Client::Api: SessionKeys, + P::Hash: Unpin, + ::Hash: Unpin, +{ + fn rotate_keys_impl(&self, owner: Vec) -> Result { + //Alerady checked in rotate key bofore calling this function? + //self.deny_unsafe.check_if_safe()?; + + let best_block_hash = self.client.info().best_hash; + let mut runtime_api = self.client.runtime_api(); + + runtime_api.register_extension(KeystoreExt::from(self.keystore.clone())); + + let version = runtime_api + .api_version::>(best_block_hash) + .map_err(|api_err| Error::Client(Box::new(api_err)))? + .ok_or_else(|| Error::MissingSessionKeysApi)?; + + if version < 2 { + #[allow(deprecated)] + runtime_api + .generate_session_keys_before_version_2(best_block_hash, None) + .map(|sk| GeneratedSessionKeys { keys: sk.into(), proof: None }) + .map_err(|api_err| Error::Client(Box::new(api_err)).into()) + } else { + runtime_api + .generate_session_keys(best_block_hash, owner, None) + .map(|sk| GeneratedSessionKeys { + keys: sk.keys.into(), + proof: Some(sk.proof.into()), + }) + .map_err(|api_err| Error::Client(Box::new(api_err)).into()) + } + } +} + /// Currently we treat all RPC transactions as externals. /// /// Possibly in the future we could allow opt-in for special treatment @@ -115,17 +155,12 @@ where } fn rotate_keys(&self, ext: &Extensions) -> Result { - check_if_safe(ext)?; - - let best_block_hash = self.client.info().best_hash; - let mut runtime_api = self.client.runtime_api(); - - runtime_api.register_extension(KeystoreExt::from(self.keystore.clone())); + check_if_safe(ext)?; + self.rotate_keys_impl(Vec::new()).map(|k| k.keys) + } - runtime_api - .generate_session_keys(best_block_hash, None) - .map(Into::into) - .map_err(|api_err| Error::Client(Box::new(api_err)).into()) + fn rotate_keys_with_owner(&self, owner: Bytes) -> Result { + self.rotate_keys_impl(owner.0) } fn has_session_keys(&self, ext: &Extensions, session_keys: Bytes) -> Result { diff --git a/substrate/client/rpc/src/state/tests.rs b/substrate/client/rpc/src/state/tests.rs index c02f0d0b759bf..297f95994d1c5 100644 --- a/substrate/client/rpc/src/state/tests.rs +++ b/substrate/client/rpc/src/state/tests.rs @@ -475,7 +475,7 @@ async fn should_return_runtime_version() { \"specVersion\":2,\"implVersion\":2,\"apis\":[[\"0xdf6acb689907609b\",5],\ [\"0x37e397fc7c91f5e4\",2],[\"0xd2bc9897eed08f15\",3],[\"0x40fe3ad401f8959a\",6],\ [\"0xbc9d89904f5b923f\",1],[\"0xc6e9a76309f39b09\",2],[\"0xdd718d5cc53262d4\",1],\ - [\"0xcbca25e39f142387\",2],[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",1],\ + [\"0xcbca25e39f142387\",2],[\"0xf78b278be53f454c\",2],[\"0xab3c0572291feb8b\",2],\ [\"0xed99c5acb25eedf5\",3],[\"0xfbc577b9d747efd6\",1]],\"transactionVersion\":1,\"systemVersion\":1,\ \"stateVersion\":1}"; diff --git a/substrate/frame/session/benchmarking/src/inner.rs b/substrate/frame/session/benchmarking/src/inner.rs index f3707763f3509..2a3f1d5ce6b32 100644 --- a/substrate/frame/session/benchmarking/src/inner.rs +++ b/substrate/frame/session/benchmarking/src/inner.rs @@ -18,7 +18,7 @@ //! Benchmarks for the Session Pallet. // This is separated into its own crate due to cyclic dependency issues. -use alloc::{vec, vec::Vec}; +use alloc::vec::Vec; use sp_runtime::traits::{One, StaticLookup, TrailingZeroInput}; use codec::Decode; @@ -34,9 +34,16 @@ use pallet_staking::{ const MAX_VALIDATORS: u32 = 1000; pub struct Pallet(pallet_session::Pallet); +/// Configuration trait for the benchmarking of `pallet-session`. pub trait Config: pallet_session::Config + pallet_session::historical::Config + pallet_staking::Config { + /// Generate a session key and a proof of ownership. + /// + /// The given `owner` is the account that will call `set_keys` using the returned session keys + /// and proof. This means that the proof should prove the ownership of `owner` over the private + /// keys associated to the session keys. + fn generate_session_keys_and_proof(owner: Self::AccountId) -> (Self::Keys, Vec); } impl OnInitialize> for Pallet { @@ -61,8 +68,7 @@ mod benchmarks { )?; let v_controller = pallet_staking::Pallet::::bonded(&v_stash).ok_or("not stash")?; - let keys = T::Keys::decode(&mut TrailingZeroInput::zeroes()).unwrap(); - let proof: Vec = vec![0, 1, 2, 3]; + let (keys, proof) = T::generate_session_keys_and_proof(v_controller.clone()); // Whitelist controller account from further DB operations. let v_controller_key = frame_system::Account::::hashed_key_for(&v_controller); frame_benchmarking::benchmarking::add_to_whitelist(v_controller_key.into()); @@ -84,8 +90,7 @@ mod benchmarks { RewardDestination::Staked, )?; let v_controller = pallet_staking::Pallet::::bonded(&v_stash).ok_or("not stash")?; - let keys = T::Keys::decode(&mut TrailingZeroInput::zeroes()).unwrap(); - let proof: Vec = vec![0, 1, 2, 3]; + let (keys, proof) = T::generate_session_keys_and_proof(v_controller.clone()); Session::::set_keys(RawOrigin::Signed(v_controller.clone()).into(), keys, proof)?; // Whitelist controller account from further DB operations. let v_controller_key = frame_system::Account::::hashed_key_for(&v_controller); @@ -163,10 +168,7 @@ fn check_membership_proof_setup( keys }; - // TODO: this benchmark is broken, session keys cannot be decoded into 128 bytes anymore, - // but not an issue for CI since it is `extra`. - let keys: T::Keys = Decode::decode(&mut &keys[..]).unwrap(); - let proof: Vec = vec![]; + let (keys, proof) = T::generate_session_keys_and_proof(controller.clone()); Session::::set_keys(RawOrigin::Signed(controller).into(), keys, proof).unwrap(); } diff --git a/substrate/frame/session/benchmarking/src/mock.rs b/substrate/frame/session/benchmarking/src/mock.rs index 288bf56ad25ee..9e726ee078fcc 100644 --- a/substrate/frame/session/benchmarking/src/mock.rs +++ b/substrate/frame/session/benchmarking/src/mock.rs @@ -19,6 +19,7 @@ #![cfg(test)] +use codec::Encode; use frame_election_provider_support::{ bounds::{ElectionBounds, ElectionBoundsBuilder}, onchain, SequentialPhragmen, @@ -153,7 +154,13 @@ impl pallet_staking::Config for Test { type TargetList = pallet_staking::UseValidatorsMap; } -impl crate::Config for Test {} +impl crate::Config for Test { + fn generate_session_keys_and_proof(owner: Self::AccountId) -> (Self::Keys, Vec) { + let keys = SessionKeys::generate(&owner.encode(), None); + + (keys.keys, keys.proof.encode()) + } +} pub fn new_test_ext() -> sp_io::TestExternalities { let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); diff --git a/substrate/frame/session/src/lib.rs b/substrate/frame/session/src/lib.rs index bfa7540132293..6c9954d8b5c71 100644 --- a/substrate/frame/session/src/lib.rs +++ b/substrate/frame/session/src/lib.rs @@ -630,19 +630,24 @@ pub mod pallet { #[pallet::call] impl Pallet { /// Sets the session key(s) of the function caller to `keys`. + /// /// Allows an account to set its session key prior to becoming a validator. /// This doesn't take effect until the next session. /// - /// The dispatch origin of this function must be signed. - /// - /// ## Complexity - /// - `O(1)`. Actual cost depends on the number of length of `T::Keys::key_ids()` which is - /// fixed. + /// - `origin`: The dispatch origin of this function must be signed. + /// - `keys`: The new session keys to set. These are the public keys of all sessions keys + /// setup in the runtime. + /// - `proof`: The proof that `origin` has access to the private keys of `keys`. See + /// [`impl_opaque_keys`](sp_runtime::impl_opaque_keys) for more information about the + /// proof format. #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::set_keys())] pub fn set_keys(origin: OriginFor, keys: T::Keys, proof: Vec) -> DispatchResult { let who = ensure_signed(origin)?; - ensure!(keys.ownership_proof_is_valid(&proof), Error::::InvalidProof); + ensure!( + who.using_encoded(|who| keys.ownership_proof_is_valid(who, &proof)), + Error::::InvalidProof, + ); Self::do_set_keys(&who, keys)?; Ok(()) @@ -656,10 +661,6 @@ pub mod pallet { /// convertible to a validator ID using the chain's typical addressing system (this usually /// means being a controller account) or directly convertible into a validator ID (which /// usually means being a stash account). - /// - /// ## Complexity - /// - `O(1)` in number of key types. Actual cost depends on the number of length of - /// `T::Keys::key_ids()` which is fixed. #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::purge_keys())] pub fn purge_keys(origin: OriginFor) -> DispatchResult { diff --git a/substrate/frame/session/src/mock.rs b/substrate/frame/session/src/mock.rs index ff85c4e2f2730..7ddd3fbf971ec 100644 --- a/substrate/frame/session/src/mock.rs +++ b/substrate/frame/session/src/mock.rs @@ -21,6 +21,7 @@ use super::*; use crate as pallet_session; #[cfg(feature = "historical")] use crate::historical as pallet_session_historical; +use codec::Encode; use frame_support::{derive_impl, parameter_types, traits::ConstU64}; use pallet_balances::{self, AccountData}; use sp_core::crypto::key_types::DUMMY; @@ -31,6 +32,7 @@ use sp_runtime::{ BuildStorage, }; use sp_staking::SessionIndex; +use sp_state_machine::BasicExternalities; use std::collections::BTreeMap; impl_opaque_keys! { @@ -68,6 +70,10 @@ impl OpaqueKeys for PreUpgradeMockSessionKeys { _ => &[], } } + + fn ownership_proof_is_valid(&self, _: &[u8], _: &[u8]) -> bool { + true + } } type Block = frame_system::mocking::MockBlock; @@ -205,6 +211,11 @@ pub fn reset_before_session_end_called() { BeforeSessionEndCalled::mutate(|b| *b = false); } +pub fn create_set_keys_proof(owner: u64, public: &UintAuthorityId) -> Vec { + //TODO: This should change to generate_proof_of_possesion + public.sign(&owner.encode()).unwrap().encode() +} + parameter_types! { pub static LastSessionEventIndex: usize = 0; } diff --git a/substrate/frame/session/src/tests.rs b/substrate/frame/session/src/tests.rs index 4a749b0a7817a..c1f03e43b9f3a 100644 --- a/substrate/frame/session/src/tests.rs +++ b/substrate/frame/session/src/tests.rs @@ -19,14 +19,14 @@ use super::*; use crate::mock::{ - authorities, before_session_end_called, force_new_session, new_test_ext, + authorities, before_session_end_called, create_set_keys_proof, force_new_session, new_test_ext, reset_before_session_end_called, session_changed, session_events_since_last_call, session_hold, set_next_validators, set_session_length, Balances, KeyDeposit, MockSessionKeys, PreUpgradeMockSessionKeys, RuntimeOrigin, Session, SessionChanged, System, Test, TestSessionChanged, TestValidatorIdOf, ValidatorAccounts, }; -use codec::Decode; +use codec::Encode; use sp_core::crypto::key_types::DUMMY; use sp_runtime::{testing::UintAuthorityId, Perbill}; @@ -88,11 +88,11 @@ fn purge_keys_works_for_stash_id() { let id = DUMMY; assert_eq!(Session::key_owner(id, UintAuthorityId(1).get_raw(id)), Some(1)); - assert_ok!(Session::purge_keys(RuntimeOrigin::signed(10))); + assert_ok!(Session::purge_keys(RuntimeOrigin::signed(1))); assert_ok!(Session::purge_keys(RuntimeOrigin::signed(2))); - assert_eq!(Session::load_keys(&10), None); - assert_eq!(Session::load_keys(&20), None); + assert_eq!(Session::load_keys(&1), None); + assert_eq!(Session::load_keys(&2), None); assert_eq!(Session::key_owner(id, UintAuthorityId(10).get_raw(id)), None); assert_eq!(Session::key_owner(id, UintAuthorityId(20).get_raw(id)), None); }) @@ -129,7 +129,11 @@ fn authorities_should_track_validators() { reset_before_session_end_called(); set_next_validators(vec![1, 2, 4]); - assert_ok!(Session::set_keys(RuntimeOrigin::signed(4), UintAuthorityId(4).into(), vec![])); + assert_ok!(Session::set_keys( + RuntimeOrigin::signed(4), + UintAuthorityId(4).into(), + create_set_keys_proof(4, &UintAuthorityId(4)), + )); force_new_session(); initialize_block(3); assert_eq!( @@ -200,7 +204,11 @@ fn session_change_should_work() { // Block 3: Set new key for validator 2; no visible change. initialize_block(3); - assert_ok!(Session::set_keys(RuntimeOrigin::signed(2), UintAuthorityId(5).into(), vec![])); + assert_ok!(Session::set_keys( + RuntimeOrigin::signed(2), + UintAuthorityId(5).into(), + create_set_keys_proof(2, &UintAuthorityId(5)), + )); assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]); assert_eq!(session_events_since_last_call(), vec![]); @@ -235,13 +243,25 @@ fn duplicates_are_not_allowed() { System::set_block_number(1); Session::on_initialize(1); assert_noop!( - Session::set_keys(RuntimeOrigin::signed(4), UintAuthorityId(1).into(), vec![]), + Session::set_keys( + RuntimeOrigin::signed(4), + UintAuthorityId(1).into(), + create_set_keys_proof(4, &UintAuthorityId(1)), + ), Error::::DuplicatedKey, ); - assert_ok!(Session::set_keys(RuntimeOrigin::signed(1), UintAuthorityId(10).into(), vec![])); + assert_ok!(Session::set_keys( + RuntimeOrigin::signed(1), + UintAuthorityId(10).into(), + create_set_keys_proof(1, &UintAuthorityId(10)), + )); // is fine now that 1 has migrated off. - assert_ok!(Session::set_keys(RuntimeOrigin::signed(4), UintAuthorityId(1).into(), vec![])); + assert_ok!(Session::set_keys( + RuntimeOrigin::signed(4), + UintAuthorityId(1).into(), + create_set_keys_proof(4, &UintAuthorityId(1)), + )); }); } @@ -284,7 +304,11 @@ fn session_changed_flag_works() { assert!(before_session_end_called()); reset_before_session_end_called(); - assert_ok!(Session::set_keys(RuntimeOrigin::signed(2), UintAuthorityId(5).into(), vec![])); + assert_ok!(Session::set_keys( + RuntimeOrigin::signed(2), + UintAuthorityId(5).into(), + create_set_keys_proof(2, &UintAuthorityId(5)), + )); force_new_session(); initialize_block(6); assert!(!session_changed()); @@ -295,7 +319,7 @@ fn session_changed_flag_works() { assert_ok!(Session::set_keys( RuntimeOrigin::signed(69), UintAuthorityId(69).into(), - vec![] + create_set_keys_proof(69, &UintAuthorityId(69)), )); force_new_session(); initialize_block(7); @@ -373,11 +397,12 @@ fn periodic_session_works() { #[test] fn session_keys_generate_output_works_as_set_keys_input() { new_test_ext().execute_with(|| { - let new_keys = mock::MockSessionKeys::generate(None); + let new_keys = mock::MockSessionKeys::generate(&2u64.encode(), None); + assert_ok!(Session::set_keys( RuntimeOrigin::signed(2), - ::Keys::decode(&mut &new_keys[..]).expect("Decode keys"), - vec![], + new_keys.keys, + new_keys.proof.encode(), )); }); } @@ -498,7 +523,7 @@ fn set_keys_should_fail_with_insufficient_funds() { // Attempt to set keys with an account that has insufficient funds // Should fail with Err(Token(FundsUnavailable)) from `pallet-balances` assert_err!( - Session::set_keys(RuntimeOrigin::signed(account_id), keys, vec![]), + Session::set_keys(RuntimeOrigin::signed(account_id), keys, create_set_keys_proof(account_id, &UintAuthorityId(account_id))), sp_runtime::TokenError::FundsUnavailable ); }); @@ -518,7 +543,7 @@ fn set_keys_should_hold_funds() { }); // Set keys and check the operation succeeds - let res = Session::set_keys(RuntimeOrigin::signed(account_id), keys, vec![]); + let res = Session::set_keys(RuntimeOrigin::signed(account_id), keys, create_set_keys_proof(account_id, &UintAuthorityId(account_id))); assert_ok!(res); // Check that the funds are held @@ -543,7 +568,7 @@ fn purge_keys_should_unhold_funds() { frame_system::Pallet::::inc_providers(&account_id); // First set the keys to reserve the deposit - let res = Session::set_keys(RuntimeOrigin::signed(account_id), keys, vec![]); + let res = Session::set_keys(RuntimeOrigin::signed(account_id), keys, create_set_keys_proof(account_id, &UintAuthorityId(account_id))); assert_ok!(res); // Check the reserved balance after setting keys @@ -580,7 +605,7 @@ fn existing_validators_without_hold_are_except() { assert_ok!(Session::set_keys( RuntimeOrigin::signed(1), UintAuthorityId(7).into(), - Default::default() + create_set_keys_proof(1, &UintAuthorityId(7)) )); assert_eq!(session_hold(1), 0); diff --git a/substrate/frame/staking/src/mock.rs b/substrate/frame/staking/src/mock.rs index f2f51fea7b017..7efc1a007a466 100644 --- a/substrate/frame/staking/src/mock.rs +++ b/substrate/frame/staking/src/mock.rs @@ -614,7 +614,10 @@ pub(crate) fn bond_validator(who: AccountId, val: Balance) { assert_ok!(Session::set_keys( RuntimeOrigin::signed(who), SessionKeys { other: who.into() }, - vec![] + SessionKeys { other: who.into() } + .create_ownership_proof(&who.encode()) + .unwrap() + .encode(), )); } diff --git a/substrate/frame/staking/src/tests.rs b/substrate/frame/staking/src/tests.rs index 9badc2c2cee50..ce78bbd8f047d 100644 --- a/substrate/frame/staking/src/tests.rs +++ b/substrate/frame/staking/src/tests.rs @@ -452,7 +452,10 @@ fn staking_should_work() { assert_ok!(Session::set_keys( RuntimeOrigin::signed(3), SessionKeys { other: 4.into() }, - vec![] + SessionKeys { other: 4.into() } + .create_ownership_proof(&AccountId::from(3u32).encode()) + .unwrap() + .encode(), )); // No effects will be seen so far. @@ -2058,7 +2061,10 @@ fn switching_roles() { assert_ok!(Session::set_keys( RuntimeOrigin::signed(5), SessionKeys { other: 6.into() }, - vec![] + SessionKeys { other: 6.into() } + .create_ownership_proof(&AccountId::from(5u32).encode()) + .unwrap() + .encode(), )); mock::start_active_era(1); @@ -2071,7 +2077,10 @@ fn switching_roles() { assert_ok!(Session::set_keys( RuntimeOrigin::signed(1), SessionKeys { other: 2.into() }, - vec![] + SessionKeys { other: 2.into() } + .create_ownership_proof(&AccountId::from(1u32).encode()) + .unwrap() + .encode(), )); // new stakes: // 11: 1000 self vote @@ -2180,7 +2189,10 @@ fn bond_with_little_staked_value_bounded() { assert_ok!(Session::set_keys( RuntimeOrigin::signed(1), SessionKeys { other: 1.into() }, - vec![] + SessionKeys { other: 1.into() } + .create_ownership_proof(&AccountId::from(1u32).encode()) + .unwrap() + .encode(), )); // 1 era worth of reward. BUT, we set the timestamp after on_initialize, so outdated by diff --git a/substrate/primitives/application-crypto/src/traits.rs b/substrate/primitives/application-crypto/src/traits.rs index 816cfe3e37fd6..38ce2f0ed966f 100644 --- a/substrate/primitives/application-crypto/src/traits.rs +++ b/substrate/primitives/application-crypto/src/traits.rs @@ -142,6 +142,7 @@ pub trait RuntimeAppPublic: Sized { fn sign>(&self, msg: &M) -> Option; /// Verify that the given signature matches the given message using this public key. + #[must_use] fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool; /// Generate proof of possession of the corresponding public key diff --git a/substrate/primitives/io/src/lib.rs b/substrate/primitives/io/src/lib.rs index e9f0dae59a07e..18d1a9e6b895d 100644 --- a/substrate/primitives/io/src/lib.rs +++ b/substrate/primitives/io/src/lib.rs @@ -2124,7 +2124,7 @@ mod tests { #[test] fn use_dalek_ext_works() { let mut ext = BasicExternalities::default(); - ext.register_extension(UseDalekExt::default()); + ext.register_extension(UseDalekExt); // With dalek the zero signature should fail to verify. ext.execute_with(|| { @@ -2140,7 +2140,7 @@ mod tests { #[test] fn dalek_should_not_panic_on_invalid_signature() { let mut ext = BasicExternalities::default(); - ext.register_extension(UseDalekExt::default()); + ext.register_extension(UseDalekExt); ext.execute_with(|| { let mut bytes = [0u8; 64]; diff --git a/substrate/primitives/runtime/Cargo.toml b/substrate/primitives/runtime/Cargo.toml index 10f9ab7daec3b..e3ef9f9fb1857 100644 --- a/substrate/primitives/runtime/Cargo.toml +++ b/substrate/primitives/runtime/Cargo.toml @@ -45,6 +45,7 @@ tuplex = { workspace = true, default-features = false } rand = { workspace = true, default-features = true } serde_json = { workspace = true, default-features = true } sp-api = { workspace = true, default-features = true } +sp-keystore = { workspace = true, default-features = true } sp-state-machine = { workspace = true, default-features = true } sp-tracing = { workspace = true, default-features = true } substrate-test-runtime-client = { workspace = true } diff --git a/substrate/primitives/runtime/src/testing.rs b/substrate/primitives/runtime/src/testing.rs index 1104435f278a6..7ddb1075dec61 100644 --- a/substrate/primitives/runtime/src/testing.rs +++ b/substrate/primitives/runtime/src/testing.rs @@ -151,6 +151,10 @@ impl OpaqueKeys for UintAuthorityId { fn get(&self, _: KeyTypeId) -> Option { self.using_encoded(|mut x| T::decode(&mut x)).ok() } + + fn ownership_proof_is_valid(&self, _: &[u8], _: &[u8]) -> bool { + true + } } impl traits::IdentifyAccount for UintAuthorityId { diff --git a/substrate/primitives/runtime/src/traits/mod.rs b/substrate/primitives/runtime/src/traits/mod.rs index b049e54a33341..3650b5a1f0c31 100644 --- a/substrate/primitives/runtime/src/traits/mod.rs +++ b/substrate/primitives/runtime/src/traits/mod.rs @@ -24,7 +24,7 @@ use crate::{ TransactionSource, TransactionValidity, TransactionValidityError, UnknownTransaction, ValidTransaction, }, - DispatchResult, + DispatchResult, KeyTypeId, }; use alloc::vec::Vec; use codec::{ @@ -1876,21 +1876,25 @@ pub trait ValidateUnsigned { /// Opaque data type that may be destructured into a series of raw byte slices (which represent /// individual keys). pub trait OpaqueKeys: Clone { - /// Types bound to this opaque keys that provide the key type ids returned. + /// The types that are bound to the [`KeyTypeId`]s. + /// + /// They can be seen as the ones working with the keys associated to the [`KeyTypeId`]s. type KeyTypeIdProviders; /// Return the key-type IDs supported by this set. - fn key_ids() -> &'static [crate::KeyTypeId]; + fn key_ids() -> &'static [KeyTypeId]; + /// Get the raw bytes of key with key-type ID `i`. - fn get_raw(&self, i: super::KeyTypeId) -> &[u8]; + fn get_raw(&self, i: KeyTypeId) -> &[u8]; + /// Get the decoded key with key-type ID `i`. - fn get(&self, i: super::KeyTypeId) -> Option { + fn get(&self, i: KeyTypeId) -> Option { T::decode(&mut self.get_raw(i)).ok() } - /// Verify a proof of ownership for the keys. - fn ownership_proof_is_valid(&self, _proof: &[u8]) -> bool { - true - } + + /// Proof the ownership of `owner` over the keys using `proof`. + #[must_use] + fn ownership_proof_is_valid(&self, owner: &[u8], proof: &[u8]) -> bool; } /// Input that adds infinite number of zero after wrapped input. @@ -2086,9 +2090,12 @@ macro_rules! impl_opaque_keys_inner { $( #[ $inner_attr:meta ] )* pub $field:ident: $type:ty, )* - } + }, + $crate_path:path, ) => { $( #[ $attr ] )* + /// + #[doc = concat!("Generated by [`impl_opaque_keys!`](", stringify!($crate_path),"::impl_opaque_keys).")] #[derive( Clone, PartialEq, Eq, $crate::codec::Encode, @@ -2109,9 +2116,30 @@ macro_rules! impl_opaque_keys_inner { /// /// The generated key pairs are stored in the keystore. /// - /// Returns the concatenated SCALE encoded public keys. - pub fn generate(seed: Option<$crate::Vec>) -> $crate::Vec { - let keys = Self{ + /// - `owner`: Some bytes that will be signed by the generated private keys. + /// These signatures are put into a tuple in the same order as the public keys. + /// The SCALE encoded signature tuple corresponds to the `proof` returned by this + /// function. + /// + /// - `seed`: Optional `seed` for seeding the private key generation. + /// + /// Returns the generated public session keys and proof. + #[allow(dead_code)] + pub fn generate( + owner: &[u8], + seed: Option<$crate::sp_std::vec::Vec>, + ) -> $crate::traits::GeneratedSessionKeys< + Self, + ( + $( + < + <$type as $crate::BoundToRuntimeAppPublic>::Public + as $crate::RuntimeAppPublic + >::Signature + ),* + ) + > { + let keys = Self { $( $field: < < @@ -2120,10 +2148,18 @@ macro_rules! impl_opaque_keys_inner { >::generate_pair(seed.clone()), )* }; - $crate::codec::Encode::encode(&keys) + + let proof = keys.create_ownership_proof(owner) + .expect("Private key that was generated a moment ago, should exist; qed"); + + $crate::traits::GeneratedSessionKeys { + keys, + proof + } } /// Converts `Self` into a `Vec` of `(raw public key, KeyTypeId)`. + #[allow(dead_code)] pub fn into_raw_public_keys( self, ) -> $crate::Vec<($crate::Vec, $crate::KeyTypeId)> { @@ -2146,6 +2182,7 @@ macro_rules! impl_opaque_keys_inner { /// keys (see [`Self::into_raw_public_keys`]). /// /// Returns `None` when the decoding failed, otherwise `Some(_)`. + #[allow(dead_code)] pub fn decode_into_raw_public_keys( encoded: &[u8], ) -> Option<$crate::Vec<($crate::Vec, $crate::KeyTypeId)>> { @@ -2153,6 +2190,37 @@ macro_rules! impl_opaque_keys_inner { .ok() .map(|s| s.into_raw_public_keys()) } + + /// Create the ownership proof. + /// + /// - `owner`: Some bytes that will be signed by the private keys associated to the + /// public keys in this session key object. These signatures are put into a tuple in + /// the same order as the public keys. The SCALE encoded signature tuple corresponds + /// to the `proof` returned by this function. + /// + /// Returns the SCALE encoded proof that will proof the ownership of the keys for `user`. + /// An error is returned if the signing of `user` failed, e.g. a private key isn't present in the keystore. + #[allow(dead_code)] + pub fn create_ownership_proof( + &self, + owner: &[u8], + ) -> $crate::sp_std::result::Result< + ( + $( + < + <$type as $crate::BoundToRuntimeAppPublic>::Public + as $crate::RuntimeAppPublic + >::Signature + ),* + ), + () + > { + let res = ($( + $crate::RuntimeAppPublic::generate_proof_of_possession(&mut self.$field.clone()).ok_or(())? + ),*); + + Ok(res) + } } impl $crate::traits::OpaqueKeys for $name { @@ -2183,14 +2251,63 @@ macro_rules! impl_opaque_keys_inner { _ => &[], } } + + fn ownership_proof_is_valid(&self, owner: &[u8], proof: &[u8]) -> bool { + // The proof is expected to be a tuple of all the signatures. + let Ok(proof) = <($( + < + < + $type as $crate::BoundToRuntimeAppPublic + >::Public as $crate::RuntimeAppPublic + >::Signature + ),*) as $crate::codec::DecodeAll>::decode_all(&mut &proof[..]) else { + return false + }; + + // "unpack" the proof so that we can access the individual signatures. + let ( $( $field ),* ) = proof; + + // Verify that all the signatures signed `owner`. + $( + + let valid = $crate::RuntimeAppPublic::verify_proof_of_possession(&self.$field, &$field); + + if !valid { + // We found an invalid signature. + return false + } + )* + + true + } } }; } -/// Implement `OpaqueKeys` for a described struct. +/// The output of generating session keys. +/// +/// Contains the public session keys and a `proof` to verify the ownership of these keys. +/// +/// To generate session keys the [`impl_opaque_keys!`](crate::impl_opaque_keys) needs to be used +/// first to create the session keys type and this type provides the `generate` function. +#[derive(Debug, Clone, Encode, Decode, TypeInfo)] +pub struct GeneratedSessionKeys { + /// The opaque public session keys for registering on-chain. + pub keys: Keys, + /// The opaque proof to verify the ownership of the keys. + pub proof: Proof, +} + +/// Implement [`OpaqueKeys`] for a described struct. /// /// Every field type must implement [`BoundToRuntimeAppPublic`](crate::BoundToRuntimeAppPublic). -/// `KeyTypeIdProviders` is set to the types given as fields. +/// The [`KeyTypeIdProviders`](OpaqueKeys::KeyTypeIdProviders) type is set to tuple of all field +/// types passed to the macro. +/// +/// The `proof` type used by the generated session keys for +/// [`ownership_proof_is_valid`](OpaqueKeys::ownership_proof_is_valid) is the SCALE encoded tuple of +/// all signatures. The order of the signatures is the same as the order of the fields in the +/// struct. Each signature is created by signing the `owner` given to the `generate` function. /// /// ```rust /// use sp_runtime::{ @@ -2234,7 +2351,8 @@ macro_rules! impl_opaque_keys { $( #[ $inner_attr ] )* pub $field: $type, )* - } + }, + $crate, } } } @@ -2260,7 +2378,8 @@ macro_rules! impl_opaque_keys { $( #[ $inner_attr ] )* pub $field: $type, )* - } + }, + $crate, } } } @@ -2424,6 +2543,7 @@ mod tests { crypto::{Pair, UncheckedFrom}, ecdsa, ed25519, sr25519, }; + use std::sync::Arc; macro_rules! signature_verify_test { ($algorithm:ident) => { @@ -2562,4 +2682,89 @@ mod tests { fn ecdsa_verify_works() { signature_verify_test!(ecdsa); } + + #[cfg(feature = "bls-experimental")] + fn bls377_verify_works() { + signature_verify_test!(bls377) + } + + #[cfg(feature = "bls-experimental")] + fn bls381_verify_works() { + signature_verify_test!(bls381) + } + + pub struct Sr25519Key; + impl crate::BoundToRuntimeAppPublic for Sr25519Key { + type Public = sp_application_crypto::sr25519::AppPublic; + } + + pub struct Ed25519Key; + impl crate::BoundToRuntimeAppPublic for Ed25519Key { + type Public = sp_application_crypto::ed25519::AppPublic; + } + + pub struct EcdsaKey; + impl crate::BoundToRuntimeAppPublic for EcdsaKey { + type Public = sp_application_crypto::ecdsa::AppPublic; + } + + impl_opaque_keys! { + /// Some comment + pub struct SessionKeys { + pub sr25519: Sr25519Key, + pub ed25519: Ed25519Key, + pub ecdsa: EcdsaKey, + } + } + + #[test] + fn opaque_keys_ownership_proof_works() { + let sr25519 = sp_core::sr25519::Pair::generate().0; + let ed25519 = sp_core::ed25519::Pair::generate().0; + let ecdsa = sp_core::ecdsa::Pair::generate().0; + + let session_keys = SessionKeys { + sr25519: sr25519.public().into(), + ed25519: ed25519.public().into(), + ecdsa: ecdsa.public().into(), + }; + + let owner = &b"owner"[..]; + + let sr25519_sig = sr25519.sign(&owner); + let ed25519_sig = ed25519.sign(&owner); + let ecdsa_sig = ecdsa.sign(&owner); + + for invalidate in [None, Some(0), Some(1), Some(2)] { + let proof = if let Some(invalidate) = invalidate { + match invalidate { + 0 => (sr25519.sign(&b"invalid"[..]), &ed25519_sig, &ecdsa_sig).encode(), + 1 => (&sr25519_sig, ed25519.sign(&b"invalid"[..]), &ecdsa_sig).encode(), + 2 => (&sr25519_sig, &ed25519_sig, ecdsa.sign(&b"invalid"[..])).encode(), + _ => unreachable!(), + } + } else { + (&sr25519_sig, &ed25519_sig, &ecdsa_sig).encode() + }; + + assert_eq!(session_keys.ownership_proof_is_valid(owner, &proof), invalidate.is_none()); + } + + // Ensure that a `proof` with extra junk data is rejected. + let proof = (&sr25519_sig, &ed25519_sig, &ecdsa_sig, "hello").encode(); + assert!(!session_keys.ownership_proof_is_valid(owner, &proof)); + + let mut ext = sp_io::TestExternalities::default(); + ext.register_extension(sp_keystore::KeystoreExt(Arc::new( + sp_keystore::testing::MemoryKeystore::new(), + ))); + + ext.execute_with(|| { + let session_keys = SessionKeys::generate(&owner, None); + + assert!(session_keys + .keys + .ownership_proof_is_valid(&owner, &session_keys.proof.encode())); + }); + } } diff --git a/substrate/primitives/session/Cargo.toml b/substrate/primitives/session/Cargo.toml index 72be81c1222ef..b9c3f34aabeee 100644 --- a/substrate/primitives/session/Cargo.toml +++ b/substrate/primitives/session/Cargo.toml @@ -21,7 +21,7 @@ scale-info = { features = ["derive"], workspace = true } sp-api = { workspace = true } sp-core = { workspace = true } sp-keystore = { optional = true, workspace = true } -sp-runtime = { optional = true, workspace = true } +sp-runtime = { workspace = true } sp-staking = { workspace = true } [features] diff --git a/substrate/primitives/session/src/lib.rs b/substrate/primitives/session/src/lib.rs index 3225097b665e6..0c86207df677d 100644 --- a/substrate/primitives/session/src/lib.rs +++ b/substrate/primitives/session/src/lib.rs @@ -115,7 +115,7 @@ where T: ProvideRuntimeApi, T::Api: SessionKeys, { - use sp_api::ApiExt; + use sp_api::{ApiError, ApiExt}; if seeds.is_empty() { return Ok(()) @@ -123,10 +123,22 @@ where let mut runtime_api = client.runtime_api(); + let version = runtime_api.api_version::>(at)?.ok_or_else(|| { + ApiError::Application(Box::from("Could not find `SessionKeys` runtime api")) + })?; + runtime_api.register_extension(sp_keystore::KeystoreExt::from(keystore)); for seed in seeds { - runtime_api.generate_session_keys(at, Some(seed.as_bytes().to_vec()))?; + let seed = Some(seed.as_bytes().to_vec()); + + if version < 2 { + #[allow(deprecated)] + runtime_api.generate_session_keys_before_version_2(at, seed.clone())?; + } else { + // `owner` isn't important here as we don't need a `proof`. + runtime_api.generate_session_keys(at, vec![], seed.clone())?; + } } Ok(()) diff --git a/substrate/primitives/session/src/runtime_api.rs b/substrate/primitives/session/src/runtime_api.rs index 3acc882aabcff..2a67fa73e5699 100644 --- a/substrate/primitives/session/src/runtime_api.rs +++ b/substrate/primitives/session/src/runtime_api.rs @@ -16,10 +16,28 @@ // limitations under the License. use alloc::vec::Vec; +use codec::{Decode, Encode}; pub use sp_core::crypto::KeyTypeId; +use sp_runtime::traits::GeneratedSessionKeys; + +/// Opaque [`GeneratedSessionKeys`](sp_runtime::traits::GeneratedSessionKeys). +#[derive(Debug, Decode, Encode, scale_info::TypeInfo)] +pub struct OpaqueGeneratedSessionKeys { + /// The public session keys. + pub keys: Vec, + /// The proof proving the ownership of the public session keys for some owner. + pub proof: Vec, +} + +impl From> for OpaqueGeneratedSessionKeys { + fn from(value: GeneratedSessionKeys) -> Self { + Self { keys: value.keys.encode(), proof: value.proof.encode() } + } +} sp_api::decl_runtime_apis! { /// Session keys runtime api. + #[api_version(2)] pub trait SessionKeys { /// Generate a set of session keys with optionally using the given seed. /// The keys should be stored within the keystore exposed via runtime @@ -28,11 +46,14 @@ sp_api::decl_runtime_apis! { /// The seed needs to be a valid `utf8` string. /// /// Returns the concatenated SCALE encoded public keys. + fn generate_session_keys(owner: Vec, seed: Option>) -> OpaqueGeneratedSessionKeys; + + #[changed_in(2)] fn generate_session_keys(seed: Option>) -> Vec; /// Decode the given public session keys. /// /// Returns the list of public raw public keys + key type. - fn decode_session_keys(encoded: Vec) -> Option, sp_core::crypto::KeyTypeId)>>; + fn decode_session_keys(encoded: Vec) -> Option, KeyTypeId)>>; } } diff --git a/substrate/test-utils/runtime/src/lib.rs b/substrate/test-utils/runtime/src/lib.rs index f440028c74c7c..896caae84ff14 100644 --- a/substrate/test-utils/runtime/src/lib.rs +++ b/substrate/test-utils/runtime/src/lib.rs @@ -747,8 +747,8 @@ impl_runtime_apis! { } impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(_: Option>) -> Vec { - SessionKeys::generate(None) + fn generate_session_keys(owner: Vec, _: Option>) -> sp_session::OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, None).into() } fn decode_session_keys( diff --git a/templates/minimal/runtime/src/lib.rs b/templates/minimal/runtime/src/lib.rs index bb7fb2ccde042..a6b2c2b7696b8 100644 --- a/templates/minimal/runtime/src/lib.rs +++ b/templates/minimal/runtime/src/lib.rs @@ -25,13 +25,25 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); extern crate alloc; -use alloc::vec::Vec; +use frame::{ + prelude::*, + runtime::{ + prelude::*, +use alloc::{vec, vec::Vec}; use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; use polkadot_sdk::{ polkadot_sdk_frame::{ self as frame, + deps::frame_support::{ + genesis_builder_helper::{build_state, get_preset} + }, deps::sp_genesis_builder, - runtime::{apis, prelude::*}, + runtime::{apis, apis::{ + self, impl_runtime_apis, ApplyExtrinsicResult, CheckInherentsResult, + ExtrinsicInclusionMode, OpaqueGeneratedSessionKeys, OpaqueMetadata, + }, + prelude::*}, + weights::{FixedFee, NoFee}, }, *, }; @@ -282,8 +294,8 @@ impl_runtime_apis! { } impl apis::SessionKeys for Runtime { - fn generate_session_keys(_seed: Option>) -> Vec { - Default::default() + fn generate_session_keys(_owner: Vec, _seed: Option>) -> OpaqueGeneratedSessionKeys { + OpaqueGeneratedSessionKeys { keys: Default::default(), proof: Default::default() } } fn decode_session_keys( diff --git a/templates/parachain/runtime/src/apis.rs b/templates/parachain/runtime/src/apis.rs index deb257d6dfee9..cb63ef62586d4 100644 --- a/templates/parachain/runtime/src/apis.rs +++ b/templates/parachain/runtime/src/apis.rs @@ -42,6 +42,7 @@ use sp_runtime::{ transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, }; +use sp_session::OpaqueGeneratedSessionKeys; use sp_version::RuntimeVersion; // Local module imports @@ -165,8 +166,8 @@ impl_runtime_apis! { } impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) + fn generate_session_keys(owner: Vec, seed: Option>) -> OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() } fn decode_session_keys( @@ -278,6 +279,7 @@ impl_runtime_apis! { ) -> Result, alloc::string::String> { use frame_benchmarking::{BenchmarkError, BenchmarkBatch}; use super::*; + use codec::Encode; use frame_system_benchmarking::Pallet as SystemBench; impl frame_system_benchmarking::Config for Runtime { @@ -292,7 +294,12 @@ impl_runtime_apis! { } use cumulus_pallet_session_benchmarking::Pallet as SessionBench; - impl cumulus_pallet_session_benchmarking::Config for Runtime {} + impl cumulus_pallet_session_benchmarking::Config for Runtime { + fn generate_session_keys_and_proof(owner: Self::AccountId) -> (Self::Keys, Vec) { + let keys = SessionKeys::generate(&owner.encode(), None); + (keys.keys, keys.proof.encode()) + } + } use polkadot_sdk::frame_support::traits::WhitelistedStorageKeys; let whitelist = AllPalletsWithSystem::whitelisted_storage_keys(); diff --git a/templates/solochain/runtime/src/apis.rs b/templates/solochain/runtime/src/apis.rs index 02880708f9228..80fb02fa9b53b 100644 --- a/templates/solochain/runtime/src/apis.rs +++ b/templates/solochain/runtime/src/apis.rs @@ -38,6 +38,7 @@ use sp_runtime::{ transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, }; +use sp_session::OpaqueGeneratedSessionKeys; use sp_version::RuntimeVersion; // Local module imports @@ -129,8 +130,8 @@ impl_runtime_apis! { } impl sp_session::SessionKeys for Runtime { - fn generate_session_keys(seed: Option>) -> Vec { - SessionKeys::generate(seed) + fn generate_session_keys(owner: Vec, seed: Option>) -> OpaqueGeneratedSessionKeys { + SessionKeys::generate(&owner, seed).into() } fn decode_session_keys(