diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index aac1b6aa26..a218c5638e 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -2274,8 +2274,8 @@ dependencies = [ "substrate-basic-authorship 2.0.0", "substrate-cli 2.0.0", "substrate-client 2.0.0", - "substrate-consensus-aura 2.0.0", - "substrate-consensus-aura-primitives 2.0.0", + "substrate-consensus-babe 2.0.0", + "substrate-consensus-babe-primitives 2.0.0", "substrate-consensus-common 2.0.0", "substrate-finality-grandpa 2.0.0", "substrate-finality-grandpa-primitives 2.0.0", @@ -2360,8 +2360,8 @@ dependencies = [ "sr-primitives 2.0.0", "sr-std 2.0.0", "sr-version 2.0.0", - "srml-aura 2.0.0", "srml-authorship 0.1.0", + "srml-babe 2.0.0", "srml-balances 2.0.0", "srml-collective 2.0.0", "srml-contracts 2.0.0", @@ -2380,7 +2380,7 @@ dependencies = [ "srml-timestamp 2.0.0", "srml-treasury 2.0.0", "substrate-client 2.0.0", - "substrate-consensus-aura-primitives 2.0.0", + "substrate-consensus-babe-primitives 2.0.0", "substrate-keyring 2.0.0", "substrate-offchain-primitives 2.0.0", "substrate-primitives 2.0.0", @@ -2460,6 +2460,15 @@ dependencies = [ "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "num-bigint" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-integer" version = "0.1.41" @@ -2469,6 +2478,17 @@ dependencies = [ "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "num-rational" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-traits" version = "0.2.8" @@ -4335,6 +4355,9 @@ dependencies = [ "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "merlin 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 4.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -6153,7 +6176,9 @@ dependencies = [ "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum nohash-hasher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0d138afcce92d219ccb6eb53d9b1e8a96ac0d633cfd3c53cd9856d96d1741bb8" "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +"checksum num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "57450397855d951f1a41305e54851b1a7b8f5d2e349543a02a2effe25459f718" "checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" +"checksum num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2885278d5fe2adc2f75ced642d52d879bffaceb5a2e0b1d4309ffdfb239b454" "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" "checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" "checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" diff --git a/substrate/core/cli/src/params.rs b/substrate/core/cli/src/params.rs index 78899ccd4c..499c8b9eca 100644 --- a/substrate/core/cli/src/params.rs +++ b/substrate/core/cli/src/params.rs @@ -431,15 +431,15 @@ struct KeyringTestAccountCliValues { help: String, conflicts_with: Vec, name: String, - variant: keyring::AuthorityKeyring, + variant: keyring::Sr25519Keyring, } lazy_static::lazy_static! { /// The Cli values for all test accounts. static ref TEST_ACCOUNTS_CLI_VALUES: Vec = { - keyring::AuthorityKeyring::iter().map(|a| { + keyring::Sr25519Keyring::iter().map(|a| { let help = format!("Shortcut for `--key //{} --name {}`.", a, a); - let conflicts_with = keyring::AuthorityKeyring::iter() + let conflicts_with = keyring::Sr25519Keyring::iter() .filter(|b| a != *b) .map(|b| b.to_string().to_lowercase()) .chain(["name", "key"].iter().map(ToString::to_string)) @@ -459,7 +459,7 @@ lazy_static::lazy_static! { /// Wrapper for exposing the keyring test accounts into the Cli. #[derive(Debug, Clone)] pub struct Keyring { - pub account: Option, + pub account: Option, } impl StructOpt for Keyring { diff --git a/substrate/core/client/src/genesis.rs b/substrate/core/client/src/genesis.rs index 8843ec84bc..3451e1e9b1 100644 --- a/substrate/core/client/src/genesis.rs +++ b/substrate/core/client/src/genesis.rs @@ -47,7 +47,7 @@ mod tests { use test_client::{ runtime::genesismap::{GenesisConfig, additional_storage_with_genesis}, runtime::{Hash, Transfer, Block, BlockNumber, Header, Digest}, - AccountKeyring, AuthorityKeyring + AccountKeyring, Sr25519Keyring, }; use runtime_primitives::traits::BlakeTwo256; use primitives::Blake2Hasher; @@ -147,7 +147,7 @@ mod tests { #[test] fn construct_genesis_should_work_with_native() { let mut storage = GenesisConfig::new(false, - vec![AuthorityKeyring::One.into(), AuthorityKeyring::Two.into()], + vec![Sr25519Keyring::One.into(), Sr25519Keyring::Two.into()], vec![AccountKeyring::One.into(), AccountKeyring::Two.into()], 1000 ).genesis_map(); @@ -176,7 +176,7 @@ mod tests { #[test] fn construct_genesis_should_work_with_wasm() { let mut storage = GenesisConfig::new(false, - vec![AuthorityKeyring::One.into(), AuthorityKeyring::Two.into()], + vec![Sr25519Keyring::One.into(), Sr25519Keyring::Two.into()], vec![AccountKeyring::One.into(), AccountKeyring::Two.into()], 1000 ).genesis_map(); @@ -205,7 +205,7 @@ mod tests { #[test] fn construct_genesis_with_bad_transaction_should_panic() { let mut storage = GenesisConfig::new(false, - vec![AuthorityKeyring::One.into(), AuthorityKeyring::Two.into()], + vec![Sr25519Keyring::One.into(), Sr25519Keyring::Two.into()], vec![AccountKeyring::One.into(), AccountKeyring::Two.into()], 68 ).genesis_map(); diff --git a/substrate/core/consensus/babe/Cargo.toml b/substrate/core/consensus/babe/Cargo.toml index 8b473932dd..516ac1a40d 100644 --- a/substrate/core/consensus/babe/Cargo.toml +++ b/substrate/core/consensus/babe/Cargo.toml @@ -7,8 +7,11 @@ edition = "2018" [dependencies] parity-codec = { version = "4.1.1", features = ["derive"] } -babe_primitives = { package = "substrate-consensus-babe-primitives", path = "primitives" } +babe-primitives = { package = "substrate-consensus-babe-primitives", path = "primitives" } primitives = { package = "substrate-primitives", path = "../../primitives" } +num-bigint = "0.2" +num-rational = "0.2" +num-traits = "0.2" runtime_support = { package = "srml-support", path = "../../../srml/support" } runtime_version = { package = "sr-version", path = "../../sr-version" } runtime_io = { package = "sr-io", path = "../../sr-io" } @@ -37,3 +40,6 @@ service = { package = "substrate-service", path = "../../service" } test-client = { package = "substrate-test-runtime-client", path = "../../test-runtime/client" } tokio = "0.1.18" env_logger = "0.6.1" + +[features] +test-helpers = [] diff --git a/substrate/core/consensus/babe/primitives/src/lib.rs b/substrate/core/consensus/babe/primitives/src/lib.rs index a7b49364f4..b6e964d27f 100644 --- a/substrate/core/consensus/babe/primitives/src/lib.rs +++ b/substrate/core/consensus/babe/primitives/src/lib.rs @@ -24,16 +24,21 @@ mod digest; use parity_codec::{Encode, Decode}; use rstd::vec::Vec; use runtime_primitives::ConsensusEngineId; -use substrate_primitives::sr25519::Public; +use substrate_primitives::sr25519; use substrate_client::decl_runtime_apis; #[cfg(feature = "std")] pub use digest::{BabePreDigest, CompatibleDigestItem}; pub use digest::{BABE_VRF_PREFIX, RawBabePreDigest}; +/// A Babe authority keypair. Necessarily equivalent to the schnorrkel public key used in +/// the main Babe module. If that ever changes, then this must, too. +#[cfg(feature = "std")] +pub type AuthorityPair = sr25519::Pair; + /// A Babe authority identifier. Necessarily equivalent to the schnorrkel public key used in /// the main Babe module. If that ever changes, then this must, too. -pub type AuthorityId = Public; +pub type AuthorityId = sr25519::Public; /// The `ConsensusEngineId` of BABE. pub const BABE_ENGINE_ID: ConsensusEngineId = *b"BABE"; @@ -54,20 +59,24 @@ pub type AuthorityIndex = u64; pub type SlotNumber = u64; /// The weight of an authority. -pub type Weight = u64; +// NOTE: we use a unique name for the weight to avoid conflicts with other +// `Weight` types, since the metadata isn't able to disambiguate. +pub type BabeWeight = u64; /// BABE epoch information #[derive(Decode, Encode, Default, PartialEq, Eq, Clone)] #[cfg_attr(any(feature = "std", test), derive(Debug))] pub struct Epoch { - /// The authorities and their weights - pub authorities: Vec<(AuthorityId, Weight)>, /// The epoch index pub epoch_index: u64, - /// Randomness for this epoch - pub randomness: [u8; VRF_OUTPUT_LENGTH], + /// The starting slot of the epoch, + pub start_slot: u64, /// The duration of this epoch pub duration: SlotNumber, + /// The authorities and their weights + pub authorities: Vec<(AuthorityId, BabeWeight)>, + /// Randomness for this epoch + pub randomness: [u8; VRF_OUTPUT_LENGTH], } /// An consensus log item for BABE. @@ -93,23 +102,10 @@ pub struct BabeConfiguration { /// Dynamic slot duration may be supported in the future. pub slot_duration: u64, - /// The number of slots per BABE epoch. Currently, only - /// the value provided by this type at genesis will be used. - /// - /// Dynamic slot duration may be supported in the future. - pub slots_per_epoch: u64, - - /// The expected block time in milliseconds for BABE. Currently, - /// only the value provided by this type at genesis will be used. - /// - /// Dynamic expected block time may be supported in the future. - pub expected_block_time: u64, - - /// The maximum permitted VRF output, or *threshold*, for BABE. Currently, - /// only the value provided by this type at genesis will be used. - /// - /// Dynamic thresholds may be supported in the future. - pub threshold: u64, + /// A constant value that is used in the threshold calculation formula. + /// Expressed as a fraction where the first member of the tuple is the + /// numerator and the second is the denominator. + pub c: (u64, u64), /// The minimum number of blocks that must be received before running the /// median algorithm to compute the offset between the on-chain time and the diff --git a/substrate/core/consensus/babe/src/lib.rs b/substrate/core/consensus/babe/src/lib.rs index be5b476dd2..4068ff633a 100644 --- a/substrate/core/consensus/babe/src/lib.rs +++ b/substrate/core/consensus/babe/src/lib.rs @@ -27,7 +27,7 @@ use consensus_common::import_queue::{ BoxJustificationImport, BoxFinalityProofImport, }; use consensus_common::well_known_cache_keys::Id as CacheKeyId; -use runtime_primitives::{generic, generic::BlockId, Justification}; +use runtime_primitives::{generic, generic::{BlockId, OpaqueDigestItemId}, Justification}; use runtime_primitives::traits::{ Block as BlockT, Header, DigestItemFor, NumberFor, ProvideRuntimeApi, SimpleBitOps, Zero, @@ -114,9 +114,9 @@ impl Config { self.0.slot_duration } - /// Retrieve the threshold for BABE - pub fn threshold(&self) -> u64 { - self.0.threshold + /// Retrieve the threshold calculation constant `c`. + pub fn c(&self) -> (u64, u64) { + self.0.c } } @@ -204,7 +204,7 @@ pub fn start_babe(BabeParams { local_key, sync_oracle: sync_oracle.clone(), force_authoring, - threshold: config.threshold(), + c: config.c(), }; register_babe_inherent_data_provider(&inherent_data_providers, config.0.slot_duration())?; Ok(slots::start_slot_worker( @@ -224,7 +224,7 @@ struct BabeWorker { local_key: Arc, sync_oracle: SO, force_authoring: bool, - threshold: u64, + c: (u64, u64), } impl SlotWorker for BabeWorker where @@ -273,7 +273,7 @@ impl SlotWorker for BabeWorker w } }; - let Epoch { ref authorities, randomness, epoch_index, .. } = epoch; + let Epoch { ref authorities, .. } = epoch; if authorities.is_empty() { error!(target: "babe", "No authorities at block {:?}", chain_head.hash()); @@ -287,14 +287,14 @@ impl SlotWorker for BabeWorker w return Box::new(future::ok(())); } - let proposal_work = if let Some(((inout, vrf_proof, _batchable_proof), authority_index)) = claim_slot( - &randomness, + let proposal_work = if let Some(claim) = claim_slot( slot_info.number, - epoch_index, epoch, &pair, - self.threshold, + self.c, ) { + let ((inout, vrf_proof, _batchable_proof), authority_index) = claim; + debug!( target: "babe", "Starting authorship at slot {}; timestamp = {}", slot_number, @@ -308,7 +308,11 @@ impl SlotWorker for BabeWorker w let proposer = match env.init(&chain_head) { Ok(p) => p, Err(e) => { - warn!(target: "babe", "Unable to author block in slot {:?}: {:?}", slot_number, e); + warn!(target: "babe", + "Unable to author block in slot {:?}: {:?}", + slot_number, + e, + ); telemetry!(CONSENSUS_WARN; "babe.unable_authoring_block"; "slot" => slot_number, "err" => ?e ); @@ -367,11 +371,6 @@ impl SlotWorker for BabeWorker w let signature = pair.sign(header_hash.as_ref()); let signature_digest_item = DigestItemFor::::babe_seal(signature); - let cache = find_epoch_digest::(&header) - .map(|epoch| vec![(well_known_cache_keys::AUTHORITIES, epoch.encode())]) - .map(|keys| keys.into_iter().collect()) - .unwrap_or_default(); - let import_block = BlockImportParams:: { origin: BlockOrigin::Own, header, @@ -396,7 +395,7 @@ impl SlotWorker for BabeWorker w "hash_previously" => ?header_hash, ); - if let Err(e) = block_import.lock().import_block(import_block, cache) { + if let Err(e) = block_import.lock().import_block(import_block, Default::default()) { warn!(target: "babe", "Error with block built on {:?}: {:?}", parent_hash, e); telemetry!(CONSENSUS_WARN; "babe.err_with_block_built_on"; @@ -423,26 +422,34 @@ macro_rules! babe_err { fn find_pre_digest(header: &B::Header) -> Result where DigestItemFor: CompatibleDigestItem, { + let mut pre_digest: Option<_> = None; for log in header.digest().logs() { - if let Some(pre_digest) = log.as_babe_pre_digest() { - return Ok(pre_digest); + trace!(target: "babe", "Checking log {:?}, looking for pre runtime digest", log); + match (log.as_babe_pre_digest(), pre_digest.is_some()) { + (Some(_), true) => Err(babe_err!("Multiple BABE pre-runtime digests, rejecting!"))?, + (None, _) => trace!(target: "babe", "Ignoring digest not meant for us"), + (s, false) => pre_digest = s, } } - - Err(babe_err!("No BABE pre-runtime digest found")) + pre_digest.ok_or_else(|| babe_err!("No BABE pre-runtime digest found")) } /// Extract the BABE epoch change digest from the given header, if it exists. -fn find_epoch_digest(header: &B::Header) -> Option +fn find_next_epoch_digest(header: &B::Header) -> Result, String> where DigestItemFor: CompatibleDigestItem, { + let mut epoch_digest: Option<_> = None; for log in header.digest().logs() { - if let Some(epoch_digest) = log.as_babe_epoch() { - return Some(epoch_digest); + trace!(target: "babe", "Checking log {:?}, looking for epoch change digest.", log); + let log = log.try_to::(OpaqueDigestItemId::Consensus(&BABE_ENGINE_ID)); + match (log, epoch_digest.is_some()) { + (Some(ConsensusLog::NextEpochData(_)), true) => Err(babe_err!("Multiple BABE epoch change digests, rejecting!"))?, + (Some(ConsensusLog::NextEpochData(epoch)), false) => epoch_digest = Some(epoch), + _ => trace!(target: "babe", "Ignoring digest not meant for us"), } } - return None; + Ok(epoch_digest) } /// Check a header has been signed by the right key. If the slot is too far in @@ -459,10 +466,10 @@ fn check_header( slot_now: u64, mut header: B::Header, hash: B::Hash, - authorities: &[AuthorityId], + authorities: &[(AuthorityId, BabeWeight)], randomness: [u8; 32], epoch_index: u64, - threshold: u64, + c: (u64, u64), ) -> Result, DigestItemFor)>, String> where DigestItemFor: CompatibleDigestItem, { @@ -486,7 +493,7 @@ fn check_header( } else if authority_index > authorities.len() as u64 { Err(babe_err!("Slot author not found")) } else { - let (pre_hash, author) = (header.hash(), &authorities[authority_index as usize]); + let (pre_hash, author) = (header.hash(), &authorities[authority_index as usize].0); if sr25519::Pair::verify(&sig, pre_hash, author.clone()) { let (inout, _batchable_proof) = { @@ -503,6 +510,7 @@ fn check_header( })? }; + let threshold = calculate_threshold(c, authorities, authority_index as usize); if !check(&inout, threshold) { return Err(babe_err!("VRF verification of block by author {:?} failed: \ threshold {} exceeded", author, threshold)); @@ -562,7 +570,9 @@ impl BabeVerifier { if !inherent_res.ok() { inherent_res .into_errors() - .try_for_each(|(i, e)| Err(self.inherent_data_providers.error_to_string(&i, &e))) + .try_for_each(|(i, e)| { + Err(self.inherent_data_providers.error_to_string(&i, &e)) + }) } else { Ok(()) } @@ -581,7 +591,9 @@ fn median_algorithm( let mut new_list: Vec<_> = time_source.1.iter().map(|&(t, sl)| { let offset: u128 = u128::from(slot_duration) .checked_mul(1_000_000u128) // self.config.get() returns *milliseconds* - .and_then(|x| x.checked_mul(u128::from(slot_number).saturating_sub(u128::from(sl)))) + .and_then(|x| { + x.checked_mul(u128::from(slot_number).saturating_sub(u128::from(sl))) + }) .expect("we cannot have timespans long enough for this to overflow; qed"); const NANOS_PER_SEC: u32 = 1_000_000_000; @@ -640,8 +652,6 @@ impl Verifier for BabeVerifier where epoch(self.api.as_ref(), &BlockId::Hash(parent_hash)) .map_err(|e| format!("Could not fetch epoch at {:?}: {:?}", parent_hash, e))?; - let authorities: Vec<_> = authorities.into_iter().map(|(s, _)| s).collect(); - // We add one to allow for some small drift. // FIXME #1019 in the future, alter this queue to allow deferring of headers let checked_header = check_header::( @@ -649,10 +659,10 @@ impl Verifier for BabeVerifier where slot_now + 1, header, hash, - &authorities[..], + &authorities, randomness, epoch_index, - self.config.threshold(), + self.config.c(), )?; match checked_header { @@ -683,12 +693,6 @@ impl Verifier for BabeVerifier where "babe.checked_and_importing"; "pre_header" => ?pre_header); - // `Consensus` is the Babe-specific authorities change log. - // It's an encoded `Epoch`, the same format as is stored in the - // cache, so no need to decode/re-encode. - let maybe_keys = find_epoch_digest::(&pre_header) - .map(|epoch| vec![(well_known_cache_keys::AUTHORITIES, epoch.encode())]); - let import_block = BlockImportParams { origin, header: pre_header, @@ -709,8 +713,7 @@ impl Verifier for BabeVerifier where &mut *self.time_source.0.lock(), ); - // FIXME #1019 extract authorities - Ok((import_block, maybe_keys)) + Ok((import_block, Default::default())) } CheckedHeader::Deferred(a, b) => { debug!(target: "babe", "Checking {:?} failed; {:?}, {:?}.", hash, a, b); @@ -732,7 +735,7 @@ fn epoch(client: &C, at: &BlockId) -> Result whe { client .cache() - .and_then(|cache| cache.get_at(&well_known_cache_keys::AUTHORITIES, at) + .and_then(|cache| cache.get_at(&well_known_cache_keys::EPOCH, at) .and_then(|v| Decode::decode(&mut &v[..]))) .or_else(|| { if client.runtime_api().has_api::>(at).unwrap_or(false) { @@ -786,8 +789,33 @@ fn make_transcript( transcript } -fn check(inout: &VRFInOut, threshold: u64) -> bool { - u64::from_le_bytes(inout.make_bytes::<[u8; 8]>(BABE_VRF_PREFIX)) < threshold +fn check(inout: &VRFInOut, threshold: u128) -> bool { + u128::from_le_bytes(inout.make_bytes::<[u8; 16]>(BABE_VRF_PREFIX)) < threshold +} + +fn calculate_threshold( + c: (u64, u64), + authorities: &[(AuthorityId, BabeWeight)], + authority_index: usize, +) -> u128 { + use num_bigint::BigUint; + use num_rational::BigRational; + use num_traits::{cast::ToPrimitive, identities::One}; + + let c = c.0 as f64 / c.1 as f64; + + let theta = + authorities[authority_index].1 as f64 / + authorities.iter().map(|(_, weight)| weight).sum::() as f64; + + let calc = || { + let p = BigRational::from_float(1f64 - (1f64 - c).powf(theta))?; + let numer = p.numer().to_biguint()?; + let denom = p.denom().to_biguint()?; + ((BigUint::one() << 128) * numer / denom).to_u128() + }; + + calc().unwrap_or(u128::max_value()) } /// Claim a slot if it is our turn. Returns `None` if it is not our turn. @@ -796,22 +824,20 @@ fn check(inout: &VRFInOut, threshold: u64) -> bool { /// the VRF. If the VRF produces a value less than `threshold`, it is our turn, /// so it returns `Some(_)`. Otherwise, it returns `None`. fn claim_slot( - randomness: &[u8], slot_number: u64, - epoch: u64, - Epoch { ref authorities, .. }: Epoch, + Epoch { ref authorities, ref randomness, epoch_index, .. }: Epoch, key: &sr25519::Pair, - threshold: u64, + c: (u64, u64), ) -> Option<((VRFInOut, VRFProof, VRFProofBatchable), usize)> { let public = &key.public(); let authority_index = authorities.iter().position(|s| &s.0 == public)?; - let transcript = make_transcript(randomness, slot_number, epoch); + let transcript = make_transcript(randomness, slot_number, epoch_index); // Compute the threshold we will use. // // We already checked that authorities contains `key.public()`, so it can't - // be empty. Therefore, this division is safe. - let threshold = threshold / authorities.len() as u64; + // be empty. Therefore, this division in `calculate_threshold` is safe. + let threshold = calculate_threshold(c, authorities, authority_index); get_keypair(key) .vrf_sign_n_check(transcript, |inout| check(inout, threshold)) @@ -832,7 +858,7 @@ fn initialize_authorities_cache(client: &C) -> Result<(), ConsensusError> // check if we already have initialized the cache let genesis_id = BlockId::Number(Zero::zero()); let genesis_epoch: Option = cache - .get_at(&well_known_cache_keys::AUTHORITIES, &genesis_id) + .get_at(&well_known_cache_keys::EPOCH, &genesis_id) .and_then(|v| Decode::decode(&mut &v[..])); if genesis_epoch.is_some() { return Ok(()); @@ -845,18 +871,17 @@ fn initialize_authorities_cache(client: &C) -> Result<(), ConsensusError> ))); let genesis_epoch = epoch(client, &genesis_id)?; - cache.initialize(&well_known_cache_keys::AUTHORITIES, genesis_epoch.encode()) + cache.initialize(&well_known_cache_keys::EPOCH, genesis_epoch.encode()) .map_err(map_err) } -/// Tree of all epoch changes across all *seen* forks. Data stored in tree is the -/// hash and block number of the block signaling the epoch change, the new epoch -/// index and the minimum *slot number* when the next epoch should start (i.e. -/// slot number begin + duration). +/// Tree of all epoch changes across all *seen* forks. Data stored in tree is +/// the hash and block number of the block signaling the epoch change, and the +/// epoch that was signalled at that block. type EpochChanges = ForkTree< ::Hash, NumberFor, - (u64, SlotNumber), + Epoch, >; /// A shared epoch changes tree. @@ -893,50 +918,56 @@ impl From> for SharedEpochChanges { /// it is missing. /// /// The epoch change tree should be pruned as blocks are finalized. -pub struct BabeBlockImport { +pub struct BabeBlockImport { inner: I, client: Arc>, + api: Arc, epoch_changes: SharedEpochChanges, } -impl Clone for BabeBlockImport { +impl Clone for BabeBlockImport { fn clone(&self) -> Self { BabeBlockImport { inner: self.inner.clone(), client: self.client.clone(), + api: self.api.clone(), epoch_changes: self.epoch_changes.clone(), } } } -impl BabeBlockImport { +impl BabeBlockImport { fn new( client: Arc>, + api: Arc, epoch_changes: SharedEpochChanges, block_import: I, ) -> Self { BabeBlockImport { client, + api, inner: block_import, epoch_changes, } } } -impl BlockImport for BabeBlockImport where +impl BlockImport for BabeBlockImport where Block: BlockT, I: BlockImport + Send + Sync, I::Error: Into, B: Backend + 'static, E: CallExecutor + 'static + Clone + Send + Sync, RA: Send + Sync, + PRA: ProvideRuntimeApi + ProvideCache, + PRA::Api: BabeApi, { type Error = ConsensusError; fn import_block( &mut self, mut block: BlockImportParams, - new_cache: HashMap>, + mut new_cache: HashMap>, ) -> Result { let hash = block.post_header().hash(); let number = block.header.number().clone(); @@ -965,21 +996,22 @@ impl BlockImport for BabeBlockImport Result { - // this can only happen when the chain starts, since there's no epoch change at genesis. - // afterwards every time we expect an epoch change it means we will import another one. + // this can only happen when the chain starts, since there's no + // epoch change at genesis. afterwards every time we expect an epoch + // change it means we will import another one. for (root, _, _) in epoch_changes.roots() { let is_descendent_of = is_descendent_of(root, &hash) - .map_err(|e| ConsensusError::from(ConsensusError::ClientImport(e.to_string())))?; + .map_err(|e| { + ConsensusError::from(ConsensusError::ClientImport(e.to_string())) + })?; if is_descendent_of { return Ok(false); @@ -989,14 +1021,18 @@ impl BlockImport for BabeBlockImport(&block.header) + .map_err(|e| ConsensusError::from(ConsensusError::ClientImport(e.to_string())))?; - match (expected_epoch_change, new_cache.contains_key(&well_known_cache_keys::AUTHORITIES)) { + match (expected_epoch_change, next_epoch_digest.is_some()) { (true, true) => {}, (false, false) => {}, (true, false) => { return Err( - ConsensusError::ClientImport("Expected epoch change to happen by this block".into()) + ConsensusError::ClientImport( + "Expected epoch change to happen by this block".into(), + ) ); }, (false, true) => { @@ -1010,40 +1046,55 @@ impl BlockImport for BabeBlockImport( - &*epoch_changes, - |insert| block.auxiliary.extend( - insert.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec()))) - ) - ); - } else { - return Err( - ConsensusError::ClientImport("Failed to decode epoch change digest".into()) + // update the current epoch in the client cache + new_cache.insert( + well_known_cache_keys::EPOCH, + enacted_epoch.encode(), ); + + let current_epoch = epoch(&*self.api, &BlockId::Hash(parent_hash))?; + + // if the authorities have changed then we populate the + // `AUTHORITIES` key with the enacted epoch, so that the inner + // `ImportBlock` can process it (`EPOCH` is specific to BABE). + // e.g. in the case of GRANDPA it would require a justification + // for the block, expecting that the authorities actually + // changed. + if current_epoch.authorities != enacted_epoch.authorities { + new_cache.insert( + well_known_cache_keys::AUTHORITIES, + enacted_epoch.encode(), + ); + } } + + old_epoch_changes = Some(epoch_changes.clone()); + + // track the epoch change in the fork tree + epoch_changes.import( + hash, + number, + next_epoch, + &is_descendent_of, + ).map_err(|e| ConsensusError::from(ConsensusError::ClientImport(e.to_string())))?; + + crate::aux_schema::write_epoch_changes::( + &*epoch_changes, + |insert| block.auxiliary.extend( + insert.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec()))) + ) + ); } let import_result = self.inner.import_block(block, new_cache); @@ -1085,7 +1136,7 @@ pub fn import_queue, I, RA, PRA>( ) -> ClientResult<( BabeImportQueue, BabeLink, - BabeBlockImport, + BabeBlockImport, impl Future, )> where B: Backend + 'static, @@ -1100,7 +1151,7 @@ pub fn import_queue, I, RA, PRA>( initialize_authorities_cache(&*api)?; let verifier = BabeVerifier { - api, + api: api.clone(), inherent_data_providers, time_source: Default::default(), config, @@ -1111,6 +1162,7 @@ pub fn import_queue, I, RA, PRA>( let block_import = BabeBlockImport::new( client.clone(), + api, epoch_changes.clone(), block_import, ); @@ -1123,7 +1175,9 @@ pub fn import_queue, I, RA, PRA>( ¬ification.hash, *notification.header.number(), &is_descendent_of, - ).map_err(|e| debug!(target: "babe", "Error pruning epoch changes fork tree: {:?}", e))?; + ).map_err(|e| { + debug!(target: "babe", "Error pruning epoch changes fork tree: {:?}", e) + })?; Ok(()) }); @@ -1138,3 +1192,39 @@ pub fn import_queue, I, RA, PRA>( Ok((queue, timestamp_core, block_import, pruning_task)) } + +/// BABE test helpers. Utility methods for manually authoring blocks. +#[cfg(feature = "test-helpers")] +pub mod test_helpers { + use super::*; + + /// Try to claim the given slot and return a `BabePreDigest` if + /// successful. + pub fn claim_slot( + client: &C, + at: &BlockId, + slot_number: u64, + key: &sr25519::Pair, + c: (u64, u64), + ) -> Option where + B: BlockT, + C: ProvideRuntimeApi + ProvideCache, + C::Api: BabeApi, + { + let epoch = epoch(client, at).unwrap(); + + super::claim_slot( + slot_number, + epoch, + key, + c, + ).map(|((inout, vrf_proof, _), authority_index)| { + BabePreDigest { + vrf_proof, + vrf_output: inout.to_output(), + authority_index: authority_index as u64, + slot_number, + } + }) + } +} diff --git a/substrate/core/consensus/babe/src/tests.rs b/substrate/core/consensus/babe/src/tests.rs index 17864142db..571951b3b7 100644 --- a/substrate/core/consensus/babe/src/tests.rs +++ b/substrate/core/consensus/babe/src/tests.rs @@ -64,7 +64,12 @@ impl Proposer for DummyProposer { type Error = Error; type Create = Result; - fn propose(&self, _: InherentData, digests: DigestFor, _: Duration) -> Result { + fn propose( + &self, + _: InherentData, + digests: DigestFor, + _: Duration, + ) -> Result { self.1.new_block(digests).unwrap().bake().map_err(|e| e.into()) } } @@ -232,7 +237,10 @@ fn run_one_test() { // wait for all finalized on each. let wait_for = futures::future::join_all(import_notifications); - let drive_to_completion = futures::future::poll_fn(|| { net.lock().poll(); Ok(Async::NotReady) }); + let drive_to_completion = futures::future::poll_fn(|| { + net.lock().poll(); + Ok(Async::NotReady) + }); let _ = runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap(); } @@ -306,17 +314,17 @@ fn sig_is_not_pre_digest() { #[test] fn can_author_block() { let _ = env_logger::try_init(); - let randomness = &[]; let (pair, _) = sr25519::Pair::generate(); let mut i = 0; let epoch = Epoch { - authorities: vec![(pair.public(), 0)], + start_slot: 0, + authorities: vec![(pair.public(), 1)], randomness: [0; 32], epoch_index: 1, duration: 100, }; loop { - match claim_slot(randomness, i, 0, epoch.clone(), &pair, u64::MAX / 10) { + match claim_slot(i, epoch.clone(), &pair, (3, 10)) { None => i += 1, Some(s) => { debug!(target: "babe", "Authored block {:?}", s); diff --git a/substrate/core/consensus/common/src/lib.rs b/substrate/core/consensus/common/src/lib.rs index e976adb83c..0c968a327d 100644 --- a/substrate/core/consensus/common/src/lib.rs +++ b/substrate/core/consensus/common/src/lib.rs @@ -123,4 +123,7 @@ pub mod well_known_cache_keys { /// A list of authorities. pub const AUTHORITIES: Id = *b"auth"; + + /// Current Epoch data. + pub const EPOCH: Id = *b"epch"; } diff --git a/substrate/core/consensus/rhd/src/lib.rs b/substrate/core/consensus/rhd/src/lib.rs index 4eb118c853..4670cb5dee 100644 --- a/substrate/core/consensus/rhd/src/lib.rs +++ b/substrate/core/consensus/rhd/src/lib.rs @@ -1331,7 +1331,7 @@ mod tests { use runtime_primitives::testing::{Block as GenericTestBlock, Header as TestHeader}; use primitives::H256; - use keyring::AuthorityKeyring; + use keyring::Ed25519Keyring; type TestBlock = GenericTestBlock<()>; @@ -1436,7 +1436,7 @@ mod tests { start_round: 0, })), round_timeout_multiplier: 10, - key: Arc::new(AuthorityKeyring::One.into()), + key: Arc::new(Ed25519Keyring::One.into()), factory: DummyFactory } } @@ -1462,10 +1462,10 @@ mod tests { fn future_gets_preempted() { let client = FakeClient { authorities: vec![ - AuthorityKeyring::One.into(), - AuthorityKeyring::Two.into(), - AuthorityKeyring::Alice.into(), - AuthorityKeyring::Eve.into(), + Ed25519Keyring::One.into(), + Ed25519Keyring::Two.into(), + Ed25519Keyring::Alice.into(), + Ed25519Keyring::Eve.into(), ], imported_heights: Mutex::new(HashSet::new()), }; @@ -1509,17 +1509,17 @@ mod tests { let hash = [0xff; 32].into(); let authorities = vec![ - AuthorityKeyring::One.into(), - AuthorityKeyring::Two.into(), - AuthorityKeyring::Alice.into(), - AuthorityKeyring::Eve.into(), + Ed25519Keyring::One.into(), + Ed25519Keyring::Two.into(), + Ed25519Keyring::Alice.into(), + Ed25519Keyring::Eve.into(), ]; let authorities_keys = vec![ - AuthorityKeyring::One.into(), - AuthorityKeyring::Two.into(), - AuthorityKeyring::Alice.into(), - AuthorityKeyring::Eve.into(), + Ed25519Keyring::One.into(), + Ed25519Keyring::Two.into(), + Ed25519Keyring::Alice.into(), + Ed25519Keyring::Eve.into(), ]; let unchecked = UncheckedJustification(rhododendron::UncheckedJustification { @@ -1570,8 +1570,8 @@ mod tests { let parent_hash = Default::default(); let authorities = vec![ - AuthorityKeyring::Alice.into(), - AuthorityKeyring::Eve.into(), + Ed25519Keyring::Alice.into(), + Ed25519Keyring::Eve.into(), ]; let block = TestBlock { @@ -1579,7 +1579,11 @@ mod tests { extrinsics: Default::default() }; - let proposal = sign_message(rhododendron::Message::Propose(1, block.clone()), &AuthorityKeyring::Alice.pair(), parent_hash);; + let proposal = sign_message( + rhododendron::Message::Propose(1, block.clone()), + &Ed25519Keyring::Alice.pair(), + parent_hash, + ); if let rhododendron::LocalizedMessage::Propose(proposal) = proposal { assert!(check_proposal(&authorities, &parent_hash, &proposal).is_ok()); let mut invalid_round = proposal.clone(); @@ -1593,7 +1597,11 @@ mod tests { } // Not an authority - let proposal = sign_message::(rhododendron::Message::Propose(1, block), &AuthorityKeyring::Bob.pair(), parent_hash);; + let proposal = sign_message::( + rhododendron::Message::Propose(1, block), + &Ed25519Keyring::Bob.pair(), + parent_hash, + ); if let rhododendron::LocalizedMessage::Propose(proposal) = proposal { assert!(check_proposal(&authorities, &parent_hash, &proposal).is_err()); } else { @@ -1607,8 +1615,8 @@ mod tests { let hash: H256 = [0xff; 32].into(); let authorities = vec![ - AuthorityKeyring::Alice.into(), - AuthorityKeyring::Eve.into(), + Ed25519Keyring::Alice.into(), + Ed25519Keyring::Eve.into(), ]; let vote = sign_message::(rhododendron::Message::Vote(rhododendron::Vote::Prepare(1, hash)), &Keyring::Alice.pair(), parent_hash);; @@ -1634,10 +1642,10 @@ mod tests { fn drop_bft_future_does_not_deadlock() { let client = FakeClient { authorities: vec![ - AuthorityKeyring::One.into(), - AuthorityKeyring::Two.into(), - AuthorityKeyring::Alice.into(), - AuthorityKeyring::Eve.into(), + Ed25519Keyring::One.into(), + Ed25519Keyring::Two.into(), + Ed25519Keyring::Alice.into(), + Ed25519Keyring::Eve.into(), ], imported_heights: Mutex::new(HashSet::new()), }; @@ -1659,10 +1667,10 @@ mod tests { fn bft_can_build_though_skipped() { let client = FakeClient { authorities: vec![ - AuthorityKeyring::One.into(), - AuthorityKeyring::Two.into(), - AuthorityKeyring::Alice.into(), - AuthorityKeyring::Eve.into(), + Ed25519Keyring::One.into(), + Ed25519Keyring::Two.into(), + Ed25519Keyring::Alice.into(), + Ed25519Keyring::Eve.into(), ], imported_heights: Mutex::new(HashSet::new()), }; diff --git a/substrate/core/consensus/rhd/src/misbehaviour_check.rs b/substrate/core/consensus/rhd/src/misbehaviour_check.rs index 58b36542f6..a475f5d1ef 100644 --- a/substrate/core/consensus/rhd/src/misbehaviour_check.rs +++ b/substrate/core/consensus/rhd/src/misbehaviour_check.rs @@ -74,7 +74,7 @@ pub fn evaluate_misbehavior( mod tests { use super::*; - use keyring::AuthorityKeyring; + use keyring::Ed25519Keyring; use rhododendron; use runtime_primitives::testing::{H256, Block as RawBlock}; @@ -109,7 +109,7 @@ mod tests { #[test] fn evaluates_double_prepare() { - let key = AuthorityKeyring::One.pair(); + let key = Ed25519Keyring::One.pair(); let parent_hash = [0xff; 32].into(); let hash_1 = [0; 32].into(); let hash_2 = [1; 32].into(); @@ -138,7 +138,7 @@ mod tests { // misbehavior has wrong target. assert!(!evaluate_misbehavior::( - &AuthorityKeyring::Two.into(), + &Ed25519Keyring::Two.into(), parent_hash, &MisbehaviorKind::BftDoublePrepare( 1, @@ -150,7 +150,7 @@ mod tests { #[test] fn evaluates_double_commit() { - let key = AuthorityKeyring::One.pair(); + let key = Ed25519Keyring::One.pair(); let parent_hash = [0xff; 32].into(); let hash_1 = [0; 32].into(); let hash_2 = [1; 32].into(); @@ -179,7 +179,7 @@ mod tests { // misbehavior has wrong target. assert!(!evaluate_misbehavior::( - &AuthorityKeyring::Two.into(), + &Ed25519Keyring::Two.into(), parent_hash, &MisbehaviorKind::BftDoubleCommit( 1, diff --git a/substrate/core/finality-grandpa/src/communication/tests.rs b/substrate/core/finality-grandpa/src/communication/tests.rs index 5760b3936c..8b4d28d9f3 100644 --- a/substrate/core/finality-grandpa/src/communication/tests.rs +++ b/substrate/core/finality-grandpa/src/communication/tests.rs @@ -23,7 +23,7 @@ use network::test::{Block, Hash}; use network_gossip::Validator; use tokio::runtime::current_thread; use std::sync::Arc; -use keyring::AuthorityKeyring; +use keyring::Ed25519Keyring; use parity_codec::Encode; use crate::environment::SharedVoterSetState; @@ -202,7 +202,7 @@ fn make_test_network() -> ( ) } -fn make_ids(keys: &[AuthorityKeyring]) -> Vec<(AuthorityId, u64)> { +fn make_ids(keys: &[Ed25519Keyring]) -> Vec<(AuthorityId, u64)> { keys.iter() .map(|key| AuthorityId(key.to_raw_public())) .map(|id| (id, 1)) @@ -220,7 +220,7 @@ impl network_gossip::ValidatorContext for NoopContext { #[test] fn good_commit_leads_to_relay() { - let private = [AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; + let private = [Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; let public = make_ids(&private[..]); let voter_set = Arc::new(public.iter().cloned().collect::>()); @@ -335,7 +335,7 @@ fn good_commit_leads_to_relay() { #[test] fn bad_commit_leads_to_report() { - let private = [AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; + let private = [Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; let public = make_ids(&private[..]); let voter_set = Arc::new(public.iter().cloned().collect::>()); diff --git a/substrate/core/finality-grandpa/src/import.rs b/substrate/core/finality-grandpa/src/import.rs index b16bf83576..2647fb5575 100644 --- a/substrate/core/finality-grandpa/src/import.rs +++ b/substrate/core/finality-grandpa/src/import.rs @@ -425,7 +425,7 @@ impl, RA, PRA, SC> BlockImport // we don't want to finalize on `inner.import_block` let mut justification = block.justification.take(); - let enacts_consensus_change = !new_cache.is_empty(); + let enacts_consensus_change = new_cache.contains_key(&well_known_cache_keys::AUTHORITIES); let import_result = (&*self.inner).import_block(block, new_cache); let mut imported_aux = { diff --git a/substrate/core/finality-grandpa/src/light_import.rs b/substrate/core/finality-grandpa/src/light_import.rs index affe2828fa..133fbabb05 100644 --- a/substrate/core/finality-grandpa/src/light_import.rs +++ b/substrate/core/finality-grandpa/src/light_import.rs @@ -246,7 +246,7 @@ fn do_import_block, RA, J>( // we don't want to finalize on `inner.import_block` let justification = block.justification.take(); - let enacts_consensus_change = !new_cache.is_empty(); + let enacts_consensus_change = new_cache.contains_key(&well_known_cache_keys::AUTHORITIES); let import_result = BlockImport::import_block(&mut client, block, new_cache); let mut imported_aux = match import_result { diff --git a/substrate/core/finality-grandpa/src/tests.rs b/substrate/core/finality-grandpa/src/tests.rs index 698996995f..506da2de14 100644 --- a/substrate/core/finality-grandpa/src/tests.rs +++ b/substrate/core/finality-grandpa/src/tests.rs @@ -23,7 +23,7 @@ use network::config::{ProtocolConfig, Roles, BoxFinalityProofRequestBuilder}; use parking_lot::Mutex; use futures03::{StreamExt as _, TryStreamExt as _}; use tokio::runtime::current_thread; -use keyring::ed25519::{Keyring as AuthorityKeyring}; +use keyring::Ed25519Keyring; use client::{ error::Result, runtime_api::{Core, RuntimeVersion, ApiExt}, @@ -342,7 +342,7 @@ impl AuthoritySetForFinalityChecker for TestApi { const TEST_GOSSIP_DURATION: Duration = Duration::from_millis(500); -fn make_ids(keys: &[AuthorityKeyring]) -> Vec<(substrate_primitives::ed25519::Public, u64)> { +fn make_ids(keys: &[Ed25519Keyring]) -> Vec<(substrate_primitives::ed25519::Public, u64)> { keys.iter() .map(|key| AuthorityId::from_raw(key.to_raw_public())) .map(|id| (id, 1)) @@ -355,7 +355,7 @@ fn run_to_completion_with( runtime: &mut current_thread::Runtime, blocks: u64, net: Arc>, - peers: &[AuthorityKeyring], + peers: &[Ed25519Keyring], with: F, ) -> u64 where F: FnOnce(current_thread::Handle) -> Option>> @@ -437,7 +437,7 @@ fn run_to_completion( runtime: &mut current_thread::Runtime, blocks: u64, net: Arc>, - peers: &[AuthorityKeyring] + peers: &[Ed25519Keyring] ) -> u64 { run_to_completion_with(runtime, blocks, net, peers, |_| None) } @@ -446,7 +446,7 @@ fn run_to_completion( fn finalize_3_voters_no_observers() { let _ = env_logger::try_init(); let mut runtime = current_thread::Runtime::new().unwrap(); - let peers = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; + let peers = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; let voters = make_ids(peers); let mut net = GrandpaTestNet::new(TestApi::new(voters), 3); @@ -470,7 +470,7 @@ fn finalize_3_voters_no_observers() { fn finalize_3_voters_1_full_observer() { let mut runtime = current_thread::Runtime::new().unwrap(); - let peers = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; + let peers = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; let voters = make_ids(peers); let mut net = GrandpaTestNet::new(TestApi::new(voters), 4); @@ -533,24 +533,24 @@ fn finalize_3_voters_1_full_observer() { fn transition_3_voters_twice_1_full_observer() { let _ = env_logger::try_init(); let peers_a = &[ - AuthorityKeyring::Alice, - AuthorityKeyring::Bob, - AuthorityKeyring::Charlie, + Ed25519Keyring::Alice, + Ed25519Keyring::Bob, + Ed25519Keyring::Charlie, ]; let peers_b = &[ - AuthorityKeyring::Dave, - AuthorityKeyring::Eve, - AuthorityKeyring::Ferdie, + Ed25519Keyring::Dave, + Ed25519Keyring::Eve, + Ed25519Keyring::Ferdie, ]; let peers_c = &[ - AuthorityKeyring::Alice, - AuthorityKeyring::Eve, - AuthorityKeyring::Two, + Ed25519Keyring::Alice, + Ed25519Keyring::Eve, + Ed25519Keyring::Two, ]; - let observer = &[AuthorityKeyring::One]; + let observer = &[Ed25519Keyring::One]; let genesis_voters = make_ids(peers_a); @@ -700,7 +700,7 @@ fn transition_3_voters_twice_1_full_observer() { #[test] fn justification_is_emitted_when_consensus_data_changes() { let mut runtime = current_thread::Runtime::new().unwrap(); - let peers = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; + let peers = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; let mut net = GrandpaTestNet::new(TestApi::new(make_ids(peers)), 3); // import block#1 WITH consensus data change @@ -718,7 +718,7 @@ fn justification_is_emitted_when_consensus_data_changes() { #[test] fn justification_is_generated_periodically() { let mut runtime = current_thread::Runtime::new().unwrap(); - let peers = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; + let peers = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; let voters = make_ids(peers); let mut net = GrandpaTestNet::new(TestApi::new(voters), 3); @@ -757,8 +757,8 @@ fn consensus_changes_works() { #[test] fn sync_justifications_on_change_blocks() { let mut runtime = current_thread::Runtime::new().unwrap(); - let peers_a = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; - let peers_b = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob]; + let peers_a = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; + let peers_b = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob]; let voters = make_ids(peers_b); // 4 peers, 3 of them are authorities and participate in grandpa @@ -813,13 +813,13 @@ fn finalizes_multiple_pending_changes_in_order() { let _ = env_logger::try_init(); let mut runtime = current_thread::Runtime::new().unwrap(); - let peers_a = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; - let peers_b = &[AuthorityKeyring::Dave, AuthorityKeyring::Eve, AuthorityKeyring::Ferdie]; - let peers_c = &[AuthorityKeyring::Dave, AuthorityKeyring::Alice, AuthorityKeyring::Bob]; + let peers_a = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; + let peers_b = &[Ed25519Keyring::Dave, Ed25519Keyring::Eve, Ed25519Keyring::Ferdie]; + let peers_c = &[Ed25519Keyring::Dave, Ed25519Keyring::Alice, Ed25519Keyring::Bob]; let all_peers = &[ - AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie, - AuthorityKeyring::Dave, AuthorityKeyring::Eve, AuthorityKeyring::Ferdie, + Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie, + Ed25519Keyring::Dave, Ed25519Keyring::Eve, Ed25519Keyring::Ferdie, ]; let genesis_voters = make_ids(peers_a); @@ -872,7 +872,7 @@ fn finalizes_multiple_pending_changes_in_order() { #[test] fn doesnt_vote_on_the_tip_of_the_chain() { let mut runtime = current_thread::Runtime::new().unwrap(); - let peers_a = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; + let peers_a = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; let voters = make_ids(peers_a); let api = TestApi::new(voters); let mut net = GrandpaTestNet::new(api, 3); @@ -898,8 +898,14 @@ fn force_change_to_new_set() { let _ = env_logger::try_init(); let mut runtime = current_thread::Runtime::new().unwrap(); // two of these guys are offline. - let genesis_authorities = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie, AuthorityKeyring::One, AuthorityKeyring::Two]; - let peers_a = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; + let genesis_authorities = &[ + Ed25519Keyring::Alice, + Ed25519Keyring::Bob, + Ed25519Keyring::Charlie, + Ed25519Keyring::One, + Ed25519Keyring::Two, + ]; + let peers_a = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; let api = TestApi::new(make_ids(genesis_authorities)); let voters = make_ids(peers_a); @@ -950,8 +956,8 @@ fn force_change_to_new_set() { #[test] fn allows_reimporting_change_blocks() { - let peers_a = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; - let peers_b = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob]; + let peers_a = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; + let peers_b = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob]; let voters = make_ids(peers_a); let api = TestApi::new(voters); let mut net = GrandpaTestNet::new(api.clone(), 3); @@ -999,8 +1005,8 @@ fn allows_reimporting_change_blocks() { #[test] fn test_bad_justification() { - let peers_a = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; - let peers_b = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob]; + let peers_a = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; + let peers_b = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob]; let voters = make_ids(peers_a); let api = TestApi::new(voters); let mut net = GrandpaTestNet::new(api.clone(), 3); @@ -1058,7 +1064,7 @@ fn voter_persists_its_votes() { // we have two authorities but we'll only be running the voter for alice // we are going to be listening for the prevotes it casts - let peers = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob]; + let peers = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob]; let voters = make_ids(peers); // alice has a chain with 20 blocks @@ -1264,7 +1270,7 @@ fn voter_persists_its_votes() { fn finalize_3_voters_1_light_observer() { let _ = env_logger::try_init(); let mut runtime = current_thread::Runtime::new().unwrap(); - let authorities = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; + let authorities = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; let voters = make_ids(authorities); let mut net = GrandpaTestNet::new(TestApi::new(voters), 4); @@ -1308,7 +1314,7 @@ fn finality_proof_is_fetched_by_light_client_when_consensus_data_changes() { let _ = ::env_logger::try_init(); let mut runtime = current_thread::Runtime::new().unwrap(); - let peers = &[AuthorityKeyring::Alice]; + let peers = &[Ed25519Keyring::Alice]; let mut net = GrandpaTestNet::new(TestApi::new(make_ids(peers)), 1); net.add_light_peer(&GrandpaTestNet::default_config()); @@ -1341,20 +1347,20 @@ fn empty_finality_proof_is_returned_to_light_client_when_authority_set_is_differ // two of these guys are offline. let genesis_authorities = if FORCE_CHANGE { vec![ - AuthorityKeyring::Alice, - AuthorityKeyring::Bob, - AuthorityKeyring::Charlie, - AuthorityKeyring::One, - AuthorityKeyring::Two, + Ed25519Keyring::Alice, + Ed25519Keyring::Bob, + Ed25519Keyring::Charlie, + Ed25519Keyring::One, + Ed25519Keyring::Two, ] } else { vec![ - AuthorityKeyring::Alice, - AuthorityKeyring::Bob, - AuthorityKeyring::Charlie, + Ed25519Keyring::Alice, + Ed25519Keyring::Bob, + Ed25519Keyring::Charlie, ] }; - let peers_a = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob, AuthorityKeyring::Charlie]; + let peers_a = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; let api = TestApi::new(make_ids(&genesis_authorities)); let voters = make_ids(peers_a); @@ -1401,7 +1407,7 @@ fn voter_catches_up_to_latest_round_when_behind() { let _ = env_logger::try_init(); let mut runtime = current_thread::Runtime::new().unwrap(); - let peers = &[AuthorityKeyring::Alice, AuthorityKeyring::Bob]; + let peers = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob]; let voters = make_ids(peers); let mut net = GrandpaTestNet::new(TestApi::new(voters), 3); diff --git a/substrate/core/keyring/src/lib.rs b/substrate/core/keyring/src/lib.rs index 5cf38401d0..e4714ad3c4 100644 --- a/substrate/core/keyring/src/lib.rs +++ b/substrate/core/keyring/src/lib.rs @@ -23,12 +23,12 @@ pub mod sr25519; pub mod ed25519; /// Convenience export: Sr25519's Keyring is exposed as `AccountKeyring`, -/// since it tends to be used for accounts. +/// since it tends to be used for accounts (although it may also be used +/// by authorities). pub use sr25519::Keyring as AccountKeyring; -/// Convenience export: Ed25519's Keyring is exposed as `AuthorityKeyring`, -/// since it tends to be used for authorities (session keys &c.). -pub use ed25519::Keyring as AuthorityKeyring; +pub use ed25519::Keyring as Ed25519Keyring; +pub use sr25519::Keyring as Sr25519Keyring; pub mod test { /// The keyring for use with accounts when using the test runtime. diff --git a/substrate/core/keyring/src/sr25519.rs b/substrate/core/keyring/src/sr25519.rs index 8db66ab5dd..202b954966 100644 --- a/substrate/core/keyring/src/sr25519.rs +++ b/substrate/core/keyring/src/sr25519.rs @@ -23,7 +23,7 @@ use substrate_primitives::{sr25519::{Pair, Public, Signature}, Pair as PairT, Pu pub use substrate_primitives::sr25519; /// Set of test accounts. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum_macros::Display, strum_macros::EnumIter)] pub enum Keyring { Alice, Bob, @@ -79,6 +79,11 @@ impl Keyring { Pair::from_string(&format!("//{}", <&'static str>::from(self)), None) .expect("static values are known good; qed") } + + /// Returns an iterator over all test accounts. + pub fn iter() -> impl Iterator { + ::iter() + } } impl From for &'static str { diff --git a/substrate/core/service/src/lib.rs b/substrate/core/service/src/lib.rs index a38009120f..bd600b81cb 100644 --- a/substrate/core/service/src/lib.rs +++ b/substrate/core/service/src/lib.rs @@ -41,7 +41,7 @@ use keystore::Store as Keystore; use network::{NetworkState, NetworkStateInfo}; use log::{log, info, warn, debug, error, Level}; use parity_codec::{Encode, Decode}; -use primitives::{Pair, ed25519, crypto}; +use primitives::{Pair, ed25519, sr25519, crypto}; use runtime_primitives::generic::BlockId; use runtime_primitives::traits::{Header, NumberFor, SaturatedConversion, Zero}; use substrate_executor::NativeExecutor; @@ -192,6 +192,7 @@ impl Service { if let Some(keystore) = keystore.as_mut() { for seed in &config.keys { keystore.generate_from_seed::(seed)?; + keystore.generate_from_seed::(seed)?; } public_key = match keystore.contents::()?.get(0) { diff --git a/substrate/core/service/test/src/lib.rs b/substrate/core/service/test/src/lib.rs index 353b326a91..8375521a70 100644 --- a/substrate/core/service/test/src/lib.rs +++ b/substrate/core/service/test/src/lib.rs @@ -358,7 +358,8 @@ pub fn sync(spec: FactoryChainSpec, mut block_factory: B, mut extrin E: FnMut(&SyncService) -> FactoryExtrinsic, { const NUM_FULL_NODES: usize = 10; - const NUM_LIGHT_NODES: usize = 10; + // FIXME: BABE light client support is currently not working. + const NUM_LIGHT_NODES: usize = 0; const NUM_BLOCKS: usize = 512; let temp = TempDir::new("substrate-sync-test").expect("Error creating test dir"); let mut network = TestNet::::new( diff --git a/substrate/core/test-client/src/lib.rs b/substrate/core/test-client/src/lib.rs index 509863e4e5..27237e13b6 100644 --- a/substrate/core/test-client/src/lib.rs +++ b/substrate/core/test-client/src/lib.rs @@ -25,7 +25,11 @@ pub use client_db::{Backend, self}; pub use client_ext::ClientExt; pub use consensus; pub use executor::{NativeExecutor, self}; -pub use keyring::{sr25519::Keyring as AuthorityKeyring, AccountKeyring}; +pub use keyring::{ + AccountKeyring, + ed25519::Keyring as Ed25519Keyring, + sr25519::Keyring as Sr25519Keyring, +}; pub use primitives::Blake2Hasher; pub use runtime_primitives::{StorageOverlay, ChildrenStorageOverlay}; pub use state_machine::ExecutionStrategy; diff --git a/substrate/core/test-runtime/Cargo.toml b/substrate/core/test-runtime/Cargo.toml index 873c1eb624..f2bf974faf 100644 --- a/substrate/core/test-runtime/Cargo.toml +++ b/substrate/core/test-runtime/Cargo.toml @@ -13,8 +13,8 @@ keyring = { package = "substrate-keyring", path = "../keyring", optional = true substrate-client = { path = "../client", default-features = false } primitives = { package = "substrate-primitives", path = "../primitives", default-features = false } inherents = { package = "substrate-inherents", path = "../inherents", default-features = false } -consensus_aura = { package = "substrate-consensus-aura-primitives", path = "../consensus/aura/primitives", default-features = false } -consensus_babe = { package = "substrate-consensus-babe-primitives", path = "../consensus/babe/primitives", default-features = false } +aura-primitives = { package = "substrate-consensus-aura-primitives", path = "../consensus/aura/primitives", default-features = false } +babe-primitives = { package = "substrate-consensus-babe-primitives", path = "../consensus/babe/primitives", default-features = false } rstd = { package = "sr-std", path = "../sr-std", default-features = false } runtime_io = { package = "sr-io", path = "../sr-io", default-features = false } runtime_primitives = { package = "sr-primitives", path = "../sr-primitives", default-features = false } @@ -54,8 +54,8 @@ std = [ "inherents/std", "runtime_primitives/std", "runtime_version/std", - "consensus_aura/std", - "consensus_babe/std", + "aura-primitives/std", + "babe-primitives/std", "primitives/std", "substrate-trie/std", "trie-db/std", diff --git a/substrate/core/test-runtime/client/src/lib.rs b/substrate/core/test-runtime/client/src/lib.rs index 104ffac820..0f43911168 100644 --- a/substrate/core/test-runtime/client/src/lib.rs +++ b/substrate/core/test-runtime/client/src/lib.rs @@ -39,7 +39,7 @@ pub mod prelude { Executor, LightExecutor, LocalExecutor, NativeExecutor, }; // Keyring - pub use super::{AccountKeyring, AuthorityKeyring}; + pub use super::{AccountKeyring, Sr25519Keyring}; } mod local_executor { @@ -172,9 +172,9 @@ impl TestClientBuilderExt for TestClientBuilder< fn genesis_config(support_changes_trie: bool) -> GenesisConfig { GenesisConfig::new(support_changes_trie, vec![ - AuthorityKeyring::Alice.into(), - AuthorityKeyring::Bob.into(), - AuthorityKeyring::Charlie.into(), + Sr25519Keyring::Alice.into(), + Sr25519Keyring::Bob.into(), + Sr25519Keyring::Charlie.into(), ], vec![ AccountKeyring::Alice.into(), AccountKeyring::Bob.into(), diff --git a/substrate/core/test-runtime/src/lib.rs b/substrate/core/test-runtime/src/lib.rs index 13b8a9eac8..7947d830a3 100644 --- a/substrate/core/test-runtime/src/lib.rs +++ b/substrate/core/test-runtime/src/lib.rs @@ -50,11 +50,10 @@ use runtime_version::NativeVersion; use runtime_support::{impl_outer_origin, parameter_types}; use inherents::{CheckInherentsResult, InherentData}; use cfg_if::cfg_if; -pub use consensus_babe::AuthorityId; + // Ensure Babe and Aura use the same crypto to simplify things a bit. +pub use babe_primitives::AuthorityId; pub type AuraId = AuthorityId; -// Ensure Babe and Aura use the same crypto to simplify things a bit. -pub type BabeId = AuthorityId; // Inlucde the WASM binary #[cfg(feature = "std")] @@ -356,6 +355,14 @@ impl srml_timestamp::Trait for Runtime { type MinimumPeriod = MinimumPeriod; } +parameter_types! { + pub const EpochDuration: u64 = 6; +} + +impl srml_babe::Trait for Runtime { + type EpochDuration = EpochDuration; +} + /// Adds one to the given input and returns the final result. #[inline(never)] fn benchmark_add_one(i: u64) -> u64 { @@ -514,29 +521,30 @@ cfg_if! { } } - impl consensus_aura::AuraApi for Runtime { + impl aura_primitives::AuraApi for Runtime { fn slot_duration() -> u64 { 1 } fn authorities() -> Vec { system::authorities() } } - impl consensus_babe::BabeApi for Runtime { - fn startup_data() -> consensus_babe::BabeConfiguration { - consensus_babe::BabeConfiguration { + impl babe_primitives::BabeApi for Runtime { + fn startup_data() -> babe_primitives::BabeConfiguration { + babe_primitives::BabeConfiguration { median_required_blocks: 0, slot_duration: 3, - expected_block_time: 1, - threshold: core::u64::MAX, - slots_per_epoch: 6, + c: (3, 10), } } - fn epoch() -> consensus_babe::Epoch { + + fn epoch() -> babe_primitives::Epoch { let authorities = system::authorities(); let authorities: Vec<_> = authorities.into_iter().map(|x|(x, 1)).collect(); - consensus_babe::Epoch { + + babe_primitives::Epoch { + start_slot: >::epoch_start_slot(), authorities, randomness: >::randomness(), - epoch_index: 1, - duration: 6, + epoch_index: >::epoch_index(), + duration: EpochDuration::get(), } } } @@ -669,30 +677,30 @@ cfg_if! { } } - impl consensus_aura::AuraApi for Runtime { + impl aura_primitives::AuraApi for Runtime { fn slot_duration() -> u64 { 1 } fn authorities() -> Vec { system::authorities() } } - impl consensus_babe::BabeApi for Runtime { - fn startup_data() -> consensus_babe::BabeConfiguration { - consensus_babe::BabeConfiguration { + impl babe_primitives::BabeApi for Runtime { + fn startup_data() -> babe_primitives::BabeConfiguration { + babe_primitives::BabeConfiguration { median_required_blocks: 0, slot_duration: 1, - expected_block_time: 1, - threshold: core::u64::MAX, - slots_per_epoch: 6, + c: (3, 10), } } - fn epoch() -> consensus_babe::Epoch { + fn epoch() -> babe_primitives::Epoch { let authorities = system::authorities(); let authorities: Vec<_> = authorities.into_iter().map(|x|(x, 1)).collect(); - consensus_babe::Epoch { + + babe_primitives::Epoch { + start_slot: >::epoch_start_slot(), authorities, randomness: >::randomness(), - epoch_index: 1, - duration: 6, + epoch_index: >::epoch_index(), + duration: EpochDuration::get(), } } } diff --git a/substrate/core/test-runtime/src/system.rs b/substrate/core/test-runtime/src/system.rs index 01b032f599..926f2f5410 100644 --- a/substrate/core/test-runtime/src/system.rs +++ b/substrate/core/test-runtime/src/system.rs @@ -313,16 +313,16 @@ mod tests { use super::*; use runtime_io::{with_externalities, TestExternalities}; - use substrate_test_runtime_client::{AuthorityKeyring, AccountKeyring}; + use substrate_test_runtime_client::{AccountKeyring, Sr25519Keyring}; use crate::{Header, Transfer, WASM_BINARY}; use primitives::{Blake2Hasher, map}; use substrate_executor::WasmExecutor; fn new_test_ext() -> TestExternalities { let authorities = vec![ - AuthorityKeyring::Alice.to_raw_public(), - AuthorityKeyring::Bob.to_raw_public(), - AuthorityKeyring::Charlie.to_raw_public() + Sr25519Keyring::Alice.to_raw_public(), + Sr25519Keyring::Bob.to_raw_public(), + Sr25519Keyring::Charlie.to_raw_public() ]; TestExternalities::new(map![ twox_128(b"latest").to_vec() => vec![69u8; 32], diff --git a/substrate/core/utils/fork-tree/src/lib.rs b/substrate/core/utils/fork-tree/src/lib.rs index d202ba6c84..c6debe859a 100644 --- a/substrate/core/utils/fork-tree/src/lib.rs +++ b/substrate/core/utils/fork-tree/src/lib.rs @@ -228,7 +228,7 @@ impl ForkTree where let node = root.find_node_where(hash, number, is_descendent_of, predicate)?; // found the node, early exit - if node.is_some() { + if let Some(node) = node { return Ok(node); } } @@ -498,7 +498,7 @@ mod node_implementation { number: &N, is_descendent_of: &F, predicate: &P, - ) -> Result>, Error> + ) -> Result>>, Error> where E: std::error::Error, F: Fn(&H, &H) -> Result, P: Fn(&V) -> bool, @@ -519,10 +519,18 @@ mod node_implementation { } // node not found in any of the descendents, if the node we're - // searching for is a descendent of this node and it passes the - // predicate, then it is this one. - if predicate(&self.data) && is_descendent_of(&self.hash, hash)? { - Ok(Some(self)) + // searching for is a descendent of this node then we will stop the + // search here, since there aren't any more children and we found + // the correct node so we don't want to backtrack. + if is_descendent_of(&self.hash, hash)? { + // if the predicate passes we return the node + if predicate(&self.data) { + Ok(Some(Some(self))) + + // otherwise we stop the search returning `None` + } else { + Ok(Some(None)) + } } else { Ok(None) } @@ -1033,4 +1041,38 @@ mod test { vec!["C", "D", "E"], ); } + + #[test] + fn find_node_doesnt_backtrack_after_finding_highest_descending_node() { + let mut tree = ForkTree::new(); + + // + // A - B + // \ + // — C + // + let is_descendent_of = |base: &&str, block: &&str| -> Result { + match (*base, *block) { + ("A", b) => Ok(b == "B" || b == "C" || b == "D"), + ("B", b) | ("C", b) => Ok(b == "D"), + ("0", _) => Ok(true), + _ => Ok(false), + } + }; + + tree.import("A", 1, 1, &is_descendent_of).unwrap(); + tree.import("B", 2, 4, &is_descendent_of).unwrap(); + tree.import("C", 2, 4, &is_descendent_of).unwrap(); + + // when searching the tree we reach both node `B` and `C`, but the + // predicate doesn't pass. still, we should not backtrack to node `A`. + let node = tree.find_node_where( + &"D", + &3, + &is_descendent_of, + &|data| *data < 3, + ).unwrap(); + + assert_eq!(node, None); + } } diff --git a/substrate/node/cli/Cargo.toml b/substrate/node/cli/Cargo.toml index 17efcc400f..a9f3efdaa5 100644 --- a/substrate/node/cli/Cargo.toml +++ b/substrate/node/cli/Cargo.toml @@ -24,8 +24,8 @@ substrate-basic-authorship = { path = "../../core/basic-authorship" } substrate-service = { path = "../../core/service" } transaction_pool = { package = "substrate-transaction-pool", path = "../../core/transaction-pool" } network = { package = "substrate-network", path = "../../core/network" } -aura = { package = "substrate-consensus-aura", path = "../../core/consensus/aura" } -aura_primitives = { package = "substrate-consensus-aura-primitives", path = "../../core/consensus/aura/primitives" } +babe = { package = "substrate-consensus-babe", path = "../../core/consensus/babe" } +babe-primitives = { package = "substrate-consensus-babe-primitives", path = "../../core/consensus/babe/primitives" } grandpa = { package = "substrate-finality-grandpa", path = "../../core/finality-grandpa" } grandpa_primitives = { package = "substrate-finality-grandpa-primitives", path = "../../core/finality-grandpa/primitives" } sr-primitives = { path = "../../core/sr-primitives" } @@ -44,6 +44,7 @@ system = { package = "srml-system", path = "../../srml/system" } balances = { package = "srml-balances", path = "../../srml/balances" } [dev-dependencies] +babe = { package = "substrate-consensus-babe", path = "../../core/consensus/babe", features = ["test-helpers"] } consensus-common = { package = "substrate-consensus-common", path = "../../core/consensus/common" } service-test = { package = "substrate-service-test", path = "../../core/service/test" } diff --git a/substrate/node/cli/src/chain_spec.rs b/substrate/node/cli/src/chain_spec.rs index 681be0325e..781495780d 100644 --- a/substrate/node/cli/src/chain_spec.rs +++ b/substrate/node/cli/src/chain_spec.rs @@ -16,13 +16,14 @@ //! Substrate chain configurations. +use babe_primitives::AuthorityId as BabeId; use primitives::{ed25519, sr25519, Pair, crypto::UncheckedInto}; -use node_primitives::{AccountId, AuraId, Balance}; +use node_primitives::{AccountId, Balance}; use node_runtime::{ - GrandpaConfig, BalancesConfig, ContractsConfig, ElectionsConfig, DemocracyConfig, - CouncilConfig, AuraConfig, ImOnlineConfig, IndicesConfig, SessionConfig, StakingConfig, - SudoConfig, TechnicalCommitteeConfig, SystemConfig, WASM_BINARY, Perbill, SessionKeys, - StakerStatus, DAYS, DOLLARS, MILLICENTS, + BabeConfig, BalancesConfig, ContractsConfig, CouncilConfig, DemocracyConfig, + ElectionsConfig, GrandpaConfig, ImOnlineConfig, IndicesConfig, Perbill, + SessionConfig, SessionKeys, StakerStatus, StakingConfig, SudoConfig, SystemConfig, + TechnicalCommitteeConfig, DAYS, DOLLARS, MILLICENTS, WASM_BINARY, }; pub use node_runtime::GenesisConfig; use substrate_service; @@ -40,8 +41,11 @@ pub fn flaming_fir_config() -> Result { ChainSpec::from_embedded(include_bytes!("../res/flaming-fir.json")) } -fn session_keys(key: ed25519::Public) -> SessionKeys { - SessionKeys { ed25519: key } +fn session_keys(ed_key: ed25519::Public, sr_key: sr25519::Public) -> SessionKeys { + SessionKeys { + ed25519: ed_key, + sr25519: sr_key, + } } fn staging_testnet_config_genesis() -> GenesisConfig { @@ -51,7 +55,7 @@ fn staging_testnet_config_genesis() -> GenesisConfig { // and // for i in 1 2 3 4 ; do for j in session; do subkey --ed25519 inspect "$secret"//fir//$j//$i; done; done - let initial_authorities: Vec<(AccountId, AccountId, AuraId, GrandpaId)> = vec![( + let initial_authorities: Vec<(AccountId, AccountId, BabeId, GrandpaId)> = vec![( // 5Fbsd6WXDGiLTxunqeK5BATNiocfCqu9bS1yArVjCgeBLkVy hex!["9c7a2ee14e565db0c69f78c7b4cd839fbf52b607d867e9e9c5a79042898a0d12"].unchecked_into(), // 5EnCiV7wSHeNhjW3FSUwiJNkcc2SBkPLn5Nj93FmbLtBjQUq @@ -116,7 +120,9 @@ fn staging_testnet_config_genesis() -> GenesisConfig { .collect::>(), }), session: Some(SessionConfig { - keys: initial_authorities.iter().map(|x| (x.0.clone(), session_keys(x.2.clone()))).collect::>(), + keys: initial_authorities.iter().map(|x| { + (x.0.clone(), session_keys(x.3.clone(), x.2.clone())) + }).collect::>(), }), staking: Some(StakingConfig { current_era: 0, @@ -124,7 +130,9 @@ fn staging_testnet_config_genesis() -> GenesisConfig { validator_count: 7, offline_slash_grace: 4, minimum_validator_count: 4, - stakers: initial_authorities.iter().map(|x| (x.0.clone(), x.1.clone(), STASH, StakerStatus::Validator)).collect(), + stakers: initial_authorities.iter().map(|x| { + (x.0.clone(), x.1.clone(), STASH, StakerStatus::Validator) + }).collect(), invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(), }), democracy: Some(DemocracyConfig::default()), @@ -149,8 +157,8 @@ fn staging_testnet_config_genesis() -> GenesisConfig { sudo: Some(SudoConfig { key: endowed_accounts[0].clone(), }), - aura: Some(AuraConfig { - authorities: initial_authorities.iter().map(|x| x.2.clone()).collect(), + babe: Some(BabeConfig { + authorities: initial_authorities.iter().map(|x| (x.2.clone(), 1)).collect(), }), im_online: Some(ImOnlineConfig { gossip_at: 0, @@ -184,9 +192,9 @@ pub fn get_account_id_from_seed(seed: &str) -> AccountId { .public() } -/// Helper function to generate AuraId from seed -pub fn get_aura_id_from_seed(seed: &str) -> AuraId { - ed25519::Pair::from_string(&format!("//{}", seed), None) +/// Helper function to generate BabeId from seed +pub fn get_babe_id_from_seed(seed: &str) -> BabeId { + sr25519::Pair::from_string(&format!("//{}", seed), None) .expect("static values are valid; qed") .public() } @@ -199,18 +207,18 @@ pub fn get_grandpa_id_from_seed(seed: &str) -> GrandpaId { } /// Helper function to generate stash, controller and session key from seed -pub fn get_authority_keys_from_seed(seed: &str) -> (AccountId, AccountId, AuraId, GrandpaId) { +pub fn get_authority_keys_from_seed(seed: &str) -> (AccountId, AccountId, BabeId, GrandpaId) { ( get_account_id_from_seed(&format!("{}//stash", seed)), get_account_id_from_seed(seed), - get_aura_id_from_seed(seed), + get_babe_id_from_seed(seed), get_grandpa_id_from_seed(seed) ) } /// Helper function to create GenesisConfig for testing pub fn testnet_genesis( - initial_authorities: Vec<(AccountId, AccountId, AuraId, GrandpaId)>, + initial_authorities: Vec<(AccountId, AccountId, BabeId, GrandpaId)>, root_key: AccountId, endowed_accounts: Option>, enable_println: bool, @@ -250,7 +258,9 @@ pub fn testnet_genesis( vesting: vec![], }), session: Some(SessionConfig { - keys: initial_authorities.iter().map(|x| (x.0.clone(), session_keys(x.2.clone()))).collect::>(), + keys: initial_authorities.iter().map(|x| { + (x.0.clone(), session_keys(x.3.clone(), x.2.clone())) + }).collect::>(), }), staking: Some(StakingConfig { current_era: 0, @@ -258,7 +268,9 @@ pub fn testnet_genesis( validator_count: 2, offline_slash: Perbill::zero(), offline_slash_grace: 0, - stakers: initial_authorities.iter().map(|x| (x.0.clone(), x.1.clone(), STASH, StakerStatus::Validator)).collect(), + stakers: initial_authorities.iter().map(|x| { + (x.0.clone(), x.1.clone(), STASH, StakerStatus::Validator) + }).collect(), invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(), }), democracy: Some(DemocracyConfig::default()), @@ -288,8 +300,8 @@ pub fn testnet_genesis( sudo: Some(SudoConfig { key: root_key, }), - aura: Some(AuraConfig { - authorities: initial_authorities.iter().map(|x| x.2.clone()).collect(), + babe: Some(BabeConfig { + authorities: initial_authorities.iter().map(|x| (x.2.clone(), 1)).collect(), }), im_online: Some(ImOnlineConfig{ gossip_at: 0, diff --git a/substrate/node/cli/src/service.rs b/substrate/node/cli/src/service.rs index 53d6f927b4..f75e97a8fe 100644 --- a/substrate/node/cli/src/service.rs +++ b/substrate/node/cli/src/service.rs @@ -21,14 +21,15 @@ use std::sync::Arc; use std::time::Duration; -use aura::{import_queue, start_aura, AuraImportQueue, SlotDuration}; +use babe::{import_queue, start_babe, BabeImportQueue, Config}; +use babe_primitives::AuthorityPair as BabePair; use client::{self, LongestChain}; use grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider}; use node_executor; use primitives::Pair; use grandpa_primitives::AuthorityPair as GrandpaPair; use futures::prelude::*; -use node_primitives::{AuraPair, Block}; +use node_primitives::Block; use node_runtime::{GenesisConfig, RuntimeApi}; use substrate_service::{ FactoryFullConfiguration, LightComponents, FullComponents, FullBackend, @@ -47,19 +48,40 @@ construct_simple_protocol! { pub struct NodeProtocol where Block = Block { } } +type BabeBlockImportForService = babe::BabeBlockImport< + FullBackend, + FullExecutor, + ::Block, + grandpa::BlockImportForService, + ::RuntimeApi, + client::Client< + FullBackend, + FullExecutor, + ::Block, + ::RuntimeApi + >, +>; + /// Node specific configuration pub struct NodeConfig { - /// grandpa connection to import block + /// GRANDPA and BABE connection to import block. // FIXME #1134 rather than putting this on the config, let's have an actual intermediate setup state - pub grandpa_import_setup: Option<(grandpa::BlockImportForService, grandpa::LinkHalfForService)>, + pub import_setup: Option<( + BabeBlockImportForService, + grandpa::LinkHalfForService, + babe::BabeLink, + )>, + /// Tasks that were created by previous setup steps and should be spawned. + pub tasks_to_spawn: Option + Send>>>, inherent_data_providers: InherentDataProviders, } impl Default for NodeConfig where F: substrate_service::ServiceFactory { fn default() -> NodeConfig { NodeConfig { - grandpa_import_setup: None, + import_setup: None, inherent_data_providers: InherentDataProviders::new(), + tasks_to_spawn: None, } } } @@ -67,7 +89,7 @@ impl Default for NodeConfig where F: substrate_service::ServiceFactory { construct_service_factory! { struct Factory { Block = Block, - ConsensusPair = AuraPair, + ConsensusPair = BabePair, FinalityPair = GrandpaPair, RuntimeApi = RuntimeApi, NetworkProtocol = NodeProtocol { |config| Ok(NodeProtocol::new()) }, @@ -83,11 +105,22 @@ construct_service_factory! { FullComponents::::new(config) }, AuthoritySetup = { |mut service: Self::FullService| { - let (block_import, link_half) = service.config.custom.grandpa_import_setup.take() + let (block_import, link_half, babe_link) = service.config.custom.import_setup.take() .expect("Link Half and Block Import are present for Full Services or setup failed before. qed"); - if let Some(aura_key) = service.authority_key() { - info!("Using aura key {}", aura_key.public()); + // spawn any futures that were created in the previous setup steps + if let Some(tasks) = service.config.custom.tasks_to_spawn.take() { + for task in tasks { + service.spawn_task( + task.select(service.on_exit()) + .map(|_| ()) + .map_err(|_| ()) + ); + } + } + + if let Some(babe_key) = service.authority_key() { + info!("Using BABE key {}", babe_key.public()); let proposer = Arc::new(substrate_basic_authorship::ProposerFactory { client: service.client(), @@ -98,18 +131,21 @@ construct_service_factory! { let select_chain = service.select_chain() .ok_or(ServiceError::SelectChainRequired)?; - let aura = start_aura( - SlotDuration::get_or_compute(&*client)?, - Arc::new(aura_key), + let babe_config = babe::BabeParams { + config: Config::get_or_compute(&*client)?, + local_key: Arc::new(babe_key), client, select_chain, block_import, - proposer, - service.network(), - service.config.custom.inherent_data_providers.clone(), - service.config.force_authoring, - )?; - let select = aura.select(service.on_exit()).then(|_| Ok(())); + env: proposer, + sync_oracle: service.network(), + inherent_data_providers: service.config.custom.inherent_data_providers.clone(), + force_authoring: service.config.force_authoring, + time_source: babe_link, + }; + + let babe = start_babe(babe_config)?; + let select = babe.select(service.on_exit()).then(|_| Ok(())); service.spawn_task(Box::new(select)); } @@ -158,27 +194,30 @@ construct_service_factory! { }, LightService = LightComponents { |config| >::new(config) }, - FullImportQueue = AuraImportQueue + FullImportQueue = BabeImportQueue { |config: &mut FactoryFullConfiguration , client: Arc>, select_chain: Self::SelectChain| { - let slot_duration = SlotDuration::get_or_compute(&*client)?; let (block_import, link_half) = grandpa::block_import::<_, _, _, RuntimeApi, FullClient, _>( client.clone(), client.clone(), select_chain )?; let justification_import = block_import.clone(); - config.custom.grandpa_import_setup = Some((block_import.clone(), link_half)); - - import_queue::<_, _, AuraPair>( - slot_duration, - Box::new(block_import), + let (import_queue, babe_link, babe_block_import, pruning_task) = import_queue( + Config::get_or_compute(&*client)?, + block_import, Some(Box::new(justification_import)), None, + client.clone(), client, config.custom.inherent_data_providers.clone(), - ).map_err(Into::into) + )?; + + config.custom.import_setup = Some((babe_block_import.clone(), link_half, babe_link)); + config.custom.tasks_to_spawn = Some(vec![Box::new(pruning_task)]); + + Ok(import_queue) }}, - LightImportQueue = AuraImportQueue + LightImportQueue = BabeImportQueue { |config: &FactoryFullConfiguration, client: Arc>| { #[allow(deprecated)] let fetch_checker = client.backend().blockchain().fetcher() @@ -188,17 +227,22 @@ construct_service_factory! { let block_import = grandpa::light_block_import::<_, _, _, RuntimeApi, LightClient>( client.clone(), Arc::new(fetch_checker), client.clone() )?; + let finality_proof_import = block_import.clone(); let finality_proof_request_builder = finality_proof_import.create_finality_proof_request_builder(); - import_queue::<_, _, AuraPair>( - SlotDuration::get_or_compute(&*client)?, - Box::new(block_import), + // FIXME: pruning task isn't started since light client doesn't do `AuthoritySetup`. + let (import_queue, ..) = import_queue( + Config::get_or_compute(&*client)?, + block_import, None, Some(Box::new(finality_proof_import)), + client.clone(), client, config.custom.inherent_data_providers.clone(), - ).map(|q| (q, finality_proof_request_builder)).map_err(Into::into) + )?; + + Ok((import_queue, finality_proof_request_builder)) }}, SelectChain = LongestChain, Self::Block> { |config: &FactoryFullConfiguration, client: Arc>| { @@ -216,25 +260,27 @@ construct_service_factory! { #[cfg(test)] mod tests { use std::sync::Arc; - use aura::CompatibleDigestItem; + use babe::CompatibleDigestItem; use consensus_common::{Environment, Proposer, BlockImportParams, BlockOrigin, ForkChoiceStrategy}; use node_primitives::DigestItem; use node_runtime::{BalancesCall, Call, CENTS, SECS_PER_BLOCK, UncheckedExtrinsic}; use parity_codec::{Encode, Decode}; use primitives::{ - crypto::Pair as CryptoPair, ed25519::Pair, blake2_256, + crypto::Pair as CryptoPair, blake2_256, sr25519::Public as AddressPublic, H256, }; use sr_primitives::{generic::{BlockId, Era, Digest}, traits::Block, OpaqueExtrinsic}; use timestamp; use finality_tracker; - use keyring::{ed25519::Keyring as AuthorityKeyring, sr25519::Keyring as AccountKeyring}; + use keyring::{AccountKeyring, Sr25519Keyring}; use substrate_service::ServiceFactory; use service_test::SyncService; use crate::service::Factory; #[cfg(feature = "rhd")] fn test_sync() { + use primitives::ed25519::Pair; + use {service_test, Factory}; use client::{BlockImportParams, BlockOrigin}; @@ -294,7 +340,7 @@ mod tests { fn test_sync() { let chain_spec = crate::chain_spec::tests::integration_test_config_with_single_authority(); - let alice = Arc::new(AuthorityKeyring::Alice.pair()); + let alice = Arc::new(Sr25519Keyring::Alice.pair()); let mut slot_num = 1u64; let block_factory = |service: &SyncService<::FullService>| { let service = service.get(); @@ -305,7 +351,6 @@ mod tests { .create_inherent_data() .expect("Creates inherent data."); inherent_data.replace_data(finality_tracker::INHERENT_IDENTIFIER, &1u64); - inherent_data.replace_data(timestamp::INHERENT_IDENTIFIER, &(slot_num * SECS_PER_BLOCK)); let parent_id = BlockId::number(service.client().info().chain.best_number); let parent_header = service.client().header(&parent_id).unwrap().unwrap(); @@ -315,7 +360,26 @@ mod tests { }); let mut digest = Digest::::default(); - digest.push(>::aura_pre_digest(slot_num)); + + // even though there's only one authority some slots might be empty, + // so we must keep trying the next slots until we can claim one. + let babe_pre_digest = loop { + inherent_data.replace_data(timestamp::INHERENT_IDENTIFIER, &(slot_num * SECS_PER_BLOCK)); + if let Some(babe_pre_digest) = babe::test_helpers::claim_slot( + &*service.client(), + &parent_id, + slot_num, + &alice, + (3, 10), + ) { + break babe_pre_digest; + } + + slot_num += 1; + }; + + digest.push(::babe_pre_digest(babe_pre_digest)); + let proposer = proposer_factory.init(&parent_header).unwrap(); let new_block = proposer.propose( inherent_data, @@ -329,7 +393,7 @@ mod tests { // add it to a digest item. let to_sign = pre_hash.encode(); let signature = alice.sign(&to_sign[..]); - let item = >::aura_seal( + let item = ::babe_seal( signature, ); slot_num += 1; diff --git a/substrate/node/executor/src/lib.rs b/substrate/node/executor/src/lib.rs index 975b689919..7d806735a1 100644 --- a/substrate/node/executor/src/lib.rs +++ b/substrate/node/executor/src/lib.rs @@ -39,7 +39,7 @@ mod tests { use super::Executor; use substrate_executor::{WasmExecutor, NativeExecutionDispatch}; use parity_codec::{Encode, Decode, Joiner}; - use keyring::{AuthorityKeyring, AccountKeyring}; + use keyring::{AccountKeyring, Ed25519Keyring, Sr25519Keyring}; use runtime_support::{Hashable, StorageValue, StorageMap, traits::{Currency, Get}}; use state_machine::{CodeExecutor, Externalities, TestExternalities as CoreTestExternalities}; use primitives::{ @@ -314,15 +314,19 @@ mod tests { }); } - fn to_session_keys(ring: &AuthorityKeyring) -> SessionKeys { + fn to_session_keys( + ed25519_keyring: &Ed25519Keyring, + sr25519_keyring: &Sr25519Keyring, + ) -> SessionKeys { SessionKeys { - ed25519: ring.to_owned().into(), + ed25519: ed25519_keyring.to_owned().into(), + sr25519: sr25519_keyring.to_owned().into(), } } fn new_test_ext(code: &[u8], support_changes_trie: bool) -> TestExternalities { let mut ext = TestExternalities::new_with_code_with_children(code, GenesisConfig { - aura: Some(Default::default()), + babe: Some(Default::default()), system: Some(SystemConfig { changes_trie_config: if support_changes_trie { Some(ChangesTrieConfiguration { digest_interval: 2, @@ -346,9 +350,18 @@ mod tests { }), session: Some(SessionConfig { keys: vec![ - (alice(), to_session_keys(&AuthorityKeyring::Alice)), - (bob(), to_session_keys(&AuthorityKeyring::Bob)), - (charlie(), to_session_keys(&AuthorityKeyring::Charlie)), + (alice(), to_session_keys( + &Ed25519Keyring::Alice, + &Sr25519Keyring::Alice, + )), + (bob(), to_session_keys( + &Ed25519Keyring::Bob, + &Sr25519Keyring::Bob, + )), + (charlie(), to_session_keys( + &Ed25519Keyring::Charlie, + &Sr25519Keyring::Charlie, + )), ] }), staking: Some(StakingConfig { diff --git a/substrate/node/primitives/src/lib.rs b/substrate/node/primitives/src/lib.rs index 351bb4fa12..c8a0b1a25e 100644 --- a/substrate/node/primitives/src/lib.rs +++ b/substrate/node/primitives/src/lib.rs @@ -44,16 +44,6 @@ pub type Balance = u128; /// Type used for expressing timestamp. pub type Moment = u64; -/// The aura crypto scheme defined via the keypair type. -#[cfg(feature = "std")] -pub type AuraPair = primitives::ed25519::Pair; - -/// Identity of an Aura authority. -pub type AuraId = primitives::ed25519::Public; - -/// Signature for an Aura authority. -pub type AuraSignature = primitives::ed25519::Signature; - /// Index of a transaction in the chain. pub type Index = u64; diff --git a/substrate/node/runtime/Cargo.toml b/substrate/node/runtime/Cargo.toml index 17c666dbdc..eca7c9ebb7 100644 --- a/substrate/node/runtime/Cargo.toml +++ b/substrate/node/runtime/Cargo.toml @@ -16,8 +16,9 @@ runtime_primitives = { package = "sr-primitives", path = "../../core/sr-primitiv offchain-primitives = { package = "substrate-offchain-primitives", path = "../../core/offchain/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 } -aura = { package = "srml-aura", path = "../../srml/aura", 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 } 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 } @@ -35,7 +36,6 @@ treasury = { package = "srml-treasury", path = "../../srml/treasury", default-fe sudo = { package = "srml-sudo", path = "../../srml/sudo", default-features = false } im-online = { package = "srml-im-online", path = "../../srml/im-online", default-features = false } node-primitives = { path = "../primitives", default-features = false } -consensus_aura = { package = "substrate-consensus-aura-primitives", path = "../../core/consensus/aura/primitives", default-features = false } rustc-hex = { version = "2.0", optional = true } serde = { version = "1.0", optional = true } substrate-keyring = { path = "../../core/keyring", optional = true } @@ -54,8 +54,9 @@ std = [ "rstd/std", "runtime_primitives/std", "support/std", - "aura/std", "authorship/std", + "babe/std", + "babe-primitives/std", "balances/std", "contracts/std", "collective/std", @@ -76,7 +77,6 @@ std = [ "serde", "safe-mix/std", "client/std", - "consensus_aura/std", "rustc-hex", "substrate-keyring", "offchain-primitives/std", diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs index c6c93c162b..28c89a6943 100644 --- a/substrate/node/runtime/src/lib.rs +++ b/substrate/node/runtime/src/lib.rs @@ -26,9 +26,10 @@ use support::{ }; use substrate_primitives::u32_trait::{_1, _2, _3, _4}; use node_primitives::{ - AccountId, AccountIndex, AuraId, Balance, BlockNumber, Hash, Index, + AccountId, AccountIndex, Balance, BlockNumber, Hash, Index, Moment, Signature, }; +use babe::{AuthorityId as BabeId}; use grandpa::fg_primitives::{self, ScheduledChange}; use client::{ block_builder::api::{self as block_builder_api, InherentData, CheckInherentsResult}, @@ -137,9 +138,12 @@ impl system::Trait for Runtime { type MaximumBlockLength = MaximumBlockLength; } -impl aura::Trait for Runtime { - type HandleReport = aura::StakingSlasher; - type AuthorityId = AuraId; +parameter_types! { + pub const EpochDuration: u64 = 10 * MINUTES; +} + +impl babe::Trait for Runtime { + type EpochDuration = EpochDuration; } impl indices::Trait for Runtime { @@ -177,7 +181,7 @@ parameter_types! { } impl timestamp::Trait for Runtime { type Moment = Moment; - type OnTimestampSet = Aura; + type OnTimestampSet = Babe; type MinimumPeriod = MinimumPeriod; } @@ -193,17 +197,14 @@ impl authorship::Trait for Runtime { type EventHandler = (); } -parameter_types! { - pub const Period: BlockNumber = 10 * MINUTES; - pub const Offset: BlockNumber = 0; -} - -type SessionHandlers = (Grandpa, Aura, ImOnline); +type SessionHandlers = (Grandpa, Babe, ImOnline); impl_opaque_keys! { pub struct SessionKeys { #[id(key_types::ED25519)] pub ed25519: GrandpaId, + #[id(key_types::SR25519)] + pub sr25519: BabeId, } } @@ -216,7 +217,7 @@ impl_opaque_keys! { impl session::Trait for Runtime { type OnSessionEnding = Staking; type SessionHandler = SessionHandlers; - type ShouldEndSession = session::PeriodicSessions; + type ShouldEndSession = Babe; type Event = Event; type Keys = SessionKeys; type ValidatorId = AccountId; @@ -378,12 +379,12 @@ impl sudo::Trait for Runtime { } impl im_online::Trait for Runtime { - type AuthorityId = AuraId; + type AuthorityId = BabeId; type Call = Call; type Event = Event; type SessionsPerEra = SessionsPerEra; type UncheckedExtrinsic = UncheckedExtrinsic; - type IsValidAuthorityId = Aura; + type IsValidAuthorityId = Babe; } impl grandpa::Trait for Runtime { @@ -408,7 +409,7 @@ construct_runtime!( UncheckedExtrinsic = UncheckedExtrinsic { System: system::{Module, Call, Storage, Config, Event}, - Aura: aura::{Module, Call, Storage, Config, Inherent(Timestamp)}, + Babe: babe::{Module, Call, Storage, Config, Inherent(Timestamp)}, Timestamp: timestamp::{Module, Call, Storage, Inherent}, Authorship: authorship::{Module, Call, Storage}, Indices: indices, @@ -525,12 +526,23 @@ impl_runtime_apis! { } } - impl consensus_aura::AuraApi for Runtime { - fn slot_duration() -> u64 { - Aura::slot_duration() + impl babe_primitives::BabeApi for Runtime { + fn startup_data() -> babe_primitives::BabeConfiguration { + babe_primitives::BabeConfiguration { + median_required_blocks: 1000, + slot_duration: Babe::slot_duration(), + c: (3, 10), + } } - fn authorities() -> Vec { - Aura::authorities() + + fn epoch() -> babe_primitives::Epoch { + babe_primitives::Epoch { + start_slot: Babe::epoch_start_slot(), + authorities: Babe::authorities(), + epoch_index: Babe::epoch_index(), + randomness: Babe::randomness(), + duration: EpochDuration::get(), + } } } } diff --git a/substrate/srml/babe/src/lib.rs b/substrate/srml/babe/src/lib.rs index 577bda8c01..c75869ab57 100644 --- a/substrate/srml/babe/src/lib.rs +++ b/substrate/srml/babe/src/lib.rs @@ -18,11 +18,12 @@ #![cfg_attr(not(feature = "std"), no_std)] #![forbid(unused_must_use, unsafe_code, unused_variables, dead_code)] + pub use timestamp; use rstd::{result, prelude::*}; use srml_support::{decl_storage, decl_module, StorageValue, traits::FindAuthor, traits::Get}; -use timestamp::{OnTimestampSet, Trait}; +use timestamp::{OnTimestampSet}; use primitives::{generic::DigestItem, ConsensusEngineId}; use primitives::traits::{IsMember, SaturatedConversion, Saturating, RandomnessBeacon, Convert}; #[cfg(feature = "std")] @@ -31,7 +32,7 @@ use parity_codec::{Encode, Decode}; use inherents::{RuntimeString, InherentIdentifier, InherentData, ProvideInherent, MakeFatalError}; #[cfg(feature = "std")] use inherents::{InherentDataProviders, ProvideInherentData}; -use babe_primitives::{BABE_ENGINE_ID, ConsensusLog, Weight, Epoch, RawBabePreDigest}; +use babe_primitives::{BABE_ENGINE_ID, ConsensusLog, BabeWeight, Epoch, RawBabePreDigest}; pub use babe_primitives::{AuthorityId, VRF_OUTPUT_LENGTH, PUBLIC_KEY_LENGTH}; /// The BABE inherent identifier. @@ -106,18 +107,28 @@ impl ProvideInherentData for InherentDataProvider { } } +pub trait Trait: timestamp::Trait { + type EpochDuration: Get; +} + /// The length of the BABE randomness pub const RANDOMNESS_LENGTH: usize = 32; decl_storage! { trait Store for Module as Babe { - NextRandomness: [u8; RANDOMNESS_LENGTH]; + /// Current epoch index. + pub EpochIndex get(epoch_index): u64; - /// Randomness under construction - UnderConstruction: [u8; VRF_OUTPUT_LENGTH]; + /// Current epoch authorities. + pub Authorities get(authorities) config(): Vec<(AuthorityId, BabeWeight)>; - /// Current epoch - pub Authorities get(authorities) config(): Vec<(AuthorityId, Weight)>; + /// Slot at which the current epoch started. It is possible that no + /// block was authored at the given slot and the epoch change was + /// signalled later than this. + pub EpochStartSlot get(epoch_start_slot): u64; + + /// Current slot number. + pub CurrentSlot get(current_slot): u64; /// The epoch randomness for the *current* epoch. /// @@ -129,10 +140,16 @@ decl_storage! { /// (like everything else on-chain) it is public. For example, it can be /// used where a number is needed that cannot have been chosen by an /// adversary, for purposes such as public-coin zero-knowledge proofs. - pub Randomness get(randomness): [u8; RANDOMNESS_LENGTH]; + // NOTE: the following fields don't use the constants to define the + // array size because the metadata API currently doesn't resolve the + // variable to its underlying value. + pub Randomness get(randomness): [u8; 32 /* RANDOMNESS_LENGTH */]; - /// Current epoch index - EpochIndex: u64; + /// Next epoch randomness. + NextRandomness: [u8; 32 /* RANDOMNESS_LENGTH */]; + + /// Randomness under construction. + UnderConstruction: [u8; 32 /* VRF_OUTPUT_LENGTH */]; } } @@ -141,7 +158,7 @@ decl_module! { pub struct Module for enum Call where origin: T::Origin { /// Initialization fn on_initialize() { - for i in Self::get_inherent_digests() + for digest in Self::get_inherent_digests() .logs .iter() .filter_map(|s| s.as_pre_runtime()) @@ -149,9 +166,15 @@ decl_module! { RawBabePreDigest::decode(&mut data) } else { None - }) { + }) + { + if EpochStartSlot::get() == 0 { + EpochStartSlot::put(digest.slot_number); + } + + CurrentSlot::put(digest.slot_number); + Self::deposit_vrf_output(&digest.vrf_output); - Self::deposit_vrf_output(&i.vrf_output); return; } } @@ -180,7 +203,7 @@ impl FindAuthor for Module { } } -impl IsMember for Module { +impl IsMember for Module { fn is_member(authority_id: &AuthorityId) -> bool { >::authorities() .iter() @@ -188,6 +211,13 @@ impl IsMember for Module { } } +impl session::ShouldEndSession for Module { + fn should_end_session(_: T::BlockNumber) -> bool { + let diff = CurrentSlot::get().saturating_sub(EpochStartSlot::get()); + diff >= T::EpochDuration::get() + } +} + impl Module { /// Determine the BABE slot duration based on the Timestamp module configuration. pub fn slot_duration() -> T::Moment { @@ -205,23 +235,17 @@ impl Module { >::digest() } - fn change_epoch(new: Epoch) { - Authorities::put(&new.authorities); - Randomness::put(&new.randomness); - Self::deposit_consensus(ConsensusLog::NextEpochData(new)) - } - fn deposit_vrf_output(vrf_output: &[u8; VRF_OUTPUT_LENGTH]) { UnderConstruction::mutate(|z| z.iter_mut().zip(vrf_output).for_each(|(x, y)| *x^=y)) } /// Call this function exactly once when an epoch changes, to update the /// randomness. Returns the new randomness. - fn randomness_change_epoch(epoch_index: u64) -> [u8; RANDOMNESS_LENGTH] { + fn randomness_change_epoch(next_epoch_index: u64) -> [u8; RANDOMNESS_LENGTH] { let this_randomness = NextRandomness::get(); let next_randomness = compute_randomness( this_randomness, - epoch_index, + next_epoch_index, UnderConstruction::get(), ); UnderConstruction::put(&[0; RANDOMNESS_LENGTH]); @@ -235,13 +259,9 @@ impl OnTimestampSet for Module { fn on_timestamp_set(_moment: T::Moment) { } } -pub trait Duration { - fn babe_epoch_duration() -> u64; -} - -impl session::OneSessionHandler for Module { +impl session::OneSessionHandler for Module { type Key = AuthorityId; - fn on_new_session<'a, I: 'a>(_changed: bool, _validators: I, queued_validators: I) + fn on_new_session<'a, I: 'a>(_changed: bool, validators: I, queued_validators: I) where I: Iterator { use staking::BalanceOf; @@ -249,25 +269,63 @@ impl session::OneSessionHandler, u64>>::convert(b) }; + // Update epoch index let epoch_index = EpochIndex::get() .checked_add(1) .expect("epoch indices will never reach 2^64 before the death of the universe; qed"); EpochIndex::put(epoch_index); - // *Next* epoch's authorities. - let authorities = queued_validators.map(|(account, k)| { + // Update authorities. + let authorities = validators.map(|(account, k)| { (k, to_votes(staking::Module::::stakers(account).total)) }).collect::>(); - // What was the next epoch is now the current epoch - let randomness = Self::randomness_change_epoch(epoch_index); - Self::change_epoch(Epoch { - randomness, - authorities, - epoch_index, - duration: T::babe_epoch_duration(), - }) + Authorities::put(authorities); + + // Update epoch start slot. + let now = CurrentSlot::get(); + EpochStartSlot::mutate(|previous| { + loop { + // on the first epoch we must account for skipping at least one + // whole epoch, in case the first block is authored with a slot + // number far in the past. + if now.saturating_sub(*previous) < T::EpochDuration::get() { + break; + } + + *previous = previous.saturating_add(T::EpochDuration::get()); + } + }); + + // Update epoch randomness. + let next_epoch_index = epoch_index + .checked_add(1) + .expect("epoch indices will never reach 2^64 before the death of the universe; qed"); + + // Returns randomness for the current epoch and computes the *next* + // epoch randomness. + let randomness = Self::randomness_change_epoch(next_epoch_index); + Randomness::put(randomness); + + // After we update the current epoch, we signal the *next* epoch change + // so that nodes can track changes. + let next_authorities = queued_validators.map(|(account, k)| { + (k, to_votes(staking::Module::::stakers(account).total)) + }).collect::>(); + + let next_epoch_start_slot = EpochStartSlot::get().saturating_add(T::EpochDuration::get()); + let next_randomness = NextRandomness::get(); + + let next = Epoch { + epoch_index: next_epoch_index, + start_slot: next_epoch_start_slot, + duration: T::EpochDuration::get(), + authorities: next_authorities, + randomness: next_randomness, + }; + + Self::deposit_consensus(ConsensusLog::NextEpochData(next)) } fn on_disabled(i: usize) { @@ -304,6 +362,7 @@ impl ProvideInherent for Module { let timestamp_based_slot = (timestamp / Self::slot_duration()).saturated_into::(); let seal_slot = data.babe_inherent_data()?; + if timestamp_based_slot == seal_slot { Ok(()) } else { diff --git a/substrate/srml/staking/src/lib.rs b/substrate/srml/staking/src/lib.rs index 7a2d0dbc08..cd3c8d7862 100644 --- a/substrate/srml/staking/src/lib.rs +++ b/substrate/srml/staking/src/lib.rs @@ -451,11 +451,12 @@ pub struct Exposure { pub others: Vec>, } -pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +pub type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; type PositiveImbalanceOf = -<::Currency as Currency<::AccountId>>::PositiveImbalance; + <::Currency as Currency<::AccountId>>::PositiveImbalance; type NegativeImbalanceOf = -<::Currency as Currency<::AccountId>>::NegativeImbalance; + <::Currency as Currency<::AccountId>>::NegativeImbalance; type MomentOf= <::Time as Time>::Moment; type RawAssignment = (::AccountId, ExtendedBalance);