Use BABE instead of AuRa in node (#3171)

* babe: add babe module trait

* babe: track current slot and epoch start slot

* babe: implement ShouldEndSession based on epochs

* babe: rename weight type to avoid ambiguities

* babe: expose epoch start slot in Epoch digest

* babe: use epoch start for validating epoch transitions

* babe: make the epoch duration a parameter type

* babe: remove unused fields from config

* node: update runtime to use babe instead of aura

* node: use babe instead of aura

* core: generate sr25519 keys from seed and add to keystore

* core: remove AuthorityKeyring

* node: remove unused primitive types related to babe crypto

* uniform babe primitives crate import name

* wrap long lines

* babe: fix find_epoch_digest

* fork-tree: fix find_node_where

* node: set babe epoch duration to "10 minutes"

* babe: cleanup import key cache if authorities don't change

* node: make integration test compile (but fail)

* node: bump spec_version

* node: fix import

* babe: don't use constants in storage fields array sizes

* babe: account for first epoch slot way in the past

* babe: signal next epoch change (not current)

* babe: calculate next epoch randomness with next epoch index

* babe: track next epoch in node

* babe: cache current epoch and authorities separately

* babe: generate valid babe vrf proofs in integration test

* babe: cleanup claim_slot

* babe: perform threshold calculation according to spec

* babe: compute relative weight in threshold

* babe: more precise threshold calculation

* babe: use floats for threshold exponent calculation

* babe: update constant c
This commit is contained in:
André Silva
2019-07-24 20:53:04 +01:00
committed by DemiMarie-parity
parent 407970406d
commit 9f50c8fce4
33 changed files with 784 additions and 429 deletions
+102 -38
View File
@@ -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<F> = babe::BabeBlockImport<
FullBackend<F>,
FullExecutor<F>,
<F as crate::ServiceFactory>::Block,
grandpa::BlockImportForService<F>,
<F as crate::ServiceFactory>::RuntimeApi,
client::Client<
FullBackend<F>,
FullExecutor<F>,
<F as crate::ServiceFactory>::Block,
<F as crate::ServiceFactory>::RuntimeApi
>,
>;
/// Node specific configuration
pub struct NodeConfig<F: substrate_service::ServiceFactory> {
/// 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<F>, grandpa::LinkHalfForService<F>)>,
pub import_setup: Option<(
BabeBlockImportForService<F>,
grandpa::LinkHalfForService<F>,
babe::BabeLink,
)>,
/// Tasks that were created by previous setup steps and should be spawned.
pub tasks_to_spawn: Option<Vec<Box<dyn Future<Item = (), Error = ()> + Send>>>,
inherent_data_providers: InherentDataProviders,
}
impl<F> Default for NodeConfig<F> where F: substrate_service::ServiceFactory {
fn default() -> NodeConfig<F> {
NodeConfig {
grandpa_import_setup: None,
import_setup: None,
inherent_data_providers: InherentDataProviders::new(),
tasks_to_spawn: None,
}
}
}
@@ -67,7 +89,7 @@ impl<F> Default for NodeConfig<F> 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::<Factory>::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<Self>
{ |config| <LightComponents<Factory>>::new(config) },
FullImportQueue = AuraImportQueue<Self::Block>
FullImportQueue = BabeImportQueue<Self::Block>
{ |config: &mut FactoryFullConfiguration<Self> , client: Arc<FullClient<Self>>, select_chain: Self::SelectChain| {
let slot_duration = SlotDuration::get_or_compute(&*client)?;
let (block_import, link_half) =
grandpa::block_import::<_, _, _, RuntimeApi, FullClient<Self>, _>(
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<Self::Block>
LightImportQueue = BabeImportQueue<Self::Block>
{ |config: &FactoryFullConfiguration<Self>, client: Arc<LightClient<Self>>| {
#[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<Self>>(
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<FullBackend<Self>, Self::Block>
{ |config: &FactoryFullConfiguration<Self>, client: Arc<FullClient<Self>>| {
@@ -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<<Factory as ServiceFactory>::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::<H256>::default();
digest.push(<DigestItem as CompatibleDigestItem<Pair>>::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(<DigestItem as CompatibleDigestItem>::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 = <DigestItem as CompatibleDigestItem<Pair>>::aura_seal(
let item = <DigestItem as CompatibleDigestItem>::babe_seal(
signature,
);
slot_num += 1;