local testnet for substrate node (#70)

This commit is contained in:
Svyatoslav Nikolsky
2020-04-23 13:31:29 +03:00
committed by Bastian Köcher
parent 37b8bc51aa
commit 04c9da81da
3 changed files with 195 additions and 15 deletions
+60 -7
View File
@@ -15,8 +15,8 @@
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.
use bridge_node_runtime::{ use bridge_node_runtime::{
AccountId, AuraConfig, BalancesConfig, BridgeEthPoAConfig, GenesisConfig, GrandpaConfig, Signature, SudoConfig, AccountId, AuraConfig, BalancesConfig, BridgeEthPoAConfig, GenesisConfig, GrandpaConfig, SessionConfig,
SystemConfig, WASM_BINARY, SessionKeys, Signature, SudoConfig, SystemConfig, WASM_BINARY,
}; };
use grandpa_primitives::AuthorityId as GrandpaId; use grandpa_primitives::AuthorityId as GrandpaId;
use sc_service; use sc_service;
@@ -34,6 +34,8 @@ pub type ChainSpec = sc_service::ChainSpec<GenesisConfig>;
pub enum Alternative { pub enum Alternative {
/// Whatever the current runtime is, with just Alice as an auth. /// Whatever the current runtime is, with just Alice as an auth.
Development, Development,
/// Whatever the current runtime is, with simple Alice/Bob/Charlie/Dave/Eve auths.
LocalTestnet,
} }
/// Helper function to generate a crypto pair from seed /// Helper function to generate a crypto pair from seed
@@ -54,8 +56,12 @@ where
} }
/// Helper function to generate an authority key for Aura /// Helper function to generate an authority key for Aura
pub fn get_authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId) { pub fn get_authority_keys_from_seed(s: &str) -> (AccountId, AuraId, GrandpaId) {
(get_from_seed::<AuraId>(s), get_from_seed::<GrandpaId>(s)) (
get_account_id_from_seed::<sr25519::Public>(s),
get_from_seed::<AuraId>(s),
get_from_seed::<GrandpaId>(s),
)
} }
impl Alternative { impl Alternative {
@@ -84,19 +90,60 @@ impl Alternative {
None, None,
None, None,
), ),
Alternative::LocalTestnet => ChainSpec::from_genesis(
"Local Testnet",
"local_testnet",
|| {
testnet_genesis(
vec![
get_authority_keys_from_seed("Alice"),
get_authority_keys_from_seed("Bob"),
get_authority_keys_from_seed("Charlie"),
get_authority_keys_from_seed("Dave"),
get_authority_keys_from_seed("Eve"),
],
get_account_id_from_seed::<sr25519::Public>("Alice"),
vec![
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_account_id_from_seed::<sr25519::Public>("Bob"),
get_account_id_from_seed::<sr25519::Public>("Charlie"),
get_account_id_from_seed::<sr25519::Public>("Dave"),
get_account_id_from_seed::<sr25519::Public>("Eve"),
get_account_id_from_seed::<sr25519::Public>("Ferdie"),
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
get_account_id_from_seed::<sr25519::Public>("Charlie//stash"),
get_account_id_from_seed::<sr25519::Public>("Dave//stash"),
get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
],
true,
)
},
vec![],
None,
None,
None,
None,
),
}) })
} }
pub(crate) fn from(s: &str) -> Option<Self> { pub(crate) fn from(s: &str) -> Option<Self> {
match s { match s {
"" | "dev" => Some(Alternative::Development), "" | "dev" => Some(Alternative::Development),
"local" => Some(Alternative::LocalTestnet),
_ => None, _ => None,
} }
} }
} }
fn session_keys(aura: AuraId, grandpa: GrandpaId) -> SessionKeys {
SessionKeys { aura, grandpa }
}
fn testnet_genesis( fn testnet_genesis(
initial_authorities: Vec<(AuraId, GrandpaId)>, initial_authorities: Vec<(AccountId, AuraId, GrandpaId)>,
root_key: AccountId, root_key: AccountId,
endowed_accounts: Vec<AccountId>, endowed_accounts: Vec<AccountId>,
_enable_println: bool, _enable_println: bool,
@@ -110,13 +157,19 @@ fn testnet_genesis(
balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(), balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(),
}), }),
pallet_aura: Some(AuraConfig { pallet_aura: Some(AuraConfig {
authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect(), authorities: Vec::new(),
}), }),
pallet_bridge_eth_poa: load_kovan_config(), pallet_bridge_eth_poa: load_kovan_config(),
pallet_grandpa: Some(GrandpaConfig { pallet_grandpa: Some(GrandpaConfig {
authorities: initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect(), authorities: Vec::new(),
}), }),
pallet_sudo: Some(SudoConfig { key: root_key }), pallet_sudo: Some(SudoConfig { key: root_key }),
pallet_session: Some(SessionConfig {
keys: initial_authorities
.iter()
.map(|x| (x.0.clone(), x.0.clone(), session_keys(x.1.clone(), x.2.clone())))
.collect::<Vec<_>>(),
}),
} }
} }
+12
View File
@@ -59,6 +59,12 @@ default-features = false
rev = "2afecf81ee19b8a6edb364b419190ea47c4a4a31" rev = "2afecf81ee19b8a6edb364b419190ea47c4a4a31"
git = "https://github.com/paritytech/substrate/" git = "https://github.com/paritytech/substrate/"
[dependencies.pallet-session]
version = "2.0.0-alpha.2"
default-features = false
rev = "2afecf81ee19b8a6edb364b419190ea47c4a4a31"
git = "https://github.com/paritytech/substrate/"
[dependencies.frame-system] [dependencies.frame-system]
version = "2.0.0-alpha.2" version = "2.0.0-alpha.2"
default-features = false default-features = false
@@ -149,6 +155,12 @@ default-features = false
rev = "2afecf81ee19b8a6edb364b419190ea47c4a4a31" rev = "2afecf81ee19b8a6edb364b419190ea47c4a4a31"
git = "https://github.com/paritytech/substrate/" git = "https://github.com/paritytech/substrate/"
[dependencies.sp-staking]
version = "2.0.0-alpha.2"
default-features = false
rev = "2afecf81ee19b8a6edb364b419190ea47c4a4a31"
git = "https://github.com/paritytech/substrate/"
[dependencies.sp-std] [dependencies.sp-std]
version = "2.0.0-alpha.2" version = "2.0.0-alpha.2"
default-features = false default-features = false
+123 -8
View File
@@ -24,12 +24,15 @@
#[cfg(feature = "std")] #[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
use codec::{Decode, Encode};
use pallet_grandpa::fg_primitives; use pallet_grandpa::fg_primitives;
use pallet_grandpa::AuthorityList as GrandpaAuthorityList; use pallet_grandpa::AuthorityList as GrandpaAuthorityList;
use sp_api::impl_runtime_apis; use sp_api::impl_runtime_apis;
use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_consensus_aura::sr25519::AuthorityId as AuraId;
use sp_core::OpaqueMetadata; use sp_core::OpaqueMetadata;
use sp_runtime::traits::{BlakeTwo256, Block as BlockT, ConvertInto, IdentifyAccount, IdentityLookup, Verify}; use sp_runtime::traits::{
BlakeTwo256, Block as BlockT, ConvertInto, IdentifyAccount, IdentityLookup, OpaqueKeys, Verify,
};
use sp_runtime::{ use sp_runtime::{
create_runtime_str, generic, impl_opaque_keys, transaction_validity::TransactionValidity, ApplyExtrinsicResult, create_runtime_str, generic, impl_opaque_keys, transaction_validity::TransactionValidity, ApplyExtrinsicResult,
MultiSignature, MultiSignature,
@@ -89,12 +92,12 @@ pub mod opaque {
pub type Block = generic::Block<Header, UncheckedExtrinsic>; pub type Block = generic::Block<Header, UncheckedExtrinsic>;
/// Opaque block identifier type. /// Opaque block identifier type.
pub type BlockId = generic::BlockId<Block>; pub type BlockId = generic::BlockId<Block>;
}
impl_opaque_keys! { impl_opaque_keys! {
pub struct SessionKeys { pub struct SessionKeys {
pub aura: Aura, pub aura: Aura,
pub grandpa: Grandpa, pub grandpa: Grandpa,
}
} }
} }
@@ -233,6 +236,72 @@ impl pallet_sudo::Trait for Runtime {
type Call = Call; type Call = Call;
} }
parameter_types! {
pub const Period: BlockNumber = 4;
pub const Offset: BlockNumber = 0;
}
impl pallet_session::Trait for Runtime {
type Event = Event;
type ValidatorId = <Self as frame_system::Trait>::AccountId;
type ValidatorIdOf = ();
type ShouldEndSession = pallet_session::PeriodicSessions<Period, Offset>;
type SessionManager = ShiftSessionManager;
type SessionHandler = <SessionKeys as OpaqueKeys>::KeyTypeIdProviders;
type Keys = SessionKeys;
type DisabledValidatorsThreshold = ();
}
pub struct ShiftSessionManager;
impl ShiftSessionManager {
/// Select validators for session.
fn select_validators(
session_index: sp_staking::SessionIndex,
available_validators: &[AccountId],
) -> Vec<AccountId> {
let available_validators_count = available_validators.len();
let count = sp_std::cmp::max(1, 2 * available_validators_count / 3);
let offset = session_index as usize % available_validators_count;
let end = offset + count;
let session_validators = match end.overflowing_sub(available_validators_count) {
(wrapped_end, false) if wrapped_end != 0 => available_validators[offset..]
.iter()
.chain(available_validators[..wrapped_end].iter())
.cloned()
.collect(),
_ => available_validators[offset..end].to_vec(),
};
session_validators
}
}
impl pallet_session::SessionManager<AccountId> for ShiftSessionManager {
fn end_session(_: sp_staking::SessionIndex) {}
fn new_session(session_index: sp_staking::SessionIndex) -> Option<Vec<AccountId>> {
// can't access genesis config here :/
if session_index == 0 || session_index == 1 {
return None;
}
// the idea that on first call (i.e. when session 1 ends) we're reading current
// set of validators from session module (they are initial validators) and save
// in our 'local storage'.
// then for every session we select (deterministically) 2/3 of these initial
// validators to serve validators of new session
let available_validators = sp_io::storage::get(b":available_validators")
.and_then(|validators| Decode::decode(&mut &validators[..]).ok())
.unwrap_or_else(|| {
let validators = <pallet_session::Module<Runtime>>::validators();
sp_io::storage::set(b":available_validators", &validators.encode());
validators
});
Some(Self::select_validators(session_index, &available_validators))
}
}
construct_runtime!( construct_runtime!(
pub enum Runtime where pub enum Runtime where
Block = Block, Block = Block,
@@ -247,6 +316,7 @@ construct_runtime!(
Balances: pallet_balances::{Module, Call, Storage, Config<T>, Event<T>}, Balances: pallet_balances::{Module, Call, Storage, Config<T>, Event<T>},
TransactionPayment: pallet_transaction_payment::{Module, Storage}, TransactionPayment: pallet_transaction_payment::{Module, Storage},
Sudo: pallet_sudo::{Module, Call, Config<T>, Storage, Event<T>}, Sudo: pallet_sudo::{Module, Call, Config<T>, Storage, Event<T>},
Session: pallet_session::{Module, Call, Storage, Event, Config<T>},
BridgeEthPoA: pallet_bridge_eth_poa::{Module, Call, Config, Storage, ValidateUnsigned}, BridgeEthPoA: pallet_bridge_eth_poa::{Module, Call, Config, Storage, ValidateUnsigned},
} }
); );
@@ -374,13 +444,13 @@ impl_runtime_apis! {
impl sp_session::SessionKeys<Block> for Runtime { impl sp_session::SessionKeys<Block> for Runtime {
fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> { fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {
opaque::SessionKeys::generate(seed) SessionKeys::generate(seed)
} }
fn decode_session_keys( fn decode_session_keys(
encoded: Vec<u8>, encoded: Vec<u8>,
) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> { ) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> {
opaque::SessionKeys::decode_into_raw_public_keys(&encoded) SessionKeys::decode_into_raw_public_keys(&encoded)
} }
} }
@@ -390,3 +460,48 @@ impl_runtime_apis! {
} }
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn shift_session_manager_works() {
let acc1 = AccountId::from([1u8; 32]);
let acc2 = AccountId::from([2u8; 32]);
let acc3 = AccountId::from([3u8; 32]);
let acc4 = AccountId::from([4u8; 32]);
let acc5 = AccountId::from([5u8; 32]);
let all_accs = vec![acc1.clone(), acc2.clone(), acc3.clone(), acc4.clone(), acc5.clone()];
// at least 1 validator is selected
assert_eq!(
ShiftSessionManager::select_validators(0, &[acc1.clone()]),
vec![acc1.clone()],
);
// at session#0, shift is also 0
assert_eq!(
ShiftSessionManager::select_validators(0, &all_accs),
vec![acc1.clone(), acc2.clone(), acc3.clone()],
);
// at session#1, shift is also 1
assert_eq!(
ShiftSessionManager::select_validators(1, &all_accs),
vec![acc2.clone(), acc3.clone(), acc4.clone()],
);
// at session#3, we're wrapping
assert_eq!(
ShiftSessionManager::select_validators(3, &all_accs),
vec![acc4, acc5, acc1.clone()],
);
// at session#5, we're starting from the beginning again
assert_eq!(
ShiftSessionManager::select_validators(5, &all_accs),
vec![acc1, acc2, acc3],
);
}
}