diff --git a/client/beefy/rpc/src/lib.rs b/client/beefy/rpc/src/lib.rs
index 3be182ceb8f39..0af474116e6d0 100644
--- a/client/beefy/rpc/src/lib.rs
+++ b/client/beefy/rpc/src/lib.rs
@@ -35,7 +35,9 @@ use jsonrpsee::{
};
use log::warn;
-use beefy_gadget::notification::{BeefyBestBlockStream, BeefyVersionedFinalityProofStream};
+use beefy_gadget::communication::notification::{
+ BeefyBestBlockStream, BeefyVersionedFinalityProofStream,
+};
mod notification;
@@ -165,8 +167,8 @@ mod tests {
use super::*;
use beefy_gadget::{
+ communication::notification::BeefyVersionedFinalityProofSender,
justification::BeefyVersionedFinalityProof,
- notification::{BeefyBestBlockStream, BeefyVersionedFinalityProofSender},
};
use beefy_primitives::{known_payload_ids, Payload, SignedCommitment};
use codec::{Decode, Encode};
diff --git a/client/beefy/src/gossip.rs b/client/beefy/src/communication/gossip.rs
similarity index 94%
rename from client/beefy/src/gossip.rs
rename to client/beefy/src/communication/gossip.rs
index 02d5efe9e0e58..6c41a2e48932a 100644
--- a/client/beefy/src/gossip.rs
+++ b/client/beefy/src/communication/gossip.rs
@@ -16,7 +16,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-use std::{collections::BTreeMap, time::Duration};
+use std::{collections::BTreeMap, sync::Arc, time::Duration};
use sc_network::PeerId;
use sc_network_gossip::{MessageIntent, ValidationResult, Validator, ValidatorContext};
@@ -28,13 +28,12 @@ use log::{debug, trace};
use parking_lot::{Mutex, RwLock};
use wasm_timer::Instant;
+use crate::{communication::peers::KnownPeers, keystore::BeefyKeystore};
use beefy_primitives::{
crypto::{Public, Signature},
VoteMessage,
};
-use crate::keystore::BeefyKeystore;
-
// Timeout for rebroadcasting messages.
const REBROADCAST_AFTER: Duration = Duration::from_secs(60 * 5);
@@ -103,17 +102,19 @@ where
topic: B::Hash,
known_votes: RwLock>,
next_rebroadcast: Mutex,
+ known_peers: Arc>>,
}
impl GossipValidator
where
B: Block,
{
- pub fn new() -> GossipValidator {
+ pub fn new(known_peers: Arc>>) -> GossipValidator {
GossipValidator {
topic: topic::(),
known_votes: RwLock::new(KnownVotes::new()),
next_rebroadcast: Mutex::new(Instant::now() + REBROADCAST_AFTER),
+ known_peers,
}
}
@@ -165,6 +166,7 @@ where
if BeefyKeystore::verify(&msg.id, &msg.signature, &msg.commitment.encode()) {
self.known_votes.write().add_known(&round, msg_hash);
+ self.known_peers.lock().note_vote_for(*sender, round);
return ValidationResult::ProcessAndKeep(self.topic)
} else {
// TODO: report peer
@@ -271,7 +273,7 @@ mod tests {
#[test]
fn note_and_drop_round_works() {
- let gv = GossipValidator::::new();
+ let gv = GossipValidator::::new(Arc::new(Mutex::new(KnownPeers::new())));
gv.note_round(1u64);
@@ -298,7 +300,7 @@ mod tests {
#[test]
fn note_same_round_twice() {
- let gv = GossipValidator::::new();
+ let gv = GossipValidator::::new(Arc::new(Mutex::new(KnownPeers::new())));
gv.note_round(3u64);
gv.note_round(7u64);
@@ -355,7 +357,7 @@ mod tests {
#[test]
fn should_avoid_verifying_signatures_twice() {
- let gv = GossipValidator::::new();
+ let gv = GossipValidator::::new(Arc::new(Mutex::new(KnownPeers::new())));
let sender = sc_network::PeerId::random();
let mut context = TestContext;
@@ -391,7 +393,7 @@ mod tests {
#[test]
fn messages_allowed_and_expired() {
- let gv = GossipValidator::::new();
+ let gv = GossipValidator::::new(Arc::new(Mutex::new(KnownPeers::new())));
let sender = sc_network::PeerId::random();
let topic = Default::default();
let intent = MessageIntent::Broadcast;
@@ -434,7 +436,7 @@ mod tests {
#[test]
fn messages_rebroadcast() {
- let gv = GossipValidator::::new();
+ let gv = GossipValidator::::new(Arc::new(Mutex::new(KnownPeers::new())));
let sender = sc_network::PeerId::random();
let topic = Default::default();
diff --git a/client/beefy/src/communication/mod.rs b/client/beefy/src/communication/mod.rs
new file mode 100644
index 0000000000000..93646677c0ecd
--- /dev/null
+++ b/client/beefy/src/communication/mod.rs
@@ -0,0 +1,118 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2022 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 .
+
+//! Communication streams for the BEEFY networking protocols.
+
+pub mod notification;
+pub mod request_response;
+
+pub(crate) mod gossip;
+pub(crate) mod peers;
+
+pub(crate) mod beefy_protocol_name {
+ use array_bytes::bytes2hex;
+ use sc_network::ProtocolName;
+
+ /// BEEFY votes gossip protocol name suffix.
+ const GOSSIP_NAME: &str = "/beefy/1";
+ /// BEEFY justifications protocol name suffix.
+ const JUSTIFICATIONS_NAME: &str = "/beefy/justifications/1";
+
+ /// Old names for the gossip protocol, used for backward compatibility.
+ pub(super) const LEGACY_NAMES: [&str; 1] = ["/paritytech/beefy/1"];
+
+ /// Name of the votes gossip protocol used by BEEFY.
+ ///
+ /// Must be registered towards the networking in order for BEEFY voter to properly function.
+ pub fn gossip_protocol_name>(
+ genesis_hash: Hash,
+ fork_id: Option<&str>,
+ ) -> ProtocolName {
+ let genesis_hash = genesis_hash.as_ref();
+ if let Some(fork_id) = fork_id {
+ format!("/{}/{}{}", bytes2hex("", genesis_hash), fork_id, GOSSIP_NAME).into()
+ } else {
+ format!("/{}{}", bytes2hex("", genesis_hash), GOSSIP_NAME).into()
+ }
+ }
+
+ /// Name of the BEEFY justifications request-response protocol.
+ pub fn justifications_protocol_name>(
+ genesis_hash: Hash,
+ fork_id: Option<&str>,
+ ) -> ProtocolName {
+ let genesis_hash = genesis_hash.as_ref();
+ if let Some(fork_id) = fork_id {
+ format!("/{}/{}{}", bytes2hex("", genesis_hash), fork_id, JUSTIFICATIONS_NAME).into()
+ } else {
+ format!("/{}{}", bytes2hex("", genesis_hash), JUSTIFICATIONS_NAME).into()
+ }
+ }
+}
+
+/// Returns the configuration value to put in
+/// [`sc_network::config::NetworkConfiguration::extra_sets`].
+/// For standard protocol name see [`beefy_protocol_name::gossip_protocol_name`].
+pub fn beefy_peers_set_config(
+ gossip_protocol_name: sc_network::ProtocolName,
+) -> sc_network_common::config::NonDefaultSetConfig {
+ let mut cfg =
+ sc_network_common::config::NonDefaultSetConfig::new(gossip_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
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use sp_core::H256;
+
+ #[test]
+ fn beefy_protocols_names() {
+ use beefy_protocol_name::{gossip_protocol_name, justifications_protocol_name};
+ // Create protocol name using random genesis hash.
+ let genesis_hash = H256::random();
+ let genesis_hex = array_bytes::bytes2hex("", genesis_hash.as_ref());
+
+ let expected_gossip_name = format!("/{}/beefy/1", genesis_hex);
+ let gossip_proto_name = gossip_protocol_name(&genesis_hash, None);
+ assert_eq!(gossip_proto_name.to_string(), expected_gossip_name);
+
+ let expected_justif_name = format!("/{}/beefy/justifications/1", genesis_hex);
+ let justif_proto_name = justifications_protocol_name(&genesis_hash, None);
+ assert_eq!(justif_proto_name.to_string(), expected_justif_name);
+
+ // Create protocol name using hardcoded genesis hash. Verify exact representation.
+ let genesis_hash = [
+ 50, 4, 60, 123, 58, 106, 216, 246, 194, 188, 139, 193, 33, 212, 202, 171, 9, 55, 123,
+ 94, 8, 43, 12, 251, 187, 57, 173, 19, 188, 74, 205, 147,
+ ];
+ let genesis_hex = "32043c7b3a6ad8f6c2bc8bc121d4caab09377b5e082b0cfbbb39ad13bc4acd93";
+
+ let expected_gossip_name = format!("/{}/beefy/1", genesis_hex);
+ let gossip_proto_name = gossip_protocol_name(&genesis_hash, None);
+ assert_eq!(gossip_proto_name.to_string(), expected_gossip_name);
+
+ let expected_justif_name = format!("/{}/beefy/justifications/1", genesis_hex);
+ let justif_proto_name = justifications_protocol_name(&genesis_hash, None);
+ assert_eq!(justif_proto_name.to_string(), expected_justif_name);
+ }
+}
diff --git a/client/beefy/src/notification.rs b/client/beefy/src/communication/notification.rs
similarity index 100%
rename from client/beefy/src/notification.rs
rename to client/beefy/src/communication/notification.rs
diff --git a/client/beefy/src/communication/peers.rs b/client/beefy/src/communication/peers.rs
new file mode 100644
index 0000000000000..0e20a0f4e0ff6
--- /dev/null
+++ b/client/beefy/src/communication/peers.rs
@@ -0,0 +1,131 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2022 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 .
+
+//! Logic for keeping track of BEEFY peers.
+
+// TODO (issue #12296): replace this naive peer tracking with generic one that infers data
+// from multiple network protocols.
+
+use sc_network::PeerId;
+use sp_runtime::traits::{Block, NumberFor, Zero};
+use std::collections::{HashMap, VecDeque};
+
+struct PeerData {
+ last_voted_on: NumberFor,
+}
+
+impl Default for PeerData {
+ fn default() -> Self {
+ PeerData { last_voted_on: Zero::zero() }
+ }
+}
+
+/// Keep a simple map of connected peers
+/// and the most recent voting round they participated in.
+pub struct KnownPeers {
+ live: HashMap>,
+}
+
+impl KnownPeers {
+ pub fn new() -> Self {
+ Self { live: HashMap::new() }
+ }
+
+ /// Add new connected `peer`.
+ pub fn add_new(&mut self, peer: PeerId) {
+ self.live.entry(peer).or_default();
+ }
+
+ /// Note vote round number for `peer`.
+ pub fn note_vote_for(&mut self, peer: PeerId, round: NumberFor) {
+ let data = self.live.entry(peer).or_default();
+ data.last_voted_on = round.max(data.last_voted_on);
+ }
+
+ /// Remove connected `peer`.
+ pub fn remove(&mut self, peer: &PeerId) {
+ self.live.remove(peer);
+ }
+
+ /// Return _filtered and cloned_ list of peers that have voted on `block` or higher.
+ pub fn at_least_at_block(&self, block: NumberFor) -> VecDeque {
+ self.live
+ .iter()
+ .filter_map(|(k, v)| (v.last_voted_on >= block).then_some(k))
+ .cloned()
+ .collect()
+ }
+
+ /// Answer whether `peer` is part of `KnownPeers` set.
+ pub fn contains(&self, peer: &PeerId) -> bool {
+ self.live.contains_key(peer)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn should_track_known_peers_progress() {
+ let (alice, bob, charlie) = (PeerId::random(), PeerId::random(), PeerId::random());
+ let mut peers = KnownPeers::::new();
+ assert!(peers.live.is_empty());
+
+ // Alice and Bob new connected peers.
+ peers.add_new(alice);
+ peers.add_new(bob);
+ // 'Tracked' Bob seen voting for 5.
+ peers.note_vote_for(bob, 5);
+ // Previously unseen Charlie now seen voting for 10.
+ peers.note_vote_for(charlie, 10);
+
+ assert_eq!(peers.live.len(), 3);
+ assert!(peers.contains(&alice));
+ assert!(peers.contains(&bob));
+ assert!(peers.contains(&charlie));
+
+ // Get peers at block >= 5
+ let at_5 = peers.at_least_at_block(5);
+ // Should be Bob and Charlie
+ assert_eq!(at_5.len(), 2);
+ assert!(at_5.contains(&bob));
+ assert!(at_5.contains(&charlie));
+
+ // 'Tracked' Alice seen voting for 10.
+ peers.note_vote_for(alice, 10);
+
+ // Get peers at block >= 9
+ let at_9 = peers.at_least_at_block(9);
+ // Should be Charlie and Alice
+ assert_eq!(at_9.len(), 2);
+ assert!(at_9.contains(&charlie));
+ assert!(at_9.contains(&alice));
+
+ // Remove Alice
+ peers.remove(&alice);
+ assert_eq!(peers.live.len(), 2);
+ assert!(!peers.contains(&alice));
+
+ // Get peers at block >= 9
+ let at_9 = peers.at_least_at_block(9);
+ // Now should be just Charlie
+ assert_eq!(at_9.len(), 1);
+ assert!(at_9.contains(&charlie));
+ }
+}
diff --git a/client/beefy/src/communication/request_response/incoming_requests_handler.rs b/client/beefy/src/communication/request_response/incoming_requests_handler.rs
new file mode 100644
index 0000000000000..c0910a60fba3b
--- /dev/null
+++ b/client/beefy/src/communication/request_response/incoming_requests_handler.rs
@@ -0,0 +1,193 @@
+// Copyright 2022 Parity Technologies (UK) Ltd.
+// This file is part of Substrate.
+
+// Substrate is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Substrate is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Substrate. If not, see .
+
+//! Helper for handling (i.e. answering) BEEFY justifications requests from a remote peer.
+
+use beefy_primitives::BEEFY_ENGINE_ID;
+use codec::Decode;
+use futures::{
+ channel::{mpsc, oneshot},
+ StreamExt,
+};
+use log::{debug, trace};
+use sc_client_api::BlockBackend;
+use sc_network::{config as netconfig, config::RequestResponseConfig, PeerId, ReputationChange};
+use sc_network_common::protocol::ProtocolName;
+use sp_runtime::{generic::BlockId, traits::Block};
+use std::{marker::PhantomData, sync::Arc};
+
+use crate::communication::request_response::{
+ on_demand_justifications_protocol_config, Error, JustificationRequest,
+};
+
+/// A request coming in, including a sender for sending responses.
+#[derive(Debug)]
+pub(crate) struct IncomingRequest {
+ /// `PeerId` of sending peer.
+ pub peer: PeerId,
+ /// The sent request.
+ pub payload: JustificationRequest,
+ /// Sender for sending response back.
+ pub pending_response: oneshot::Sender,
+}
+
+impl IncomingRequest {
+ /// Create new `IncomingRequest`.
+ pub fn new(
+ peer: PeerId,
+ payload: JustificationRequest,
+ pending_response: oneshot::Sender,
+ ) -> Self {
+ Self { peer, payload, pending_response }
+ }
+
+ /// Try building from raw network request.
+ ///
+ /// This function will fail if the request cannot be decoded and will apply passed in
+ /// reputation changes in that case.
+ ///
+ /// Params:
+ /// - The raw request to decode
+ /// - Reputation changes to apply for the peer in case decoding fails.
+ pub fn try_from_raw(
+ raw: netconfig::IncomingRequest,
+ reputation_changes: Vec,
+ ) -> Result {
+ let netconfig::IncomingRequest { payload, peer, pending_response } = raw;
+ let payload = match JustificationRequest::decode(&mut payload.as_ref()) {
+ Ok(payload) => payload,
+ Err(err) => {
+ let response = netconfig::OutgoingResponse {
+ result: Err(()),
+ reputation_changes,
+ sent_feedback: None,
+ };
+ if let Err(_) = pending_response.send(response) {
+ return Err(Error::DecodingErrorNoReputationChange(peer, err))
+ }
+ return Err(Error::DecodingError(peer, err))
+ },
+ };
+ Ok(Self::new(peer, payload, pending_response))
+ }
+}
+
+/// Receiver for incoming BEEFY justifications requests.
+///
+/// Takes care of decoding and handling of invalid encoded requests.
+pub(crate) struct IncomingRequestReceiver {
+ raw: mpsc::Receiver,
+}
+
+impl IncomingRequestReceiver {
+ pub fn new(inner: mpsc::Receiver) -> Self {
+ Self { raw: inner }
+ }
+
+ /// Try to receive the next incoming request.
+ ///
+ /// Any received request will be decoded, on decoding errors the provided reputation changes
+ /// will be applied and an error will be reported.
+ pub async fn recv(&mut self, reputation_changes: F) -> Result, Error>
+ where
+ B: Block,
+ F: FnOnce() -> Vec,
+ {
+ let req = match self.raw.next().await {
+ None => return Err(Error::RequestChannelExhausted),
+ Some(raw) => IncomingRequest::::try_from_raw(raw, reputation_changes())?,
+ };
+ Ok(req)
+ }
+}
+
+/// Handler for incoming BEEFY justifications requests from a remote peer.
+pub struct BeefyJustifsRequestHandler {
+ pub(crate) request_receiver: IncomingRequestReceiver,
+ pub(crate) justif_protocol_name: ProtocolName,
+ pub(crate) client: Arc,
+ pub(crate) _block: PhantomData,
+}
+
+impl BeefyJustifsRequestHandler
+where
+ B: Block,
+ Client: BlockBackend + Send + Sync,
+{
+ /// Create a new [`BeefyJustifsRequestHandler`].
+ pub fn new>(
+ genesis_hash: Hash,
+ fork_id: Option<&str>,
+ client: Arc,
+ ) -> (Self, RequestResponseConfig) {
+ let (request_receiver, config) =
+ on_demand_justifications_protocol_config(genesis_hash, fork_id);
+ let justif_protocol_name = config.name.clone();
+
+ (Self { request_receiver, justif_protocol_name, client, _block: PhantomData }, config)
+ }
+
+ /// Network request-response protocol name used by this handler.
+ pub fn protocol_name(&self) -> ProtocolName {
+ self.justif_protocol_name.clone()
+ }
+
+ // Sends back justification response if justification found in client backend.
+ fn handle_request(&self, request: IncomingRequest) -> Result<(), Error> {
+ // TODO (issue #12293): validate `request` and change peer reputation for invalid requests.
+
+ let maybe_encoded_proof = self
+ .client
+ .justifications(&BlockId::Number(request.payload.begin))
+ .map_err(Error::Client)?
+ .and_then(|justifs| justifs.get(BEEFY_ENGINE_ID).cloned())
+ // No BEEFY justification present.
+ .ok_or(());
+
+ request
+ .pending_response
+ .send(netconfig::OutgoingResponse {
+ result: maybe_encoded_proof,
+ reputation_changes: Vec::new(),
+ sent_feedback: None,
+ })
+ .map_err(|_| Error::SendResponse)
+ }
+
+ /// Run [`BeefyJustifsRequestHandler`].
+ pub async fn run(mut self) {
+ trace!(target: "beefy::sync", "🥩 Running BeefyJustifsRequestHandler");
+
+ while let Ok(request) = self.request_receiver.recv(|| vec![]).await {
+ let peer = request.peer;
+ match self.handle_request(request) {
+ Ok(()) => {
+ debug!(
+ target: "beefy::sync",
+ "🥩 Handled BEEFY justification request from {:?}.", peer
+ )
+ },
+ Err(e) => {
+ // TODO (issue #12293): apply reputation changes here based on error type.
+ debug!(
+ target: "beefy::sync",
+ "🥩 Failed to handle BEEFY justification request from {:?}: {}", peer, e,
+ )
+ },
+ }
+ }
+ }
+}
diff --git a/client/beefy/src/communication/request_response/mod.rs b/client/beefy/src/communication/request_response/mod.rs
new file mode 100644
index 0000000000000..c83bb9d57e91b
--- /dev/null
+++ b/client/beefy/src/communication/request_response/mod.rs
@@ -0,0 +1,101 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2022 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 .
+
+//! Request/response protocol for syncing BEEFY justifications.
+
+mod incoming_requests_handler;
+pub(crate) mod outgoing_requests_engine;
+
+pub use incoming_requests_handler::BeefyJustifsRequestHandler;
+
+use futures::channel::mpsc;
+use std::time::Duration;
+
+use codec::{Decode, Encode, Error as CodecError};
+use sc_network::{config::RequestResponseConfig, PeerId};
+use sp_runtime::traits::{Block, NumberFor};
+
+use crate::communication::beefy_protocol_name::justifications_protocol_name;
+use incoming_requests_handler::IncomingRequestReceiver;
+
+// 10 seems reasonable, considering justifs are explicitly requested only
+// for mandatory blocks, by nodes that are syncing/catching-up.
+const JUSTIF_CHANNEL_SIZE: usize = 10;
+
+const MAX_RESPONSE_SIZE: u64 = 1024 * 1024;
+const JUSTIF_REQUEST_TIMEOUT: Duration = Duration::from_secs(3);
+
+/// Get the configuration for the BEEFY justifications Request/response protocol.
+///
+/// Returns a receiver for messages received on this protocol and the requested
+/// `ProtocolConfig`.
+///
+/// Consider using [`BeefyJustifsRequestHandler`] instead of this low-level function.
+pub(crate) fn on_demand_justifications_protocol_config>(
+ genesis_hash: Hash,
+ fork_id: Option<&str>,
+) -> (IncomingRequestReceiver, RequestResponseConfig) {
+ let name = justifications_protocol_name(genesis_hash, fork_id);
+ let fallback_names = vec![];
+ let (tx, rx) = mpsc::channel(JUSTIF_CHANNEL_SIZE);
+ let rx = IncomingRequestReceiver::new(rx);
+ let cfg = RequestResponseConfig {
+ name,
+ fallback_names,
+ max_request_size: 32,
+ max_response_size: MAX_RESPONSE_SIZE,
+ // We are connected to all validators:
+ request_timeout: JUSTIF_REQUEST_TIMEOUT,
+ inbound_queue: Some(tx),
+ };
+ (rx, cfg)
+}
+
+/// BEEFY justification request.
+#[derive(Debug, Clone, Encode, Decode)]
+pub struct JustificationRequest {
+ /// Start collecting proofs from this block.
+ pub begin: NumberFor,
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+ #[error(transparent)]
+ Client(#[from] sp_blockchain::Error),
+
+ #[error(transparent)]
+ RuntimeApi(#[from] sp_api::ApiError),
+
+ /// Decoding failed, we were able to change the peer's reputation accordingly.
+ #[error("Decoding request failed for peer {0}.")]
+ DecodingError(PeerId, #[source] CodecError),
+
+ /// Decoding failed, but sending reputation change failed.
+ #[error("Decoding request failed for peer {0}, and changing reputation failed.")]
+ DecodingErrorNoReputationChange(PeerId, #[source] CodecError),
+
+ /// Incoming request stream exhausted. Should only happen on shutdown.
+ #[error("Incoming request channel got closed.")]
+ RequestChannelExhausted,
+
+ #[error("Failed to send response.")]
+ SendResponse,
+
+ #[error("Received invalid response.")]
+ InvalidResponse,
+}
diff --git a/client/beefy/src/communication/request_response/outgoing_requests_engine.rs b/client/beefy/src/communication/request_response/outgoing_requests_engine.rs
new file mode 100644
index 0000000000000..e22958e19cd2e
--- /dev/null
+++ b/client/beefy/src/communication/request_response/outgoing_requests_engine.rs
@@ -0,0 +1,245 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2022 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 .
+
+//! Generating request logic for request/response protocol for syncing BEEFY justifications.
+
+use beefy_primitives::{crypto::AuthorityId, BeefyApi, ValidatorSet};
+use codec::Encode;
+use futures::{
+ channel::{oneshot, oneshot::Canceled},
+ stream::{self, StreamExt},
+};
+use log::{debug, error, warn};
+use parking_lot::Mutex;
+use sc_network::{PeerId, ProtocolName};
+use sc_network_common::{
+ request_responses::{IfDisconnected, RequestFailure},
+ service::NetworkRequest,
+};
+use sp_api::ProvideRuntimeApi;
+use sp_runtime::{
+ generic::BlockId,
+ traits::{Block, NumberFor},
+};
+use std::{collections::VecDeque, result::Result, sync::Arc};
+
+use crate::{
+ communication::request_response::{Error, JustificationRequest},
+ justification::{decode_and_verify_finality_proof, BeefyVersionedFinalityProof},
+ KnownPeers,
+};
+
+/// Response type received from network.
+type Response = Result, RequestFailure>;
+/// Used to receive a response from the network.
+type ResponseReceiver = oneshot::Receiver;
+
+enum State {
+ Idle(stream::Pending>),
+ AwaitingResponse(PeerId, NumberFor, stream::Once),
+}
+
+pub struct OnDemandJustificationsEngine {
+ network: Arc,
+ runtime: Arc,
+ protocol_name: ProtocolName,
+
+ live_peers: Arc>>,
+ peers_cache: VecDeque,
+
+ state: State,
+}
+
+impl OnDemandJustificationsEngine
+where
+ B: Block,
+ R: ProvideRuntimeApi,
+ R::Api: BeefyApi,
+{
+ pub fn new(
+ network: Arc,
+ runtime: Arc,
+ protocol_name: ProtocolName,
+ live_peers: Arc>>,
+ ) -> Self {
+ Self {
+ network,
+ runtime,
+ protocol_name,
+ live_peers,
+ peers_cache: VecDeque::new(),
+ state: State::Idle(stream::pending()),
+ }
+ }
+
+ fn reset_peers_cache_for_block(&mut self, block: NumberFor) {
+ // TODO (issue #12296): replace peer selection with generic one that involves all protocols.
+ self.peers_cache = self.live_peers.lock().at_least_at_block(block);
+ }
+
+ fn try_next_peer(&mut self) -> Option {
+ // TODO (issue #12296): replace peer selection with generic one that involves all protocols.
+ let live = self.live_peers.lock();
+ while let Some(peer) = self.peers_cache.pop_front() {
+ if live.contains(&peer) {
+ return Some(peer)
+ }
+ }
+ None
+ }
+
+ fn request_from_peer(&mut self, peer: PeerId, block: NumberFor) {
+ debug!(target: "beefy::sync", "🥩 requesting justif #{:?} from peer {:?}", block, peer);
+
+ let payload = JustificationRequest:: { begin: block }.encode();
+
+ let (tx, rx) = oneshot::channel();
+
+ self.network.start_request(
+ peer,
+ self.protocol_name.clone(),
+ payload,
+ tx,
+ IfDisconnected::ImmediateError,
+ );
+
+ self.state = State::AwaitingResponse(peer, block, stream::once(rx));
+ }
+
+ /// If no other request is in progress, start new justification request for `block`.
+ pub fn request(&mut self, block: NumberFor) {
+ // ignore new requests while there's already one pending
+ match &self.state {
+ State::AwaitingResponse(_, _, _) => return,
+ State::Idle(_) => (),
+ }
+ self.reset_peers_cache_for_block(block);
+
+ // Start the requests engine - each unsuccessful received response will automatically
+ // trigger a new request to the next peer in the `peers_cache` until there are none left.
+ if let Some(peer) = self.try_next_peer() {
+ self.request_from_peer(peer, block);
+ } else {
+ debug!(target: "beefy::sync", "🥩 no good peers to request justif #{:?} from", block);
+ }
+ }
+
+ /// Cancel any pending request for block numbers smaller or equal to `block`.
+ pub fn cancel_requests_older_than(&mut self, block: NumberFor) {
+ match &self.state {
+ State::AwaitingResponse(_, number, _) if *number <= block => {
+ debug!(
+ target: "beefy::sync",
+ "🥩 cancel pending request for justification #{:?}",
+ number
+ );
+ self.state = State::Idle(stream::pending());
+ },
+ _ => (),
+ }
+ }
+
+ fn process_response(
+ &mut self,
+ peer: PeerId,
+ block: NumberFor,
+ validator_set: &ValidatorSet,
+ response: Result,
+ ) -> Result, Error> {
+ response
+ .map_err(|e| {
+ debug!(
+ target: "beefy::sync",
+ "🥩 for on demand justification #{:?}, peer {:?} hung up: {:?}",
+ block, peer, e
+ );
+ Error::InvalidResponse
+ })?
+ .map_err(|e| {
+ debug!(
+ target: "beefy::sync",
+ "🥩 for on demand justification #{:?}, peer {:?} error: {:?}",
+ block, peer, e
+ );
+ Error::InvalidResponse
+ })
+ .and_then(|encoded| {
+ decode_and_verify_finality_proof::(&encoded[..], block, &validator_set).map_err(
+ |e| {
+ debug!(
+ target: "beefy::sync",
+ "🥩 for on demand justification #{:?}, peer {:?} responded with invalid proof: {:?}",
+ block, peer, e
+ );
+ Error::InvalidResponse
+ },
+ )
+ })
+ }
+
+ pub async fn next(&mut self) -> Option> {
+ let (peer, block, resp) = match &mut self.state {
+ State::Idle(pending) => {
+ let _ = pending.next().await;
+ // This never happens since 'stream::pending' never generates any items.
+ return None
+ },
+ State::AwaitingResponse(peer, block, receiver) => {
+ let resp = receiver.next().await?;
+ (*peer, *block, resp)
+ },
+ };
+ // We received the awaited response. Our 'stream::once()' receiver will never generate any
+ // other response, meaning we're done with current state. Move the engine to `State::Idle`.
+ self.state = State::Idle(stream::pending());
+
+ let block_id = BlockId::number(block);
+ let validator_set = self
+ .runtime
+ .runtime_api()
+ .validator_set(&block_id)
+ .map_err(|e| {
+ error!(target: "beefy::sync", "🥩 Runtime API error {:?} in on-demand justif engine.", e);
+ e
+ })
+ .ok()?
+ .or_else(|| {
+ error!(target: "beefy::sync", "🥩 BEEFY pallet not available for block {:?}.", block);
+ None
+ })?;
+
+ self.process_response(peer, block, &validator_set, resp)
+ .map_err(|_| {
+ // No valid justification received, try next peer in our set.
+ if let Some(peer) = self.try_next_peer() {
+ self.request_from_peer(peer, block);
+ } else {
+ warn!(target: "beefy::sync", "🥩 ran out of peers to request justif #{:?} from", block);
+ }
+ })
+ .map(|proof| {
+ debug!(
+ target: "beefy::sync",
+ "🥩 received valid on-demand justif #{:?} from {:?}",
+ block, peer
+ );
+ proof
+ })
+ .ok()
+ }
+}
diff --git a/client/beefy/src/import.rs b/client/beefy/src/import.rs
index db4d8bfba7450..89a4517334189 100644
--- a/client/beefy/src/import.rs
+++ b/client/beefy/src/import.rs
@@ -33,8 +33,8 @@ use sc_client_api::backend::Backend;
use sc_consensus::{BlockCheckParams, BlockImport, BlockImportParams, ImportResult};
use crate::{
+ communication::notification::BeefyVersionedFinalityProofSender,
justification::{decode_and_verify_finality_proof, BeefyVersionedFinalityProof},
- notification::BeefyVersionedFinalityProofSender,
};
/// A block-import handler for BEEFY.
diff --git a/client/beefy/src/lib.rs b/client/beefy/src/lib.rs
index ad527b2929585..7407f101e99a5 100644
--- a/client/beefy/src/lib.rs
+++ b/client/beefy/src/lib.rs
@@ -17,10 +17,12 @@
// along with this program. If not, see .
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>(
- genesis_hash: &Hash,
- chain_spec: &Box,
- ) -> 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::::channel();
+ BeefyVersionedFinalityProofStream::::channel();
let (to_rpc_best_block_sender, from_voter_best_beefy_stream) =
- notification::BeefyBestBlockStream::::channel();
+ BeefyBestBlockStream::::channel();
// BlockImport -> Voter links
let (to_voter_justif_sender, from_block_import_justif_stream) =
- notification::BeefyVersionedFinalityProofStream::::channel();
+ BeefyVersionedFinalityProofStream::::channel();
// BlockImport
let import =
@@ -180,6 +152,24 @@ where
(import, voter_links, rpc_links)
}
+/// BEEFY gadget network parameters.
+pub struct BeefyNetworkParams
+where
+ B: Block,
+ N: GossipNetwork + NetworkRequest + SyncOracle + Send + Sync + 'static,
+{
+ /// Network implementing gossip, requests and sync-oracle.
+ pub network: Arc,
+ /// 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
where
@@ -188,7 +178,7 @@ where
C: Client,
R: ProvideRuntimeApi,
R::Api: BeefyApi + MmrApi,
- N: GossipNetwork + Clone + SyncOracle + Send + Sync + 'static,
+ N: GossipNetwork + NetworkRequest + SyncOracle + Send + Sync + 'static,
{
/// BEEFY client
pub client: Arc,
@@ -198,16 +188,16 @@ where
pub runtime: Arc,
/// Local key store
pub key_store: Option,
- /// Gossip network
- pub network: N,
+ /// 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,
- /// 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,
+ /// Handler for incoming BEEFY justifications requests from a remote peer.
+ pub on_demand_justifications_handler: BeefyJustifsRequestHandler,
}
/// Start the BEEFY gadget.
@@ -217,32 +207,43 @@ pub async fn start_beefy_gadget(beefy_params: BeefyParams,
- C: Client,
+ C: Client + BlockBackend,
R: ProvideRuntimeApi,
R::Api: BeefyApi + MmrApi,
- N: GossipNetwork + Clone + SyncOracle + Send + Sync + 'static,
+ N: GossipNetwork + 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;
}
diff --git a/client/beefy/src/round.rs b/client/beefy/src/round.rs
index c96613eb38a95..45d346ccd85eb 100644
--- a/client/beefy/src/round.rs
+++ b/client/beefy/src/round.rs
@@ -33,7 +33,7 @@ use sp_runtime::traits::{Block, NumberFor};
/// whether the local `self` validator has voted/signed.
///
/// Does not do any validation on votes or signatures, layers above need to handle that (gossip).
-#[derive(Default)]
+#[derive(Debug, Default)]
struct RoundTracker {
self_vote: bool,
votes: HashMap,
@@ -69,6 +69,7 @@ pub fn threshold(authorities: usize) -> usize {
/// Only round numbers > `best_done` are of interest, all others are considered stale.
///
/// Does not do any validation on votes or signatures, layers above need to handle that (gossip).
+#[derive(Debug)]
pub(crate) struct Rounds {
rounds: BTreeMap<(Payload, NumberFor), RoundTracker>,
session_start: NumberFor,
@@ -135,7 +136,7 @@ where
}
}
- pub(crate) fn try_conclude(
+ pub(crate) fn should_conclude(
&mut self,
round: &(P, NumberFor),
) -> Option>> {
@@ -148,7 +149,6 @@ where
if done {
let signatures = self.rounds.remove(round)?.votes;
- self.conclude(round.1);
Some(
self.validators()
.iter()
@@ -279,7 +279,7 @@ mod tests {
true
));
// round not concluded
- assert!(rounds.try_conclude(&round).is_none());
+ assert!(rounds.should_conclude(&round).is_none());
// self vote already present, should not self vote
assert!(!rounds.should_self_vote(&round));
@@ -296,7 +296,7 @@ mod tests {
(Keyring::Dave.public(), Keyring::Dave.sign(b"I am committed")),
false
));
- assert!(rounds.try_conclude(&round).is_none());
+ assert!(rounds.should_conclude(&round).is_none());
// add 2nd good vote
assert!(rounds.add_vote(
@@ -305,7 +305,7 @@ mod tests {
false
));
// round not concluded
- assert!(rounds.try_conclude(&round).is_none());
+ assert!(rounds.should_conclude(&round).is_none());
// add 3rd good vote
assert!(rounds.add_vote(
@@ -314,7 +314,8 @@ mod tests {
false
));
// round concluded
- assert!(rounds.try_conclude(&round).is_some());
+ assert!(rounds.should_conclude(&round).is_some());
+ rounds.conclude(round.1);
// Eve is a validator, but round was concluded, adding vote disallowed
assert!(!rounds.add_vote(
@@ -432,11 +433,12 @@ mod tests {
assert_eq!(3, rounds.rounds.len());
// conclude unknown round
- assert!(rounds.try_conclude(&(H256::from_low_u64_le(5), 5)).is_none());
+ assert!(rounds.should_conclude(&(H256::from_low_u64_le(5), 5)).is_none());
assert_eq!(3, rounds.rounds.len());
// conclude round 2
- let signatures = rounds.try_conclude(&(H256::from_low_u64_le(2), 2)).unwrap();
+ let signatures = rounds.should_conclude(&(H256::from_low_u64_le(2), 2)).unwrap();
+ rounds.conclude(2);
assert_eq!(1, rounds.rounds.len());
assert_eq!(
diff --git a/client/beefy/src/tests.rs b/client/beefy/src/tests.rs
index 3e49f4e05cc91..8057bd7cab7a5 100644
--- a/client/beefy/src/tests.rs
+++ b/client/beefy/src/tests.rs
@@ -21,10 +21,9 @@
use futures::{future, stream::FuturesUnordered, Future, StreamExt};
use parking_lot::Mutex;
use serde::{Deserialize, Serialize};
-use std::{collections::HashMap, sync::Arc, task::Poll};
+use std::{collections::HashMap, marker::PhantomData, sync::Arc, task::Poll};
use tokio::{runtime::Runtime, time::Duration};
-use sc_chain_spec::{ChainSpec, GenericChainSpec};
use sc_client_api::HeaderBackend;
use sc_consensus::{
BlockImport, BlockImportParams, BoxJustificationImport, ForkChoiceStrategy, ImportResult,
@@ -33,7 +32,7 @@ use sc_consensus::{
use sc_keystore::LocalKeystore;
use sc_network_test::{
Block, BlockImportAdapter, FullPeerConfig, PassThroughVerifier, Peer, PeersClient,
- TestNetFactory,
+ PeersFullClient, TestNetFactory,
};
use sc_utils::notification::NotificationReceiver;
@@ -42,6 +41,7 @@ use beefy_primitives::{
BeefyApi, ConsensusLog, MmrRootHash, ValidatorSet, VersionedFinalityProof, BEEFY_ENGINE_ID,
KEY_TYPE as BeefyKeyType,
};
+use sc_network::{config::RequestResponseConfig, ProtocolName};
use sp_mmr_primitives::{
BatchProof, EncodableOpaqueLeaf, Error as MmrError, LeafIndex, MmrApi, Proof,
};
@@ -60,11 +60,21 @@ use sp_runtime::{
use substrate_test_runtime_client::{runtime::Header, ClientExt};
use crate::{
- beefy_block_import_and_links, beefy_protocol_name, justification::*,
- keystore::tests::Keyring as BeefyKeyring, BeefyRPCLinks, BeefyVoterLinks,
+ beefy_block_import_and_links,
+ communication::request_response::{
+ on_demand_justifications_protocol_config, BeefyJustifsRequestHandler,
+ },
+ gossip_protocol_name,
+ justification::*,
+ keystore::tests::Keyring as BeefyKeyring,
+ BeefyRPCLinks, BeefyVoterLinks,
};
-pub(crate) const BEEFY_PROTOCOL_NAME: &'static str = "/beefy/1";
+const GENESIS_HASH: H256 = H256::zero();
+fn beefy_gossip_proto_name() -> ProtocolName {
+ gossip_protocol_name(GENESIS_HASH, None)
+}
+
const GOOD_MMR_ROOT: MmrRootHash = MmrRootHash::repeat_byte(0xbf);
const BAD_MMR_ROOT: MmrRootHash = MmrRootHash::repeat_byte(0x42);
@@ -89,35 +99,12 @@ impl BuildStorage for Genesis {
}
}
-#[test]
-fn beefy_protocol_name() {
- let chain_spec = GenericChainSpec::::from_json_bytes(
- &include_bytes!("../../chain-spec/res/chain_spec.json")[..],
- )
- .unwrap()
- .cloned_box();
-
- // Create protocol name using random genesis hash.
- let genesis_hash = H256::random();
- let expected = format!("/{}/beefy/1", array_bytes::bytes2hex("", genesis_hash.as_ref()));
- let proto_name = beefy_protocol_name::standard_name(&genesis_hash, &chain_spec);
- assert_eq!(proto_name.to_string(), expected);
-
- // Create protocol name using hardcoded genesis hash. Verify exact representation.
- let genesis_hash = [
- 50, 4, 60, 123, 58, 106, 216, 246, 194, 188, 139, 193, 33, 212, 202, 171, 9, 55, 123, 94,
- 8, 43, 12, 251, 187, 57, 173, 19, 188, 74, 205, 147,
- ];
- let expected =
- "/32043c7b3a6ad8f6c2bc8bc121d4caab09377b5e082b0cfbbb39ad13bc4acd93/beefy/1".to_string();
- let proto_name = beefy_protocol_name::standard_name(&genesis_hash, &chain_spec);
- assert_eq!(proto_name.to_string(), expected);
-}
-
#[derive(Default)]
pub(crate) struct PeerData {
pub(crate) beefy_rpc_links: Mutex