// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
use crate::{
communication::{
notification::{
BeefyBestBlockSender, BeefyBestBlockStream, BeefyVersionedFinalityProofSender,
BeefyVersionedFinalityProofStream,
},
peers::KnownPeers,
request_response::{
outgoing_requests_engine::OnDemandJustificationsEngine, BeefyJustifsRequestHandler,
},
},
error::Error,
import::BeefyBlockImport,
metrics::register_metrics,
};
use futures::{stream::Fuse, FutureExt, StreamExt};
use log::{debug, error, info, warn};
use parking_lot::Mutex;
use prometheus::Registry;
use sc_client_api::{Backend, BlockBackend, BlockchainEvents, FinalityNotifications, Finalizer};
use sc_consensus::BlockImport;
use sc_network::{NetworkRequest, NotificationService, ProtocolName};
use sc_network_gossip::{GossipEngine, Network as GossipNetwork, Syncing as GossipSyncing};
use sp_api::ProvideRuntimeApi;
use sp_blockchain::{Backend as BlockchainBackend, HeaderBackend};
use sp_consensus::{Error as ConsensusError, SyncOracle};
use sp_consensus_beefy::{
ecdsa_crypto::AuthorityId, BeefyApi, ConsensusLog, MmrRootHash, PayloadProvider, ValidatorSet,
BEEFY_ENGINE_ID,
};
use sp_keystore::KeystorePtr;
use sp_mmr_primitives::MmrApi;
use sp_runtime::traits::{Block, Header as HeaderT, NumberFor, Zero};
use std::{
collections::{BTreeMap, VecDeque},
marker::PhantomData,
sync::Arc,
time::Duration,
};
mod aux_schema;
mod error;
mod keystore;
mod metrics;
mod round;
mod worker;
pub mod communication;
pub mod import;
pub mod justification;
use crate::{
communication::gossip::GossipValidator,
justification::BeefyVersionedFinalityProof,
keystore::BeefyKeystore,
metrics::VoterMetrics,
round::Rounds,
worker::{BeefyWorker, PersistedState},
};
pub use communication::beefy_protocol_name::{
gossip_protocol_name, justifications_protocol_name as justifs_protocol_name,
};
use sp_runtime::generic::OpaqueDigestItemId;
#[cfg(test)]
mod tests;
const LOG_TARGET: &str = "beefy";
const HEADER_SYNC_DELAY: Duration = Duration::from_secs(60);
/// A convenience BEEFY client trait that defines all the type bounds a BEEFY client
/// has to satisfy. Ideally that should actually be a trait alias. Unfortunately as
/// of today, Rust does not allow a type alias to be used as a trait bound. Tracking
/// issue is .
pub trait Client:
BlockchainEvents + HeaderBackend + Finalizer + Send + Sync
where
B: Block,
BE: Backend,
{
// empty
}
impl Client for T
where
B: Block,
BE: Backend,
T: BlockchainEvents
+ HeaderBackend
+ Finalizer
+ ProvideRuntimeApi
+ Send
+ Sync,
{
// empty
}
/// Links between the block importer, the background voter and the RPC layer,
/// to be used by the voter.
#[derive(Clone)]
pub struct BeefyVoterLinks {
// BlockImport -> Voter links
/// Stream of BEEFY signed commitments from block import to voter.
pub from_block_import_justif_stream: BeefyVersionedFinalityProofStream,
// Voter -> RPC links
/// Sends BEEFY signed commitments from voter to RPC.
pub to_rpc_justif_sender: BeefyVersionedFinalityProofSender,
/// Sends BEEFY best block hashes from voter to RPC.
pub to_rpc_best_block_sender: BeefyBestBlockSender,
}
/// Links used by the BEEFY RPC layer, from the BEEFY background voter.
#[derive(Clone)]
pub struct BeefyRPCLinks {
/// Stream of signed commitments coming from the voter.
pub from_voter_justif_stream: BeefyVersionedFinalityProofStream,
/// Stream of BEEFY best block hashes coming from the voter.
pub from_voter_best_beefy_stream: BeefyBestBlockStream,
}
/// Make block importer and link half necessary to tie the background voter to it.
pub fn beefy_block_import_and_links(
wrapped_block_import: I,
backend: Arc,
runtime: Arc,
prometheus_registry: Option,
) -> (BeefyBlockImport, BeefyVoterLinks, BeefyRPCLinks)
where
B: Block,
BE: Backend,
I: BlockImport + Send + Sync,
RuntimeApi: ProvideRuntimeApi + Send + Sync,
RuntimeApi::Api: BeefyApi,
{
// Voter -> RPC links
let (to_rpc_justif_sender, from_voter_justif_stream) =
BeefyVersionedFinalityProofStream::::channel();
let (to_rpc_best_block_sender, from_voter_best_beefy_stream) =
BeefyBestBlockStream::::channel();
// BlockImport -> Voter links
let (to_voter_justif_sender, from_block_import_justif_stream) =
BeefyVersionedFinalityProofStream::::channel();
let metrics = register_metrics(prometheus_registry);
// BlockImport
let import = BeefyBlockImport::new(
backend,
runtime,
wrapped_block_import,
to_voter_justif_sender,
metrics,
);
let voter_links = BeefyVoterLinks {
from_block_import_justif_stream,
to_rpc_justif_sender,
to_rpc_best_block_sender,
};
let rpc_links = BeefyRPCLinks { from_voter_best_beefy_stream, from_voter_justif_stream };
(import, voter_links, rpc_links)
}
/// BEEFY gadget network parameters.
pub struct BeefyNetworkParams {
/// Network implementing gossip, requests and sync-oracle.
pub network: Arc,
/// Syncing service implementing a sync oracle and an event stream for peers.
pub sync: Arc,
/// Handle for receiving notification events.
pub notification_service: Box,
/// Chain specific BEEFY gossip protocol name. See
/// [`communication::beefy_protocol_name::gossip_protocol_name`].
pub gossip_protocol_name: ProtocolName,
/// Chain specific BEEFY on-demand justifications protocol name. See
/// [`communication::beefy_protocol_name::justifications_protocol_name`].
pub justifications_protocol_name: ProtocolName,
pub _phantom: PhantomData,
}
/// BEEFY gadget initialization parameters.
pub struct BeefyParams {
/// BEEFY client
pub client: Arc,
/// Client Backend
pub backend: Arc,
/// BEEFY Payload provider
pub payload_provider: P,
/// Runtime Api Provider
pub runtime: Arc,
/// Local key store
pub key_store: Option,
/// BEEFY voter network params
pub network_params: BeefyNetworkParams,
/// Minimal delta between blocks, BEEFY should vote for
pub min_block_delta: u32,
/// Prometheus metric registry
pub prometheus_registry: Option,
/// Links between the block importer, the background voter and the RPC layer.
pub links: BeefyVoterLinks,
/// Handler for incoming BEEFY justifications requests from a remote peer.
pub on_demand_justifications_handler: BeefyJustifsRequestHandler,
/// Whether running under "Authority" role.
pub is_authority: bool,
}
/// Helper object holding BEEFY worker communication/gossip components.
///
/// These are created once, but will be reused if worker is restarted/reinitialized.
pub(crate) struct BeefyComms {
pub gossip_engine: GossipEngine,
pub gossip_validator: Arc>,
pub on_demand_justifications: OnDemandJustificationsEngine,
}
/// Helper builder object for building [worker::BeefyWorker].
///
/// It has to do it in two steps: initialization and build, because the first step can sleep waiting
/// for certain chain and backend conditions, and while sleeping we still need to pump the
/// GossipEngine. Once initialization is done, the GossipEngine (and other pieces) are added to get
/// the complete [worker::BeefyWorker] object.
pub(crate) struct BeefyWorkerBuilder {
// utilities
backend: Arc,
runtime: Arc,
key_store: BeefyKeystore,
// voter metrics
metrics: Option,
persisted_state: PersistedState,
}
impl BeefyWorkerBuilder
where
B: Block + codec::Codec,
BE: Backend,
R: ProvideRuntimeApi,
R::Api: BeefyApi,
{
/// This will wait for the chain to enable BEEFY (if not yet enabled) and also wait for the
/// backend to sync all headers required by the voter to build a contiguous chain of mandatory
/// justifications. Then it builds the initial voter state using a combination of previously
/// persisted state in AUX DB and latest chain information/progress.
///
/// Returns a sane `BeefyWorkerBuilder` that can build the `BeefyWorker`.
pub async fn async_initialize(
backend: Arc,
runtime: Arc,
key_store: BeefyKeystore,
metrics: Option,
min_block_delta: u32,
gossip_validator: Arc>,
finality_notifications: &mut Fuse>,
is_authority: bool,
) -> Result {
// Wait for BEEFY pallet to be active before starting voter.
let (beefy_genesis, best_grandpa) =
wait_for_runtime_pallet(&*runtime, finality_notifications).await?;
let persisted_state = Self::load_or_init_state(
beefy_genesis,
best_grandpa,
min_block_delta,
backend.clone(),
runtime.clone(),
&key_store,
&metrics,
is_authority,
)
.await?;
// Update the gossip validator with the right starting round and set id.
persisted_state
.gossip_filter_config()
.map(|f| gossip_validator.update_filter(f))?;
Ok(BeefyWorkerBuilder { backend, runtime, key_store, metrics, persisted_state })
}
/// Takes rest of missing pieces as params and builds the `BeefyWorker`.
pub fn build