--- a/node/cli/src/chain_spec.rs +++ b/node/cli/src/chain_spec.rs @@ -24,7 +24,7 @@ use serde_json::map::Map; use up_common::types::opaque::*; -use up_common::constants::EXISTENTIAL_DEPOSIT; +use up_common::constants::CANDIDACY_BOND; #[cfg(feature = "unique-runtime")] pub use unique_runtime as default_runtime; @@ -151,6 +151,7 @@ AccountPublic::from(get_from_seed::(seed)).into_account() } +#[cfg(not(any(feature = "unique-runtime", feature = "quartz-runtime")))] macro_rules! testnet_genesis { ( $runtime:path, @@ -191,7 +192,7 @@ .cloned() .map(|(acc, _)| acc) .collect(), - candidacy_bond: EXISTENTIAL_DEPOSIT * 16, + candidacy_bond: CANDIDACY_BOND, ..Default::default() }, session: SessionConfig { @@ -207,9 +208,6 @@ .collect(), }, aura: Default::default(), - /*aura: AuraConfig { - authorities: $initial_authorities, - },*/ aura_ext: Default::default(), evm: EVMConfig { accounts: BTreeMap::new(), @@ -219,6 +217,56 @@ }}; } +#[cfg(any(feature = "unique-runtime", feature = "quartz-runtime"))] +macro_rules! testnet_genesis { + ( + $runtime:path, + $root_key:expr, + $initial_invulnerables:expr, + $endowed_accounts:expr, + $id:expr + ) => {{ + use $runtime::*; + + GenesisConfig { + system: SystemConfig { + code: WASM_BINARY + .expect("WASM binary was not build, please build it!") + .to_vec(), + }, + balances: BalancesConfig { + balances: $endowed_accounts + .iter() + .cloned() + // 1e13 UNQ + .map(|k| (k, 1 << 100)) + .collect(), + }, + treasury: Default::default(), + tokens: TokensConfig { balances: vec![] }, + sudo: SudoConfig { + key: Some($root_key), + }, + vesting: VestingConfig { vesting: vec![] }, + parachain_info: ParachainInfoConfig { + parachain_id: $id.into(), + }, + parachain_system: Default::default(), + aura: AuraConfig { + authorities: $initial_invulnerables + .into_iter() + .map(|(_, aura)| aura) + .collect(), + }, + aura_ext: Default::default(), + evm: EVMConfig { + accounts: BTreeMap::new(), + }, + ethereum: EthereumConfig {}, + } + }}; +} + pub fn development_config() -> DefaultChainSpec { let mut properties = Map::new(); properties.insert("tokenSymbol".into(), default_runtime::TOKEN_SYMBOL.into()); --- a/primitives/common/src/constants.rs +++ b/primitives/common/src/constants.rs @@ -41,7 +41,10 @@ pub const CENTIUNIQUE: Balance = 10 * MILLIUNIQUE; pub const UNIQUE: Balance = 100 * CENTIUNIQUE; +/// Minimum balance required to create or keep an account open. pub const EXISTENTIAL_DEPOSIT: u128 = 0; +/// Amount of Balance reserved for candidate registration. +pub const CANDIDACY_BOND: u128 = EXISTENTIAL_DEPOSIT; // Targeting 0.1 UNQ per transfer pub const WEIGHT_TO_FEE_COEFF: u32 = /**/207_163_598/**/; --- /dev/null +++ b/runtime/common/config/pallets/collator_selection.rs @@ -0,0 +1,78 @@ +// Copyright 2019-2022 Unique Network (Gibraltar) Ltd. +// This file is part of Unique Network. + +// Unique Network 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. + +// Unique Network 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 Unique Network. If not, see . + +use frame_support::{parameter_types, PalletId}; +use frame_system::EnsureRoot; +use crate::{ + AccountId, BlockNumber, Runtime, RuntimeEvent, Balances, Aura, Session, SessionKeys, + CollatorSelection, +}; +use up_common::constants::*; + +parameter_types! { + pub const SessionPeriod: BlockNumber = HOURS; + pub const SessionOffset: BlockNumber = 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 lets be pedantic. + type SessionHandler = ::KeyTypeIdProviders; + type Keys = SessionKeys; + type WeightInfo = pallet_session::weights::SubstrateWeight; // (); +} + +parameter_types! { + pub const UncleGenerations: u32 = 0; +} + +impl pallet_authorship::Config for Runtime { + type FindAuthor = pallet_session::FindAccountFromAuthorIndex; + type UncleGenerations = UncleGenerations; + type FilterUncle = (); + type EventHandler = CollatorSelection; +} + +parameter_types! { + pub const PotId: PalletId = PalletId(*b"PotStake"); + pub const MaxCandidates: u32 = 1000; + pub const MinCandidates: u32 = 5; + pub const MaxInvulnerables: u32 = 100; +} + +impl pallet_collator_selection::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + // We allow root only to execute privileged collator selection operations. + type UpdateOrigin = EnsureRoot; + type PotId = PotId; + type MaxCandidates = MaxCandidates; + type MinCandidates = MinCandidates; + type MaxInvulnerables = MaxInvulnerables; + // todo:collator kick threshold should be in storage and configured only by root -- or rather UpdateOrigin + // Should be a multiple of session or things will get inconsistent. + type KickThreshold = SessionPeriod; + type ValidatorId = ::AccountId; + type ValidatorIdOf = pallet_collator_selection::IdentityCollator; + type ValidatorRegistration = Session; + type WeightInfo = (); +} --- a/runtime/common/config/pallets/mod.rs +++ b/runtime/common/config/pallets/mod.rs @@ -46,6 +46,9 @@ #[cfg(feature = "app-promotion")] pub mod app_promotion; +#[cfg(feature = "collator-selection")] +pub mod collator_selection; + parameter_types! { pub TreasuryAccountId: AccountId = TreasuryModuleId::get().into_account_truncating(); pub const CollectionCreationPrice: Balance = 2 * UNIQUE; --- a/runtime/common/config/substrate.rs +++ b/runtime/common/config/substrate.rs @@ -34,7 +34,7 @@ }; use crate::{ runtime_common::DealWithFees, Runtime, RuntimeEvent, RuntimeCall, RuntimeOrigin, PalletInfo, - System, Balances, Treasury, SS58Prefix, Aura, Session, SessionKeys, CollatorSelection, Version, + System, Balances, Treasury, SS58Prefix, Version, }; use up_common::{types::*, constants::*}; @@ -215,58 +215,4 @@ type AuthorityId = AuraId; type DisabledValidators = (); type MaxAuthorities = MaxAuthorities; -} - -parameter_types! { - pub const SessionPeriod: BlockNumber = HOURS; - pub const SessionOffset: BlockNumber = 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 lets be pedantic. - type SessionHandler = ::KeyTypeIdProviders; - type Keys = SessionKeys; - type WeightInfo = pallet_session::weights::SubstrateWeight; // (); -} - -parameter_types! { - pub const UncleGenerations: u32 = 0; -} - -impl pallet_authorship::Config for Runtime { - type FindAuthor = pallet_session::FindAccountFromAuthorIndex; - type UncleGenerations = UncleGenerations; - type FilterUncle = (); - type EventHandler = CollatorSelection; -} - -parameter_types! { - pub const PotId: PalletId = PalletId(*b"PotStake"); - pub const MaxCandidates: u32 = 1000; - pub const MinCandidates: u32 = 5; - pub const MaxInvulnerables: u32 = 100; -} - -impl pallet_collator_selection::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Currency = Balances; - // We allow root only to execute privileged collator selection operations. - type UpdateOrigin = EnsureRoot; - type PotId = PotId; - type MaxCandidates = MaxCandidates; - type MinCandidates = MinCandidates; - type MaxInvulnerables = MaxInvulnerables; - // Should be a multiple of session or things will get inconsistent. - type KickThreshold = SessionPeriod; - type ValidatorId = ::AccountId; - type ValidatorIdOf = pallet_collator_selection::IdentityCollator; - type ValidatorRegistration = Session; - type WeightInfo = (); } --- a/runtime/common/construct_runtime/mod.rs +++ b/runtime/common/construct_runtime/mod.rs @@ -32,9 +32,15 @@ ParachainSystem: cumulus_pallet_parachain_system::{Pallet, Call, Config, Storage, Inherent, Event, ValidateUnsigned} = 20, ParachainInfo: parachain_info::{Pallet, Storage, Config} = 21, + #[runtimes(opal)] Authorship: pallet_authorship::{Pallet, Call, Storage} = 22, + + #[runtimes(opal)] CollatorSelection: pallet_collator_selection::{Pallet, Call, Storage, Event, Config} = 23, + + #[runtimes(opal)] Session: pallet_session::{Pallet, Call, Storage, Event, Config} = 24, + Aura: pallet_aura::{Pallet, Config} = 25, AuraExt: cumulus_pallet_aura_ext::{Pallet, Config} = 26, --- a/runtime/common/mod.rs +++ b/runtime/common/mod.rs @@ -180,154 +180,166 @@ pub struct AuraToCollatorSelection; impl frame_support::traits::OnRuntimeUpgrade for AuraToCollatorSelection { fn on_runtime_upgrade() -> Weight { - use frame_support::{BoundedVec, storage::migration}; - use sp_runtime::{ - traits::{OpaqueKeys, Saturating}, - RuntimeAppPublic, - }; - use pallet_session::SessionManager; - use up_common::constants::EXISTENTIAL_DEPOSIT; - use crate::config::substrate::MaxInvulnerables; - - let mut weight = ::DbWeight::get().reads(1); + #[cfg(feature = "collator-selection")] + { + use frame_support::{BoundedVec, storage::migration}; + use sp_runtime::{ + traits::{OpaqueKeys, Saturating}, + RuntimeAppPublic, + }; + use pallet_session::SessionManager; + use up_common::constants::CANDIDACY_BOND; + use crate::config::pallets::collator_selection::MaxInvulnerables; - let version = - migration::get_storage_value::<()>(b"AuraToCollatorSelection", b"StorageVersion", &[]); - - let should_upgrade = match version { - None => true, - Some(_) => false, - }; + let mut weight = ::DbWeight::get().reads(1); - if should_upgrade { - log::info!( - target: "runtime::aura_to_collator_selection", - "Running migration of Aura authorities to Collator Selection invulnerables" + let version = migration::get_storage_value::<()>( + b"AuraToCollatorSelection", + b"StorageVersion", + &[], ); - let invulnerables = pallet_aura::Pallet::::authorities() - .iter() - .cloned() - .filter_map(|authority_id| { - weight.saturating_accrue(::DbWeight::get().reads_writes(1, 1)); - let vec = authority_id.clone().to_raw_vec(); - let slice = vec.as_slice(); - let array: Option<[u8; 32]> = match slice.try_into() { - Ok(a) => Some(a), - Err(_) => { - log::error!("Failed to convert an Aura authority to a Collator Selection invulnerable: {:?}", authority_id); - None - }, - }; - array.map(|a| (AccountId::from(a), authority_id)) - }) - .collect::>(); + let should_upgrade = match version { + None => true, + Some(_) => false, + }; + + if should_upgrade { + log::info!( + target: "runtime::aura_to_collator_selection", + "Running migration of Aura authorities to Collator Selection invulnerables" + ); - let bounded_invulnerables = BoundedVec::<_, MaxInvulnerables>::try_from( - invulnerables + let invulnerables = pallet_aura::Pallet::::authorities() .iter() .cloned() - .map(|(acc, _)| acc) - .collect::>(), - ) - .expect("Existing collators/invulnerables are more than MaxInvulnerables"); + .filter_map(|authority_id| { + weight.saturating_accrue(::DbWeight::get().reads_writes(1, 1)); + let vec = authority_id.clone().to_raw_vec(); + let slice = vec.as_slice(); + let array: Option<[u8; 32]> = match slice.try_into() { + Ok(a) => Some(a), + Err(_) => { + log::error!("Failed to convert an Aura authority to a Collator Selection invulnerable: {:?}", authority_id); + None + }, + }; + array.map(|a| (AccountId::from(a), authority_id)) + }) + .collect::>(); - >::put(bounded_invulnerables); - >::put(0); - >::put(EXISTENTIAL_DEPOSIT * 16); + let bounded_invulnerables = BoundedVec::<_, MaxInvulnerables>::try_from( + invulnerables + .iter() + .cloned() + .map(|(acc, _)| acc) + .collect::>(), + ) + .expect("Existing collators/invulnerables are more than MaxInvulnerables"); + + >::put(bounded_invulnerables); + >::put(0); + >::put(CANDIDACY_BOND); - let keys = invulnerables - .into_iter() - .map(|(acc, aura)| { - ( - acc.clone(), // account id - acc, // validator id - SessionKeys { aura: aura.clone() }, // session keys - ) - }) - .collect::>(); + let keys = invulnerables + .into_iter() + .map(|(acc, aura)| { + ( + acc.clone(), // account id + acc, // validator id + SessionKeys { aura: aura.clone() }, // session keys + ) + }) + .collect::>(); - for (account, val, keys) in keys.iter().cloned() { - for id in ::Keys::key_ids() { - >::insert((*id, keys.get_raw(*id)), &val) - } - >::insert(&val, &keys); - // todo exercise caution, the following is taken from genesis - if frame_system::Pallet::::inc_consumers_without_limit(&account).is_err() { - log::warn!( - "We have entered an error with incrementing consumers without limit during the migration" - ); - // This will leak a provider reference, however it only happens once (at - // genesis) so it's really not a big deal and we assume that the user wants to - // do this since it's the only way a non-endowed account can contain a session - // key. - frame_system::Pallet::::inc_providers(&account); + for (account, val, keys) in keys.iter().cloned() { + for id in ::Keys::key_ids() { + >::insert((*id, keys.get_raw(*id)), &val) + } + >::insert(&val, &keys); + // todo exercise caution, the following is taken from genesis + if frame_system::Pallet::::inc_consumers_without_limit(&account) + .is_err() + { + log::warn!( + "We have entered an error with incrementing consumers without limit during the migration" + ); + // This will leak a provider reference, however it only happens once (at + // genesis) so it's really not a big deal and we assume that the user wants to + // do this since it's the only way a non-endowed account can contain a session + // key. + frame_system::Pallet::::inc_providers(&account); + } } - } - let initial_validators_0 = - ::SessionManager::new_session(0).unwrap_or_else( - || { - frame_support::print( - "No initial validator provided by `SessionManager`, use \ - session config keys to generate initial validator set.", - ); - keys.iter().map(|x| x.1.clone()).collect() - }, - ); - /*assert!( - !initial_validators_0.is_empty(), - "Empty validator set for session 0 in (pseudo) genesis block!" - );*/ + let initial_validators_0 = + ::SessionManager::new_session(0) + .unwrap_or_else(|| { + frame_support::print( + "No initial validator provided by `SessionManager`, use \ + session config keys to generate initial validator set.", + ); + keys.iter().map(|x| x.1.clone()).collect() + }); + /*assert!( + !initial_validators_0.is_empty(), + "Empty validator set for session 0 in (pseudo) genesis block!" + );*/ - let initial_validators_1 = - ::SessionManager::new_session(1) - .unwrap_or_else(|| initial_validators_0.clone()); - /*assert!( - !initial_validators_1.is_empty(), - "Empty validator set for session 1 in (pseudo) genesis block!" - );*/ + let initial_validators_1 = + ::SessionManager::new_session(1) + .unwrap_or_else(|| initial_validators_0.clone()); + /*assert!( + !initial_validators_1.is_empty(), + "Empty validator set for session 1 in (pseudo) genesis block!" + );*/ - let queued_keys: Vec<_> = initial_validators_1 - .iter() - .cloned() - .map(|v| { - ( - v.clone(), - >::get(&v) - .expect("Validator in session 1 missing keys!"), - ) - }) - .collect(); + let queued_keys: Vec<_> = initial_validators_1 + .iter() + .cloned() + .map(|v| { + ( + v.clone(), + >::get(&v) + .expect("Validator in session 1 missing keys!"), + ) + }) + .collect(); - // Tell everyone about the genesis session keys -- Aura must've already initialized it - //::SessionHandler::on_genesis_session::<::Keys>(&queued_keys); + // Tell everyone about the genesis session keys -- Aura must've already initialized it + //::SessionHandler::on_genesis_session::<::Keys>(&queued_keys); + + >::put(initial_validators_0); + >::put(queued_keys); - >::put(initial_validators_0); - >::put(queued_keys); + ::SessionManager::start_session(0); - ::SessionManager::start_session(0); + log::info!( + target: "runtime::aura_to_collator_selection", + "Migration of Aura authorities to Collator Selection invulnerables is complete." + ); - log::info!( - target: "runtime::aura_to_collator_selection", - "Migration of Aura authorities to Collator Selection invulnerables is complete." - ); + migration::put_storage_value::<()>( + b"AuraToCollatorSelection", + b"StorageVersion", + &[], + (), + ); - migration::put_storage_value::<()>( - b"AuraToCollatorSelection", - b"StorageVersion", - &[], - (), - ); + weight += ::DbWeight::get().writes(1) + } else { + log::info!( + target: "runtime::aura_to_collator_selection", + "The storage migration has already been flagged as complete. No migration needs to be done.", + ); + } - weight += ::DbWeight::get().writes(1) - } else { - log::info!( - target: "runtime::aura_to_collator_selection", - "The storage migration has already been flagged as complete. No migration needs to be done.", - ); + weight } - weight + #[cfg(not(feature = "collator-selection"))] + { + Weight::zero() + } } } --- a/runtime/opal/Cargo.toml +++ b/runtime/opal/Cargo.toml @@ -173,13 +173,14 @@ "pallet-foreign-assets/std", ] limit-testing = ['pallet-unique/limit-testing', 'up-data-structs/limit-testing'] -opal-runtime = ['refungible', 'rmrk', 'app-promotion', 'foreign-assets'] +opal-runtime = ['refungible', 'rmrk', 'app-promotion', 'foreign-assets', 'collator-selection'] refungible = [] scheduler = [] rmrk = [] foreign-assets = [] app-promotion = [] +collator-selection = [] ################################################################################ # Substrate Dependencies --- a/runtime/quartz/Cargo.toml +++ b/runtime/quartz/Cargo.toml @@ -177,6 +177,7 @@ scheduler = [] rmrk = [] foreign-assets = [] +collator-selection = [] ################################################################################ # Substrate Dependencies --- a/runtime/unique/Cargo.toml +++ b/runtime/unique/Cargo.toml @@ -178,6 +178,7 @@ scheduler = [] rmrk = [] foreign-assets = [] +collator-selection = [] ################################################################################ # Substrate Dependencies