Beefy on-demand justifications as a custom RequestResponse protocol (#12124)

* client/beefy: create communication module and move gossip there

* client/beefy: move beefy_protocol_name module to communication

* client/beefy: move notification module under communication

* client/beefy: add incoming request_response protocol handler

* client/beefy: keep track of connected peers and their progress

* client/beefy: add logic for generating Justif requests

* client/beefy: cancel outdated on-demand justification requests

* try Andre's suggestion for JustificationEngine

* justif engine add justifs validation

* client/beefy: impl OnDemandJustificationsEngine async next()

* move beefy proto name test

* client/beefy: initialize OnDemandJustificationsEngine

* client/tests: allow for custom req-resp protocols

* client/beefy: on-demand-justif: implement simple peer selection strategy

* client/beefy: fix voter initialization

Fix corner case where voter gets a single burst of finality
notifications just when it starts.

The notification stream was consumed by "wait_for_pallet" logic,
then main loop would subscribe to finality notifications, but by that
time some notifications might've been lost.

Fix this by subscribing the main loop to notifications before waiting
for pallet to become available. Share the same stream with the main loop
so that notifications for blocks before pallet available are ignored,
while _all_ notifications after pallet available are processed.

Add regression test for this.

Signed-off-by: acatangiu <adrian@parity.io>

* client/beefy: make sure justif requests are always out for mandatory blocks

* client/beefy: add test for on-demand justifications sync

* client/beefy: tweak main loop event processing order

* client/beefy: run on-demand-justif-handler under same async task as voter

* client/beefy: add test for known-peers

* client/beefy: reorg request-response module

* client/beefy: add issue references for future work todos

* client/beefy: consolidate on-demand-justifications engine state machine

Signed-off-by: acatangiu <adrian@parity.io>

* client/beefy: fix for polkadot companion

* client/beefy: implement review suggestions

* cargo fmt and clippy

* fix merge damage

* fix rust-doc

* fix merge damage

* fix merge damage

* client/beefy: add test for justif proto name

Signed-off-by: acatangiu <adrian@parity.io>
This commit is contained in:
Adrian Catangiu
2022-10-03 16:00:57 +03:00
committed by GitHub
parent bb9d2fa75a
commit 2a27545afe
14 changed files with 1208 additions and 219 deletions
+67 -64
View File
@@ -17,10 +17,12 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use beefy_primitives::{BeefyApi, MmrRootHash};
use parking_lot::Mutex;
use prometheus::Registry;
use sc_client_api::{Backend, BlockchainEvents, Finalizer};
use sc_client_api::{Backend, BlockBackend, BlockchainEvents, Finalizer};
use sc_consensus::BlockImport;
use sc_network::ProtocolName;
use sc_network_common::service::NetworkRequest;
use sc_network_gossip::Network as GossipNetwork;
use sp_api::ProvideRuntimeApi;
use sp_blockchain::HeaderBackend;
@@ -28,68 +30,38 @@ use sp_consensus::{Error as ConsensusError, SyncOracle};
use sp_keystore::SyncCryptoStorePtr;
use sp_mmr_primitives::MmrApi;
use sp_runtime::traits::Block;
use std::sync::Arc;
use std::{marker::PhantomData, sync::Arc};
mod error;
mod gossip;
mod keystore;
mod metrics;
mod round;
mod worker;
pub mod communication;
pub mod import;
pub mod justification;
pub mod notification;
#[cfg(test)]
mod tests;
use crate::{
import::BeefyBlockImport,
notification::{
BeefyBestBlockSender, BeefyBestBlockStream, BeefyVersionedFinalityProofSender,
BeefyVersionedFinalityProofStream,
communication::{
notification::{
BeefyBestBlockSender, BeefyBestBlockStream, BeefyVersionedFinalityProofSender,
BeefyVersionedFinalityProofStream,
},
peers::KnownPeers,
request_response::{
outgoing_requests_engine::OnDemandJustificationsEngine, BeefyJustifsRequestHandler,
},
},
import::BeefyBlockImport,
};
pub use beefy_protocol_name::standard_name as protocol_standard_name;
pub(crate) mod beefy_protocol_name {
use sc_chain_spec::ChainSpec;
use sc_network::ProtocolName;
const NAME: &str = "/beefy/1";
/// Old names for the notifications protocol, used for backward compatibility.
pub(crate) const LEGACY_NAMES: [&str; 1] = ["/paritytech/beefy/1"];
/// Name of the notifications protocol used by BEEFY.
///
/// Must be registered towards the networking in order for BEEFY to properly function.
pub fn standard_name<Hash: AsRef<[u8]>>(
genesis_hash: &Hash,
chain_spec: &Box<dyn ChainSpec>,
) -> ProtocolName {
let genesis_hash = genesis_hash.as_ref();
let chain_prefix = match chain_spec.fork_id() {
Some(fork_id) => format!("/{}/{}", array_bytes::bytes2hex("", genesis_hash), fork_id),
None => format!("/{}", array_bytes::bytes2hex("", genesis_hash)),
};
format!("{}{}", chain_prefix, NAME).into()
}
}
/// Returns the configuration value to put in
/// [`sc_network::config::NetworkConfiguration::extra_sets`].
/// For standard protocol name see [`beefy_protocol_name::standard_name`].
pub fn beefy_peers_set_config(
protocol_name: ProtocolName,
) -> sc_network_common::config::NonDefaultSetConfig {
let mut cfg = sc_network_common::config::NonDefaultSetConfig::new(protocol_name, 1024 * 1024);
cfg.allow_non_reserved(25, 25);
cfg.add_fallback_names(beefy_protocol_name::LEGACY_NAMES.iter().map(|&n| n.into()).collect());
cfg
}
pub use communication::beefy_protocol_name::{
gossip_protocol_name, justifications_protocol_name as justifs_protocol_name,
};
/// 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
@@ -159,13 +131,13 @@ where
{
// Voter -> RPC links
let (to_rpc_justif_sender, from_voter_justif_stream) =
notification::BeefyVersionedFinalityProofStream::<B>::channel();
BeefyVersionedFinalityProofStream::<B>::channel();
let (to_rpc_best_block_sender, from_voter_best_beefy_stream) =
notification::BeefyBestBlockStream::<B>::channel();
BeefyBestBlockStream::<B>::channel();
// BlockImport -> Voter links
let (to_voter_justif_sender, from_block_import_justif_stream) =
notification::BeefyVersionedFinalityProofStream::<B>::channel();
BeefyVersionedFinalityProofStream::<B>::channel();
// BlockImport
let import =
@@ -180,6 +152,24 @@ where
(import, voter_links, rpc_links)
}
/// BEEFY gadget network parameters.
pub struct BeefyNetworkParams<B, N>
where
B: Block,
N: GossipNetwork<B> + NetworkRequest + SyncOracle + Send + Sync + 'static,
{
/// Network implementing gossip, requests and sync-oracle.
pub network: Arc<N>,
/// 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<B>,
}
/// BEEFY gadget initialization parameters.
pub struct BeefyParams<B, BE, C, N, R>
where
@@ -188,7 +178,7 @@ where
C: Client<B, BE>,
R: ProvideRuntimeApi<B>,
R::Api: BeefyApi<B> + MmrApi<B, MmrRootHash>,
N: GossipNetwork<B> + Clone + SyncOracle + Send + Sync + 'static,
N: GossipNetwork<B> + NetworkRequest + SyncOracle + Send + Sync + 'static,
{
/// BEEFY client
pub client: Arc<C>,
@@ -198,16 +188,16 @@ where
pub runtime: Arc<R>,
/// Local key store
pub key_store: Option<SyncCryptoStorePtr>,
/// Gossip network
pub network: N,
/// BEEFY voter network params
pub network_params: BeefyNetworkParams<B, N>,
/// Minimal delta between blocks, BEEFY should vote for
pub min_block_delta: u32,
/// Prometheus metric registry
pub prometheus_registry: Option<Registry>,
/// Chain specific GRANDPA protocol name. See [`beefy_protocol_name::standard_name`].
pub protocol_name: ProtocolName,
/// Links between the block importer, the background voter and the RPC layer.
pub links: BeefyVoterLinks<B>,
/// Handler for incoming BEEFY justifications requests from a remote peer.
pub on_demand_justifications_handler: BeefyJustifsRequestHandler<B, C>,
}
/// Start the BEEFY gadget.
@@ -217,32 +207,43 @@ pub async fn start_beefy_gadget<B, BE, C, N, R>(beefy_params: BeefyParams<B, BE,
where
B: Block,
BE: Backend<B>,
C: Client<B, BE>,
C: Client<B, BE> + BlockBackend<B>,
R: ProvideRuntimeApi<B>,
R::Api: BeefyApi<B> + MmrApi<B, MmrRootHash>,
N: GossipNetwork<B> + Clone + SyncOracle + Send + Sync + 'static,
N: GossipNetwork<B> + NetworkRequest + SyncOracle + Send + Sync + 'static,
{
let BeefyParams {
client,
backend,
runtime,
key_store,
network,
network_params,
min_block_delta,
prometheus_registry,
protocol_name,
links,
on_demand_justifications_handler,
} = beefy_params;
let sync_oracle = network.clone();
let gossip_validator = Arc::new(gossip::GossipValidator::new());
let BeefyNetworkParams { network, gossip_protocol_name, justifications_protocol_name, .. } =
network_params;
let known_peers = Arc::new(Mutex::new(KnownPeers::new()));
let gossip_validator =
Arc::new(communication::gossip::GossipValidator::new(known_peers.clone()));
let gossip_engine = sc_network_gossip::GossipEngine::new(
network,
protocol_name,
network.clone(),
gossip_protocol_name,
gossip_validator.clone(),
None,
);
let on_demand_justifications = OnDemandJustificationsEngine::new(
network.clone(),
runtime.clone(),
justifications_protocol_name,
known_peers.clone(),
);
let metrics =
prometheus_registry.as_ref().map(metrics::Metrics::register).and_then(
|result| match result {
@@ -261,10 +262,12 @@ where
client,
backend,
runtime,
sync_oracle,
network,
key_store: key_store.into(),
known_peers,
gossip_engine,
gossip_validator,
on_demand_justifications,
links,
metrics,
min_block_delta,
@@ -272,5 +275,5 @@ where
let worker = worker::BeefyWorker::<_, _, _, _, _>::new(worker_params);
worker.run().await
futures::future::join(worker.run(), on_demand_justifications_handler.run()).await;
}