diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 03e76eb979..592b39e08c 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -2296,6 +2296,7 @@ dependencies = [ "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", + "srml-authority-discovery 0.1.0", "srml-balances 2.0.0", "srml-contracts 2.0.0", "srml-finality-tracker 2.0.0", @@ -2419,6 +2420,7 @@ dependencies = [ "sr-staking-primitives 2.0.0", "sr-std 2.0.0", "sr-version 2.0.0", + "srml-authority-discovery 0.1.0", "srml-authorship 0.1.0", "srml-babe 2.0.0", "srml-balances 2.0.0", @@ -2440,9 +2442,9 @@ dependencies = [ "srml-system 2.0.0", "srml-timestamp 2.0.0", "srml-treasury 2.0.0", + "substrate-authority-discovery-primitives 2.0.0", "substrate-client 2.0.0", "substrate-consensus-babe-primitives 2.0.0", - "substrate-consensus-common-primitives 2.0.0", "substrate-keyring 2.0.0", "substrate-offchain-primitives 2.0.0", "substrate-primitives 2.0.0", @@ -2503,7 +2505,6 @@ dependencies = [ "srml-timestamp 2.0.0", "substrate-client 2.0.0", "substrate-consensus-babe-primitives 2.0.0", - "substrate-consensus-common-primitives 2.0.0", "substrate-offchain-primitives 2.0.0", "substrate-primitives 2.0.0", "substrate-session 2.0.0", @@ -3805,6 +3806,23 @@ dependencies = [ "substrate-primitives 2.0.0", ] +[[package]] +name = "srml-authority-discovery" +version = "0.1.0" +dependencies = [ + "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 2.0.0", + "sr-primitives 2.0.0", + "sr-std 2.0.0", + "srml-im-online 0.1.0", + "srml-session 2.0.0", + "srml-support 2.0.0", + "srml-system 2.0.0", + "substrate-application-crypto 2.0.0", + "substrate-primitives 2.0.0", +] + [[package]] name = "srml-authorship" version = "0.1.0" @@ -4381,6 +4399,16 @@ dependencies = [ "substrate-test-runtime-client 2.0.0", ] +[[package]] +name = "substrate-authority-discovery-primitives" +version = "2.0.0" +dependencies = [ + "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-primitives 2.0.0", + "sr-std 2.0.0", + "substrate-client 2.0.0", +] + [[package]] name = "substrate-basic-authorship" version = "2.0.0" @@ -4619,16 +4647,6 @@ dependencies = [ "substrate-test-runtime-client 2.0.0", ] -[[package]] -name = "substrate-consensus-common-primitives" -version = "2.0.0" -dependencies = [ - "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "sr-primitives 2.0.0", - "sr-std 2.0.0", - "substrate-client 2.0.0", -] - [[package]] name = "substrate-consensus-rhd" version = "2.0.0" diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml index eb75ac19f7..048bfb7629 100644 --- a/substrate/Cargo.toml +++ b/substrate/Cargo.toml @@ -82,6 +82,7 @@ members = [ "srml/generic-asset", "srml/grandpa", "srml/im-online", + "srml/authority-discovery", "srml/indices", "srml/membership", "srml/metadata", diff --git a/substrate/core/consensus/common/primitives/Cargo.toml b/substrate/core/authority-discovery/primitives/Cargo.toml similarity index 54% rename from substrate/core/consensus/common/primitives/Cargo.toml rename to substrate/core/authority-discovery/primitives/Cargo.toml index de59c3723d..aeae5cc61f 100644 --- a/substrate/core/consensus/common/primitives/Cargo.toml +++ b/substrate/core/authority-discovery/primitives/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "substrate-consensus-common-primitives" +name = "substrate-authority-discovery-primitives" version = "2.0.0" authors = ["Parity Technologies "] description = "Common consensus primitives" @@ -7,9 +7,9 @@ edition = "2018" [dependencies] codec = { package = "parity-scale-codec", default-features = false, version = "1.0.3" } -client = { package = "substrate-client", path = "../../../client", default-features = false } -sr-primitives = { path = "../../../sr-primitives", default-features = false } -rstd = { package = "sr-std", path = "../../../sr-std", default-features = false } +client = { package = "substrate-client", path = "../../client", default-features = false } +sr-primitives = { path = "../../sr-primitives", default-features = false } +rstd = { package = "sr-std", path = "../../sr-std", default-features = false } [features] default = ["std"] diff --git a/substrate/core/authority-discovery/primitives/src/lib.rs b/substrate/core/authority-discovery/primitives/src/lib.rs new file mode 100644 index 0000000000..556b758aa6 --- /dev/null +++ b/substrate/core/authority-discovery/primitives/src/lib.rs @@ -0,0 +1,49 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Runtime Api to help discover authorities. + +#![cfg_attr(not(feature = "std"), no_std)] + +use client::decl_runtime_apis; +use codec::Codec; +use rstd::vec::Vec; + +decl_runtime_apis! { + /// The authority discovery api. + /// + /// This api is used by the `core/authority-discovery` module to retrieve our + /// own authority identifier, to retrieve identifiers of the current authority + /// set, as well as sign and verify Kademlia Dht external address payloads + /// from and to other authorities. + pub trait AuthorityDiscoveryApi { + /// Returns own authority identifier iff it is part of the current authority + /// set, otherwise this function returns None. The restriction might be + /// softened in the future in case a consumer needs to learn own authority + /// identifier. + fn authority_id() -> Option; + + /// Retrieve authority identifiers of the current authority set. + fn authorities() -> Vec; + + /// Sign the given payload with the private key corresponding to the given authority id. + fn sign(payload: Vec, authority_id: AuthorityId) -> Option>; + + /// Verify the given signature for the given payload with the given + /// authority identifier. + fn verify(payload: Vec, signature: Vec, authority_id: AuthorityId) -> bool; + } +} diff --git a/substrate/core/consensus/common/primitives/src/lib.rs b/substrate/core/consensus/common/primitives/src/lib.rs deleted file mode 100644 index f6c1800081..0000000000 --- a/substrate/core/consensus/common/primitives/src/lib.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Common consensus primitives. - -#![cfg_attr(not(feature = "std"), no_std)] - -use codec::Codec; -use client::decl_runtime_apis; -use rstd::vec::Vec; - -decl_runtime_apis! { - /// Common consensus runtime api. - pub trait ConsensusApi { - /// Returns the set of authorities of the currently active consensus mechanism. - fn authorities() -> Vec; - } -} diff --git a/substrate/core/state-machine/src/testing.rs b/substrate/core/state-machine/src/testing.rs index 5026abcb2f..00a0208a1f 100644 --- a/substrate/core/state-machine/src/testing.rs +++ b/substrate/core/state-machine/src/testing.rs @@ -89,6 +89,11 @@ impl TestExternalities { self.offchain = Some(Box::new(offchain)); } + /// Set keystore. + pub fn set_keystore(&mut self, keystore: BareCryptoStorePtr) { + self.keystore = Some(keystore); + } + /// Get mutable reference to changes trie storage. pub fn changes_trie_storage(&mut self) -> &mut ChangesTrieInMemoryStorage { &mut self.changes_trie_storage diff --git a/substrate/node-template/runtime/Cargo.toml b/substrate/node-template/runtime/Cargo.toml index 54aee700f8..986feeb38c 100644 --- a/substrate/node-template/runtime/Cargo.toml +++ b/substrate/node-template/runtime/Cargo.toml @@ -25,7 +25,6 @@ timestamp = { package = "srml-timestamp", path = "../../srml/timestamp", default sudo = { package = "srml-sudo", path = "../../srml/sudo", default_features = false } sr-primitives = { path = "../../core/sr-primitives", default_features = false } client = { package = "substrate-client", path = "../../core/client", default_features = false } -consensus-primitives = { package = "substrate-consensus-common-primitives", path = "../../core/consensus/common/primitives", default-features = false } offchain-primitives = { package = "substrate-offchain-primitives", path = "../../core/offchain/primitives", default-features = false } [build-dependencies] @@ -53,7 +52,6 @@ std = [ "version/std", "serde", "safe-mix/std", - "consensus-primitives/std", "offchain-primitives/std", "substrate-session/std", ] diff --git a/substrate/node-template/runtime/src/lib.rs b/substrate/node-template/runtime/src/lib.rs index 9f520971ad..6a73a47d36 100644 --- a/substrate/node-template/runtime/src/lib.rs +++ b/substrate/node-template/runtime/src/lib.rs @@ -400,12 +400,6 @@ impl_runtime_apis! { } } - impl consensus_primitives::ConsensusApi for Runtime { - fn authorities() -> Vec { - Babe::authorities().into_iter().map(|(a, _)| a).collect() - } - } - impl substrate_session::SessionKeys for Runtime { fn generate_session_keys(seed: Option>) -> Vec { let seed = seed.as_ref().map(|s| rstd::str::from_utf8(&s).expect("Seed is an utf8 string")); diff --git a/substrate/node/cli/Cargo.toml b/substrate/node/cli/Cargo.toml index c8aaf576dc..7b4ebb0c5f 100644 --- a/substrate/node/cli/Cargo.toml +++ b/substrate/node/cli/Cargo.toml @@ -46,6 +46,7 @@ system = { package = "srml-system", path = "../../srml/system" } balances = { package = "srml-balances", path = "../../srml/balances" } support = { package = "srml-support", path = "../../srml/support", default-features = false } im_online = { package = "srml-im-online", path = "../../srml/im-online", default-features = false } +authority-discovery = { package = "srml-authority-discovery", path = "../../srml/authority-discovery", default-features = false } [dev-dependencies] keystore = { package = "substrate-keystore", path = "../../core/keystore" } diff --git a/substrate/node/cli/src/chain_spec.rs b/substrate/node/cli/src/chain_spec.rs index fe8df33f79..fca4c78b89 100644 --- a/substrate/node/cli/src/chain_spec.rs +++ b/substrate/node/cli/src/chain_spec.rs @@ -19,10 +19,9 @@ use primitives::{Pair, Public, crypto::UncheckedInto}; pub use node_primitives::{AccountId, Balance}; use node_runtime::{ - BabeConfig, BalancesConfig, ContractsConfig, CouncilConfig, DemocracyConfig, - ElectionsConfig, GrandpaConfig, ImOnlineConfig, IndicesConfig, - SessionConfig, SessionKeys, StakerStatus, StakingConfig, SudoConfig, SystemConfig, - TechnicalCommitteeConfig, WASM_BINARY, + AuthorityDiscoveryConfig, BabeConfig, BalancesConfig, ContractsConfig, CouncilConfig, DemocracyConfig, + ElectionsConfig, GrandpaConfig, ImOnlineConfig, IndicesConfig, SessionConfig, SessionKeys, StakerStatus, + StakingConfig, SudoConfig, SystemConfig, TechnicalCommitteeConfig, WASM_BINARY, }; use node_runtime::constants::{time::*, currency::*}; pub use node_runtime::GenesisConfig; @@ -171,6 +170,9 @@ fn staging_testnet_config_genesis() -> GenesisConfig { im_online: Some(ImOnlineConfig { keys: vec![], }), + authority_discovery: Some(AuthorityDiscoveryConfig{ + keys: vec![], + }), grandpa: Some(GrandpaConfig { authorities: vec![], }), @@ -302,6 +304,9 @@ pub fn testnet_genesis( im_online: Some(ImOnlineConfig { keys: vec![], }), + authority_discovery: Some(AuthorityDiscoveryConfig{ + keys: vec![], + }), grandpa: Some(GrandpaConfig { authorities: vec![], }), diff --git a/substrate/node/runtime/Cargo.toml b/substrate/node/runtime/Cargo.toml index 188d0c6f97..9424788e2f 100644 --- a/substrate/node/runtime/Cargo.toml +++ b/substrate/node/runtime/Cargo.toml @@ -15,12 +15,12 @@ rstd = { package = "sr-std", path = "../../core/sr-std", default-features = fals sr-primitives = { path = "../../core/sr-primitives", default-features = false } sr-staking-primitives = { path = "../../core/sr-staking-primitives", default-features = false } offchain-primitives = { package = "substrate-offchain-primitives", path = "../../core/offchain/primitives", default-features = false } +authority-discovery-primitives = { package = "substrate-authority-discovery-primitives", path = "../../core/authority-discovery/primitives", default-features = false } version = { package = "sr-version", path = "../../core/sr-version", default-features = false } support = { package = "srml-support", path = "../../srml/support", default-features = false } authorship = { package = "srml-authorship", path = "../../srml/authorship", default-features = false } babe = { package = "srml-babe", path = "../../srml/babe", default-features = false } babe-primitives = { package = "substrate-consensus-babe-primitives", path = "../../core/consensus/babe/primitives", default-features = false } -consensus-primitives = { package = "substrate-consensus-common-primitives", path = "../../core/consensus/common/primitives", default-features = false } balances = { package = "srml-balances", path = "../../srml/balances", default-features = false } contracts = { package = "srml-contracts", path = "../../srml/contracts", default-features = false } collective = { package = "srml-collective", path = "../../srml/collective", default-features = false } @@ -38,6 +38,7 @@ timestamp = { package = "srml-timestamp", path = "../../srml/timestamp", default treasury = { package = "srml-treasury", path = "../../srml/treasury", default-features = false } sudo = { package = "srml-sudo", path = "../../srml/sudo", default-features = false } im-online = { package = "srml-im-online", path = "../../srml/im-online", default-features = false } +authority-discovery = { package = "srml-authority-discovery", path = "../../srml/authority-discovery", default-features = false } offences = { package = "srml-offences", path = "../../srml/offences", default-features = false } node-primitives = { path = "../primitives", default-features = false } rustc-hex = { version = "2.0", optional = true } @@ -54,6 +55,8 @@ no_std = [ "contracts/core", ] std = [ + "authority-discovery-primitives/std", + "authority-discovery/std", "authorship/std", "babe-primitives/std", "babe/std", @@ -61,7 +64,6 @@ std = [ "client/std", "codec/std", "collective/std", - "consensus-primitives/std", "contracts/std", "democracy/std", "elections/std", diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs index 7507609b3b..b1e4065b5f 100644 --- a/substrate/node/runtime/src/lib.rs +++ b/substrate/node/runtime/src/lib.rs @@ -80,8 +80,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to equal spec_version. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 147, - impl_version: 150, + spec_version: 148, + impl_version: 148, apis: RUNTIME_API_VERSIONS, }; @@ -407,6 +407,8 @@ impl offences::Trait for Runtime { type OnOffenceHandler = Staking; } +impl authority_discovery::Trait for Runtime {} + impl grandpa::Trait for Runtime { type Event = Event; } @@ -447,6 +449,7 @@ construct_runtime!( Contracts: contracts, Sudo: sudo, ImOnline: im_online::{Module, Call, Storage, Event, ValidateUnsigned, Config}, + AuthorityDiscovery: authority_discovery::{Module, Call, Config}, Offences: offences::{Module, Call, Storage, Event}, } ); @@ -576,15 +579,26 @@ impl_runtime_apis! { } } - impl node_primitives::AccountNonceApi for Runtime { - fn account_nonce(account: AccountId) -> Index { - System::account_nonce(account) + impl authority_discovery_primitives::AuthorityDiscoveryApi for Runtime { + fn authority_id() -> Option { + AuthorityDiscovery::authority_id() + } + fn authorities() -> Vec { + AuthorityDiscovery::authorities() + } + + fn sign(payload: Vec, authority_id: im_online::AuthorityId) -> Option> { + AuthorityDiscovery::sign(payload, authority_id) + } + + fn verify(payload: Vec, signature: Vec, public_key: im_online::AuthorityId) -> bool { + AuthorityDiscovery::verify(payload, signature, public_key) } } - impl consensus_primitives::ConsensusApi for Runtime { - fn authorities() -> Vec { - Babe::authorities().into_iter().map(|(a, _)| a).collect() + impl node_primitives::AccountNonceApi for Runtime { + fn account_nonce(account: AccountId) -> Index { + System::account_nonce(account) } } diff --git a/substrate/node/testing/src/genesis.rs b/substrate/node/testing/src/genesis.rs index a7df99da14..35ff93d1a6 100644 --- a/substrate/node/testing/src/genesis.rs +++ b/substrate/node/testing/src/genesis.rs @@ -89,6 +89,7 @@ pub fn config(support_changes_trie: bool, code: Option<&[u8]>) -> GenesisConfig authorities: vec![], }), im_online: Some(Default::default()), + authority_discovery: Some(Default::default()), democracy: Some(Default::default()), collective_Instance1: Some(Default::default()), collective_Instance2: Some(Default::default()), diff --git a/substrate/srml/authority-discovery/Cargo.toml b/substrate/srml/authority-discovery/Cargo.toml new file mode 100644 index 0000000000..5d52bdb246 --- /dev/null +++ b/substrate/srml/authority-discovery/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "srml-authority-discovery" +version = "0.1.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } +sr-primitives = { path = "../../core/sr-primitives", default-features = false } +primitives = { package = "substrate-primitives", path = "../../core/primitives", default-features = false } +app-crypto = { package = "substrate-application-crypto", path = "../../core/application-crypto", default-features = false } +rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } +serde = { version = "1.0", optional = true } +session = { package = "srml-session", path = "../session", default-features = false } +im-online = { package = "srml-im-online", path = "../im-online", default-features = false } +srml-support = { path = "../support", default-features = false } +sr-io = { package = "sr-io", path = "../../core/sr-io", default-features = false } +system = { package = "srml-system", path = "../system", default-features = false } + +[features] +default = ["std"] +std = [ + "codec/std", + "sr-primitives/std", + "primitives/std", + "rstd/std", + "serde", + "session/std", + "im-online/std", + "srml-support/std", + "sr-io/std", + "system/std", + "app-crypto/std", +] diff --git a/substrate/srml/authority-discovery/src/lib.rs b/substrate/srml/authority-discovery/src/lib.rs new file mode 100644 index 0000000000..00912aeffe --- /dev/null +++ b/substrate/srml/authority-discovery/src/lib.rs @@ -0,0 +1,349 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! # Authority discovery module. +//! +//! This module is used by the `core/authority-discovery` to retrieve the +//! current set of authorities, learn its own authority id as well as sign and +//! verify messages to and from other authorities. +//! +//! ## Dependencies +//! +//! This module depends on the [I’m online module](../srml_im_online/index.html) +//! using its session key. + +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +use app_crypto::RuntimeAppPublic; +use codec::{Decode, Encode}; +use rstd::prelude::*; +use srml_support::{decl_module, decl_storage, StorageValue}; + +pub trait Trait: system::Trait + session::Trait {} + +decl_storage! { + trait Store for Module as AuthorityDiscovery { + /// The current set of keys that may issue a heartbeat. + Keys get(keys): Vec; + } + add_extra_genesis { + config(keys): Vec; + build(| + storage: &mut (sr_primitives::StorageOverlay, sr_primitives::ChildrenStorageOverlay), + config: &GenesisConfig + | { + sr_io::with_storage( + storage, + || Module::::initialize_keys(&config.keys), + ); + }) + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + } +} + +impl Module { + /// Returns own authority identifier iff it is part of the current authority + /// set, otherwise this function returns None. The restriction might be + /// softened in the future in case a consumer needs to learn own authority + /// identifier. + pub fn authority_id() -> Option { + let authorities = Keys::get(); + + let local_keys = im_online::AuthorityId::all(); + + authorities.into_iter().find_map(|authority| { + if local_keys.contains(&authority) { + Some(authority) + } else { + None + } + }) + } + + /// Retrieve authority identifiers of the current authority set. + pub fn authorities() -> Vec { + Keys::get() + } + + /// Sign the given payload with the private key corresponding to the given authority id. + pub fn sign(payload: Vec, authority_id: im_online::AuthorityId) -> Option> { + authority_id.sign(&payload).map(|s| s.encode()) + } + + /// Verify the given signature for the given payload with the given + /// authority identifier. + pub fn verify( + payload: Vec, + signature: Vec, + authority_id: im_online::AuthorityId, + ) -> bool { + im_online::AuthoritySignature::decode(&mut &signature[..]) + .map(|s| authority_id.verify(&payload, &s)) + .unwrap_or(false) + } + + fn initialize_keys(keys: &[im_online::AuthorityId]) { + if !keys.is_empty() { + assert!(Keys::get().is_empty(), "Keys are already initialized!"); + Keys::put_ref(keys); + } + } +} + +impl session::OneSessionHandler for Module { + type Key = im_online::AuthorityId; + + fn on_genesis_session<'a, I: 'a>(validators: I) + where + I: Iterator, + { + let keys = validators.map(|x| x.1).collect::>(); + Self::initialize_keys(&keys); + } + + fn on_new_session<'a, I: 'a>(_changed: bool, _validators: I, next_validators: I) + where + I: Iterator, + { + // Remember who the authorities are for the new session. + Keys::put(next_validators.map(|x| x.1).collect::>()); + } + + fn on_disabled(_i: usize) { + // ignore + } +} + +#[cfg(test)] +mod tests { + use super::*; + use app_crypto::Pair; + use primitives::testing::KeyStore; + use primitives::{crypto::key_types, sr25519, traits::BareCryptoStore, H256}; + use sr_io::{with_externalities, TestExternalities}; + use sr_primitives::testing::{Header, UintAuthorityId}; + use sr_primitives::traits::{ConvertInto, IdentityLookup, OpaqueKeys}; + use sr_primitives::Perbill; + use srml_support::{impl_outer_origin, parameter_types}; + + type AuthorityDiscovery = Module; + type SessionIndex = u32; + + #[derive(Clone, Eq, PartialEq)] + pub struct Test; + impl Trait for Test {} + + pub struct TestOnSessionEnding; + impl session::OnSessionEnding for TestOnSessionEnding { + fn on_session_ending( + _: SessionIndex, + _: SessionIndex, + ) -> Option> { + None + } + } + + impl session::Trait for Test { + type OnSessionEnding = TestOnSessionEnding; + type Keys = UintAuthorityId; + type ShouldEndSession = session::PeriodicSessions; + type SessionHandler = TestSessionHandler; + type Event = (); + type ValidatorId = im_online::AuthorityId; + type ValidatorIdOf = ConvertInto; + type SelectInitialValidators = (); + } + + pub type BlockNumber = u64; + + parameter_types! { + pub const Period: BlockNumber = 1; + pub const Offset: BlockNumber = 0; + pub const UncleGenerations: u64 = 0; + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); + } + + impl system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = BlockNumber; + type Call = (); + type Hash = H256; + type Hashing = ::sr_primitives::traits::BlakeTwo256; + type AccountId = im_online::AuthorityId; + type Lookup = IdentityLookup; + type Header = Header; + type WeightMultiplierUpdate = (); + type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type AvailableBlockRatio = AvailableBlockRatio; + type MaximumBlockLength = MaximumBlockLength; + type Version = (); + } + + impl_outer_origin! { + pub enum Origin for Test {} + } + + pub struct TestSessionHandler; + impl session::SessionHandler for TestSessionHandler { + fn on_new_session( + _changed: bool, + _validators: &[(im_online::AuthorityId, Ks)], + _queued_validators: &[(im_online::AuthorityId, Ks)], + ) { + } + + fn on_disabled(_validator_index: usize) {} + + fn on_genesis_session(_validators: &[(im_online::AuthorityId, Ks)]) {} + } + + #[test] + fn authority_id_fn_returns_intersection_of_current_authorities_and_keys_in_key_store() { + // Create keystore and generate key. + let key_store = KeyStore::new(); + key_store + .write() + .sr25519_generate_new(key_types::IM_ONLINE, None) + .expect("Generates key."); + + // Retrieve key to later check if we got the right one. + let public_key = key_store + .read() + .sr25519_public_keys(key_types::IM_ONLINE) + .pop() + .unwrap(); + let authority_id = im_online::AuthorityId::from(public_key); + + // Build genesis. + let mut t = system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + GenesisConfig { + keys: vec![authority_id.clone()], + } + .assimilate_storage::(&mut t) + .unwrap(); + + // Create externalities. + let mut externalities = TestExternalities::new(t); + externalities.set_keystore(key_store); + + with_externalities(&mut externalities, || { + assert_eq!( + authority_id, + AuthorityDiscovery::authority_id().expect("Retrieving public key.") + ); + }); + } + + #[test] + fn authority_id_fn_does_not_return_key_outside_current_authority_set() { + // Create keystore and generate key. + let key_store = KeyStore::new(); + key_store + .write() + .sr25519_generate_new(key_types::IM_ONLINE, None) + .expect("Generates key."); + + // Build genesis. + let mut t = system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + // Generate random authority set. + let keys = vec![(); 5] + .iter() + .map(|_x| sr25519::Pair::generate_with_phrase(None).0.public()) + .map(im_online::AuthorityId::from) + .collect(); + + GenesisConfig { keys: keys } + .assimilate_storage::(&mut t) + .unwrap(); + + // Create externalities. + let mut externalities = TestExternalities::new(t); + externalities.set_keystore(key_store); + + with_externalities(&mut externalities, || { + assert_eq!(None, AuthorityDiscovery::authority_id()); + }); + } + + #[test] + fn sign_and_verify_workflow() { + // Create keystore and generate key. + let key_store = KeyStore::new(); + key_store + .write() + .sr25519_generate_new(key_types::IM_ONLINE, None) + .expect("Generates key."); + + // Retrieve key to later check if we got the right one. + let public_key = key_store + .read() + .sr25519_public_keys(key_types::IM_ONLINE) + .pop() + .unwrap(); + let authority_id = im_online::AuthorityId::from(public_key); + + // Build genesis. + let mut t = system::GenesisConfig::default() + .build_storage::() + .unwrap(); + + GenesisConfig { + keys: vec![authority_id.clone()], + } + .assimilate_storage::(&mut t) + .unwrap(); + + // Create externalities. + let mut externalities = TestExternalities::new(t); + externalities.set_keystore(key_store); + + with_externalities(&mut externalities, || { + let authority_id = AuthorityDiscovery::authority_id().expect("authority id"); + let payload = String::from("test payload").into_bytes(); + let sig = + AuthorityDiscovery::sign(payload.clone(), authority_id.clone()).expect("signature"); + + assert!(AuthorityDiscovery::verify( + payload, + sig.clone(), + authority_id.clone() + )); + + assert!(!AuthorityDiscovery::verify( + String::from("other payload").into_bytes(), + sig, + authority_id + )) + }); + } +}