mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-06 18:21:45 +00:00
V0.11.0 network update (#976)
* Adjust RPC methods to match v0.11.1 * Adds fork handling for gossipsub topics * Update gossipsub topics to v0.11.0
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
use crate::discovery::Discovery;
|
||||
use crate::rpc::{RPCEvent, RPCMessage, RPC};
|
||||
use crate::types::GossipEncoding;
|
||||
use crate::Enr;
|
||||
use crate::{error, GossipTopic, NetworkConfig, NetworkGlobals, PubsubMessage, TopicHash};
|
||||
use crate::types::{GossipEncoding, GossipKind, GossipTopic};
|
||||
use crate::{error, Enr, NetworkConfig, NetworkGlobals, PubsubMessage, TopicHash};
|
||||
use futures::prelude::*;
|
||||
use libp2p::{
|
||||
core::identity::Keypair,
|
||||
@@ -47,6 +46,9 @@ pub struct Behaviour<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> {
|
||||
#[behaviour(ignore)]
|
||||
network_globals: Arc<NetworkGlobals<TSpec>>,
|
||||
#[behaviour(ignore)]
|
||||
/// Keeps track of the current EnrForkId for upgrading gossipsub topics.
|
||||
enr_fork_id: EnrForkId,
|
||||
#[behaviour(ignore)]
|
||||
/// Logger for behaviour actions.
|
||||
log: slog::Logger,
|
||||
}
|
||||
@@ -74,7 +76,7 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, T
|
||||
discovery: Discovery::new(
|
||||
local_key,
|
||||
net_conf,
|
||||
enr_fork_id,
|
||||
enr_fork_id.clone(),
|
||||
network_globals.clone(),
|
||||
log,
|
||||
)?,
|
||||
@@ -82,6 +84,7 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, T
|
||||
events: Vec::new(),
|
||||
seen_gossip_messages: LruCache::new(100_000),
|
||||
network_globals,
|
||||
enr_fork_id,
|
||||
log: behaviour_log,
|
||||
})
|
||||
}
|
||||
@@ -99,25 +102,57 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, T
|
||||
impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, TSpec> {
|
||||
/* Pubsub behaviour functions */
|
||||
|
||||
/// Subscribes to a gossipsub topic kind, letting the network service determine the
|
||||
/// encoding and fork version.
|
||||
pub fn subscribe_kind(&mut self, kind: GossipKind) -> bool {
|
||||
let gossip_topic =
|
||||
GossipTopic::new(kind, GossipEncoding::SSZ, self.enr_fork_id.fork_digest);
|
||||
self.subscribe(gossip_topic)
|
||||
}
|
||||
|
||||
/// Unsubscribes from a gossipsub topic kind, letting the network service determine the
|
||||
/// encoding and fork version.
|
||||
pub fn unsubscribe_kind(&mut self, kind: GossipKind) -> bool {
|
||||
let gossip_topic =
|
||||
GossipTopic::new(kind, GossipEncoding::SSZ, self.enr_fork_id.fork_digest);
|
||||
self.unsubscribe(gossip_topic)
|
||||
}
|
||||
|
||||
/// Subscribes to a specific subnet id;
|
||||
pub fn subscribe_to_subnet(&mut self, subnet_id: SubnetId) -> bool {
|
||||
let topic = GossipTopic::new(
|
||||
subnet_id.into(),
|
||||
GossipEncoding::SSZ,
|
||||
self.enr_fork_id.fork_digest,
|
||||
);
|
||||
self.subscribe(topic)
|
||||
}
|
||||
|
||||
/// Un-Subscribes from a specific subnet id;
|
||||
pub fn unsubscribe_from_subnet(&mut self, subnet_id: SubnetId) -> bool {
|
||||
let topic = GossipTopic::new(
|
||||
subnet_id.into(),
|
||||
GossipEncoding::SSZ,
|
||||
self.enr_fork_id.fork_digest,
|
||||
);
|
||||
self.unsubscribe(topic)
|
||||
}
|
||||
|
||||
/// Subscribes to a gossipsub topic.
|
||||
pub fn subscribe(&mut self, topic: GossipTopic) -> bool {
|
||||
fn subscribe(&mut self, topic: GossipTopic) -> bool {
|
||||
// update the network globals
|
||||
self.network_globals
|
||||
.gossipsub_subscriptions
|
||||
.write()
|
||||
.insert(topic.clone());
|
||||
// subscribe to the topic
|
||||
|
||||
let topic_str: String = topic.clone().into();
|
||||
debug!(self.log, "Subscribed to topic"; "topic" => topic_str);
|
||||
self.gossipsub.subscribe(topic.into())
|
||||
}
|
||||
|
||||
/// Subscribes to a specific subnet id;
|
||||
pub fn subscribe_to_subnet(&mut self, subnet_id: SubnetId) {
|
||||
let topic = GossipTopic::new(subnet_id.into(), GossipEncoding::SSZ);
|
||||
self.subscribe(topic);
|
||||
}
|
||||
|
||||
/// Unsubscribe from a gossipsub topic.
|
||||
pub fn unsubscribe(&mut self, topic: GossipTopic) -> bool {
|
||||
fn unsubscribe(&mut self, topic: GossipTopic) -> bool {
|
||||
// update the network globals
|
||||
self.network_globals
|
||||
.gossipsub_subscriptions
|
||||
@@ -127,17 +162,11 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, T
|
||||
self.gossipsub.unsubscribe(topic.into())
|
||||
}
|
||||
|
||||
/// Un-Subscribes from a specific subnet id;
|
||||
pub fn unsubscribe_from_subnet(&mut self, subnet_id: SubnetId) {
|
||||
let topic = GossipTopic::new(subnet_id.into(), GossipEncoding::SSZ);
|
||||
self.unsubscribe(topic);
|
||||
}
|
||||
|
||||
/// Publishes a list of messages on the pubsub (gossipsub) behaviour, choosing the encoding.
|
||||
pub fn publish(&mut self, messages: Vec<PubsubMessage<TSpec>>) {
|
||||
for message in messages {
|
||||
for topic in message.topics() {
|
||||
let message_data = message.encode();
|
||||
for topic in message.topics(GossipEncoding::SSZ, self.enr_fork_id.fork_digest) {
|
||||
let message_data = message.encode(GossipEncoding::SSZ);
|
||||
self.gossipsub.publish(&topic.into(), message_data);
|
||||
}
|
||||
}
|
||||
@@ -195,8 +224,30 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, T
|
||||
|
||||
/// Updates the local ENR's "eth2" field with the latest EnrForkId.
|
||||
pub fn update_fork_version(&mut self, enr_fork_id: EnrForkId) {
|
||||
self.discovery.update_eth2_enr(enr_fork_id);
|
||||
// TODO: Handle gossipsub fork update
|
||||
self.discovery.update_eth2_enr(enr_fork_id.clone());
|
||||
|
||||
// unsubscribe from all gossip topics and re-subscribe to their new fork counterparts
|
||||
let subscribed_topics = self
|
||||
.network_globals
|
||||
.gossipsub_subscriptions
|
||||
.read()
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<GossipTopic>>();
|
||||
|
||||
// unsubscribe from all topics
|
||||
for topic in &subscribed_topics {
|
||||
self.unsubscribe(topic.clone());
|
||||
}
|
||||
|
||||
// re-subscribe modifying the fork version
|
||||
for mut topic in subscribed_topics {
|
||||
*topic.digest() = enr_fork_id.fork_digest;
|
||||
self.subscribe(topic);
|
||||
}
|
||||
|
||||
// update the local reference
|
||||
self.enr_fork_id = enr_fork_id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::types::{GossipEncoding, GossipKind, GossipTopic};
|
||||
use crate::types::GossipKind;
|
||||
use crate::Enr;
|
||||
use libp2p::discv5::{Discv5Config, Discv5ConfigBuilder};
|
||||
use libp2p::gossipsub::{GossipsubConfig, GossipsubConfigBuilder, GossipsubMessage, MessageId};
|
||||
@@ -61,7 +61,7 @@ pub struct Config {
|
||||
pub client_version: String,
|
||||
|
||||
/// List of extra topics to initially subscribe to as strings.
|
||||
pub topics: Vec<GossipTopic>,
|
||||
pub topics: Vec<GossipKind>,
|
||||
|
||||
/// Introduces randomization in network propagation of messages. This should only be set for
|
||||
/// testing purposes and will likely be removed in future versions.
|
||||
@@ -78,11 +78,11 @@ impl Default for Config {
|
||||
|
||||
// The default topics that we will initially subscribe to
|
||||
let topics = vec![
|
||||
GossipTopic::new(GossipKind::BeaconBlock, GossipEncoding::SSZ),
|
||||
GossipTopic::new(GossipKind::BeaconAggregateAndProof, GossipEncoding::SSZ),
|
||||
GossipTopic::new(GossipKind::VoluntaryExit, GossipEncoding::SSZ),
|
||||
GossipTopic::new(GossipKind::ProposerSlashing, GossipEncoding::SSZ),
|
||||
GossipTopic::new(GossipKind::AttesterSlashing, GossipEncoding::SSZ),
|
||||
GossipKind::BeaconBlock,
|
||||
GossipKind::BeaconAggregateAndProof,
|
||||
GossipKind::VoluntaryExit,
|
||||
GossipKind::ProposerSlashing,
|
||||
GossipKind::AttesterSlashing,
|
||||
];
|
||||
|
||||
// The function used to generate a gossipsub message id
|
||||
|
||||
@@ -16,7 +16,7 @@ pub mod types;
|
||||
// shift this type into discv5
|
||||
pub type Enr = libp2p::discv5::enr::Enr<libp2p::discv5::enr::CombinedKey>;
|
||||
|
||||
pub use crate::types::{error, GossipTopic, NetworkGlobals, PeerInfo, PubsubData, PubsubMessage};
|
||||
pub use crate::types::{error, GossipTopic, NetworkGlobals, PeerInfo, PubsubMessage};
|
||||
pub use config::Config as NetworkConfig;
|
||||
pub use libp2p::gossipsub::{MessageId, Topic, TopicHash};
|
||||
pub use libp2p::{multiaddr, Multiaddr};
|
||||
|
||||
@@ -13,7 +13,7 @@ pub type RequestId = usize;
|
||||
#[derive(Encode, Decode, Clone, Debug, PartialEq)]
|
||||
pub struct StatusMessage {
|
||||
/// The fork version of the chain we are broadcasting.
|
||||
pub fork_version: [u8; 4],
|
||||
pub fork_digest: [u8; 4],
|
||||
|
||||
/// Latest finalized root.
|
||||
pub finalized_root: Hash256,
|
||||
@@ -101,9 +101,6 @@ impl ssz::Decode for GoodbyeReason {
|
||||
/// Request a number of beacon block roots from a peer.
|
||||
#[derive(Encode, Decode, Clone, Debug, PartialEq)]
|
||||
pub struct BlocksByRangeRequest {
|
||||
/// The hash tree root of a block on the requested chain.
|
||||
pub head_block_root: Hash256,
|
||||
|
||||
/// The starting slot to request blocks.
|
||||
pub start_slot: u64,
|
||||
|
||||
@@ -238,7 +235,7 @@ impl ErrorMessage {
|
||||
|
||||
impl std::fmt::Display for StatusMessage {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Status Message: Fork Version: {:?}, Finalized Root: {}, Finalized Epoch: {}, Head Root: {}, Head Slot: {}", self.fork_version, self.finalized_root, self.finalized_epoch, self.head_root, self.head_slot)
|
||||
write!(f, "Status Message: Fork Digest: {:?}, Finalized Root: {}, Finalized Epoch: {}, Head Root: {}, Head Slot: {}", self.fork_digest, self.finalized_root, self.finalized_epoch, self.head_root, self.head_slot)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,8 +280,8 @@ impl std::fmt::Display for BlocksByRangeRequest {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Head Block Root: {}, Start Slot: {}, Count: {}, Step: {}",
|
||||
self.head_block_root, self.start_slot, self.count, self.step
|
||||
"Start Slot: {}, Count: {}, Step: {}",
|
||||
self.start_slot, self.count, self.step
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use crate::behaviour::{Behaviour, BehaviourEvent};
|
||||
use crate::multiaddr::Protocol;
|
||||
use crate::rpc::RPCEvent;
|
||||
use crate::types::error;
|
||||
use crate::NetworkConfig;
|
||||
use crate::{NetworkGlobals, PubsubMessage, TopicHash};
|
||||
use crate::types::{error, GossipKind};
|
||||
use crate::{NetworkConfig, NetworkGlobals, PubsubMessage, TopicHash};
|
||||
use futures::prelude::*;
|
||||
use futures::Stream;
|
||||
use libp2p::core::{
|
||||
@@ -144,18 +143,12 @@ impl<TSpec: EthSpec> Service<TSpec> {
|
||||
}
|
||||
}
|
||||
|
||||
let mut subscribed_topics: Vec<String> = vec![];
|
||||
for topic in &config.topics {
|
||||
let topic_string: String = topic.clone().into();
|
||||
if swarm.subscribe(topic.clone()) {
|
||||
trace!(log, "Subscribed to topic"; "topic" => format!("{}", topic_string));
|
||||
subscribed_topics.push(topic_string);
|
||||
network_globals
|
||||
.gossipsub_subscriptions
|
||||
.write()
|
||||
.insert(topic.clone());
|
||||
let mut subscribed_topics: Vec<GossipKind> = vec![];
|
||||
for topic_kind in &config.topics {
|
||||
if swarm.subscribe_kind(topic_kind.clone()) {
|
||||
subscribed_topics.push(topic_kind.clone());
|
||||
} else {
|
||||
warn!(log, "Could not subscribe to topic"; "topic" => format!("{}",topic_string));
|
||||
warn!(log, "Could not subscribe to topic"; "topic" => format!("{}",topic_kind));
|
||||
}
|
||||
}
|
||||
info!(log, "Subscribed to topics"; "topics" => format!("{:?}", subscribed_topics));
|
||||
|
||||
@@ -6,5 +6,5 @@ mod topics;
|
||||
|
||||
pub use globals::NetworkGlobals;
|
||||
pub use peer_info::{EnrBitfield, PeerInfo};
|
||||
pub use pubsub::{PubsubData, PubsubMessage};
|
||||
pub use pubsub::PubsubMessage;
|
||||
pub use topics::{GossipEncoding, GossipKind, GossipTopic};
|
||||
|
||||
@@ -10,17 +10,8 @@ use types::{
|
||||
SignedBeaconBlock, VoluntaryExit,
|
||||
};
|
||||
|
||||
/// Messages that are passed to and from the pubsub (Gossipsub) behaviour.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct PubsubMessage<T: EthSpec> {
|
||||
/// The encoding to be used to encode/decode the message
|
||||
pub encoding: GossipEncoding,
|
||||
/// The actual message being sent.
|
||||
pub data: PubsubData<T>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum PubsubData<T: EthSpec> {
|
||||
pub enum PubsubMessage<T: EthSpec> {
|
||||
/// Gossipsub message providing notification of a new block.
|
||||
BeaconBlock(Box<SignedBeaconBlock<T>>),
|
||||
/// Gossipsub message providing notification of a Aggregate attestation and associated proof.
|
||||
@@ -36,36 +27,30 @@ pub enum PubsubData<T: EthSpec> {
|
||||
}
|
||||
|
||||
impl<T: EthSpec> PubsubMessage<T> {
|
||||
pub fn new(encoding: GossipEncoding, data: PubsubData<T>) -> Self {
|
||||
PubsubMessage { encoding, data }
|
||||
/// Returns the topics that each pubsub message will be sent across, given a supported
|
||||
/// gossipsub encoding and fork version.
|
||||
pub fn topics(&self, encoding: GossipEncoding, fork_version: [u8; 4]) -> Vec<GossipTopic> {
|
||||
vec![GossipTopic::new(self.kind(), encoding, fork_version)]
|
||||
}
|
||||
|
||||
/// Returns the topics that each pubsub message will be sent across, given a supported
|
||||
/// gossipsub encoding.
|
||||
pub fn topics(&self) -> Vec<GossipTopic> {
|
||||
let encoding = self.encoding.clone();
|
||||
match &self.data {
|
||||
PubsubData::BeaconBlock(_) => vec![GossipTopic::new(GossipKind::BeaconBlock, encoding)],
|
||||
PubsubData::AggregateAndProofAttestation(_) => vec![GossipTopic::new(
|
||||
GossipKind::BeaconAggregateAndProof,
|
||||
encoding,
|
||||
)],
|
||||
PubsubData::Attestation(attestation_data) => vec![GossipTopic::new(
|
||||
GossipKind::CommitteeIndex(attestation_data.0),
|
||||
encoding,
|
||||
)],
|
||||
PubsubData::VoluntaryExit(_) => {
|
||||
vec![GossipTopic::new(GossipKind::VoluntaryExit, encoding)]
|
||||
}
|
||||
PubsubData::ProposerSlashing(_) => {
|
||||
vec![GossipTopic::new(GossipKind::ProposerSlashing, encoding)]
|
||||
}
|
||||
PubsubData::AttesterSlashing(_) => {
|
||||
vec![GossipTopic::new(GossipKind::AttesterSlashing, encoding)]
|
||||
/// Returns the kind of gossipsub topic associated with the message.
|
||||
pub fn kind(&self) -> GossipKind {
|
||||
match self {
|
||||
PubsubMessage::BeaconBlock(_) => GossipKind::BeaconBlock,
|
||||
PubsubMessage::AggregateAndProofAttestation(_) => GossipKind::BeaconAggregateAndProof,
|
||||
PubsubMessage::Attestation(attestation_data) => {
|
||||
GossipKind::CommitteeIndex(attestation_data.0)
|
||||
}
|
||||
PubsubMessage::VoluntaryExit(_) => GossipKind::VoluntaryExit,
|
||||
PubsubMessage::ProposerSlashing(_) => GossipKind::ProposerSlashing,
|
||||
PubsubMessage::AttesterSlashing(_) => GossipKind::AttesterSlashing,
|
||||
}
|
||||
}
|
||||
|
||||
/// This decodes `data` into a `PubsubMessage` given a list of topics.
|
||||
///
|
||||
/// The topics are checked
|
||||
/// in order and as soon as one topic matches the decoded data, we return the data.
|
||||
/* Note: This is assuming we are not hashing topics. If we choose to hash topics, these will
|
||||
* need to be modified.
|
||||
*
|
||||
@@ -85,61 +70,48 @@ impl<T: EthSpec> PubsubMessage<T> {
|
||||
// group each part by encoding type
|
||||
GossipEncoding::SSZ => {
|
||||
// the ssz decoders
|
||||
let encoding = GossipEncoding::SSZ;
|
||||
match gossip_topic.kind() {
|
||||
GossipKind::BeaconAggregateAndProof => {
|
||||
let agg_and_proof =
|
||||
SignedAggregateAndProof::from_ssz_bytes(data)
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
return Ok(PubsubMessage::new(
|
||||
encoding,
|
||||
PubsubData::AggregateAndProofAttestation(Box::new(
|
||||
agg_and_proof,
|
||||
)),
|
||||
return Ok(PubsubMessage::AggregateAndProofAttestation(
|
||||
Box::new(agg_and_proof),
|
||||
));
|
||||
}
|
||||
GossipKind::CommitteeIndex(subnet_id) => {
|
||||
let attestation = Attestation::from_ssz_bytes(data)
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
return Ok(PubsubMessage::new(
|
||||
encoding,
|
||||
PubsubData::Attestation(Box::new((
|
||||
*subnet_id,
|
||||
attestation,
|
||||
))),
|
||||
));
|
||||
return Ok(PubsubMessage::Attestation(Box::new((
|
||||
*subnet_id,
|
||||
attestation,
|
||||
))));
|
||||
}
|
||||
GossipKind::BeaconBlock => {
|
||||
let beacon_block = SignedBeaconBlock::from_ssz_bytes(data)
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
return Ok(PubsubMessage::new(
|
||||
encoding,
|
||||
PubsubData::BeaconBlock(Box::new(beacon_block)),
|
||||
));
|
||||
return Ok(PubsubMessage::BeaconBlock(Box::new(beacon_block)));
|
||||
}
|
||||
GossipKind::VoluntaryExit => {
|
||||
let voluntary_exit = VoluntaryExit::from_ssz_bytes(data)
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
return Ok(PubsubMessage::new(
|
||||
encoding,
|
||||
PubsubData::VoluntaryExit(Box::new(voluntary_exit)),
|
||||
));
|
||||
return Ok(PubsubMessage::VoluntaryExit(Box::new(
|
||||
voluntary_exit,
|
||||
)));
|
||||
}
|
||||
GossipKind::ProposerSlashing => {
|
||||
let proposer_slashing = ProposerSlashing::from_ssz_bytes(data)
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
return Ok(PubsubMessage::new(
|
||||
encoding,
|
||||
PubsubData::ProposerSlashing(Box::new(proposer_slashing)),
|
||||
));
|
||||
return Ok(PubsubMessage::ProposerSlashing(Box::new(
|
||||
proposer_slashing,
|
||||
)));
|
||||
}
|
||||
GossipKind::AttesterSlashing => {
|
||||
let attester_slashing = AttesterSlashing::from_ssz_bytes(data)
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
return Ok(PubsubMessage::new(
|
||||
encoding,
|
||||
PubsubData::AttesterSlashing(Box::new(attester_slashing)),
|
||||
));
|
||||
return Ok(PubsubMessage::AttesterSlashing(Box::new(
|
||||
attester_slashing,
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,19 +122,19 @@ impl<T: EthSpec> PubsubMessage<T> {
|
||||
Err(format!("Unknown gossipsub topics: {:?}", unknown_topics))
|
||||
}
|
||||
|
||||
/// Encodes a pubsub message based on the topic encodings. The first known encoding is used. If
|
||||
/// Encodes a `PubsubMessage` based on the topic encodings. The first known encoding is used. If
|
||||
/// no encoding is known, and error is returned.
|
||||
pub fn encode(&self) -> Vec<u8> {
|
||||
match self.encoding {
|
||||
pub fn encode(&self, encoding: GossipEncoding) -> Vec<u8> {
|
||||
match encoding {
|
||||
GossipEncoding::SSZ => {
|
||||
// SSZ Encodings
|
||||
return match &self.data {
|
||||
PubsubData::BeaconBlock(data) => data.as_ssz_bytes(),
|
||||
PubsubData::AggregateAndProofAttestation(data) => data.as_ssz_bytes(),
|
||||
PubsubData::VoluntaryExit(data) => data.as_ssz_bytes(),
|
||||
PubsubData::ProposerSlashing(data) => data.as_ssz_bytes(),
|
||||
PubsubData::AttesterSlashing(data) => data.as_ssz_bytes(),
|
||||
PubsubData::Attestation(data) => data.1.as_ssz_bytes(),
|
||||
return match &self {
|
||||
PubsubMessage::BeaconBlock(data) => data.as_ssz_bytes(),
|
||||
PubsubMessage::AggregateAndProofAttestation(data) => data.as_ssz_bytes(),
|
||||
PubsubMessage::VoluntaryExit(data) => data.as_ssz_bytes(),
|
||||
PubsubMessage::ProposerSlashing(data) => data.as_ssz_bytes(),
|
||||
PubsubMessage::AttesterSlashing(data) => data.as_ssz_bytes(),
|
||||
PubsubMessage::Attestation(data) => data.1.as_ssz_bytes(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,11 +23,14 @@ pub const ATTESTER_SLASHING_TOPIC: &str = "attester_slashing";
|
||||
pub struct GossipTopic {
|
||||
/// The encoding of the topic.
|
||||
encoding: GossipEncoding,
|
||||
/// The fork digest of the topic,
|
||||
fork_digest: [u8; 4],
|
||||
/// The kind of topic.
|
||||
kind: GossipKind,
|
||||
}
|
||||
|
||||
/// Enum that brings these topics into the rust type system.
|
||||
// NOTE: There is intentionally no unknown type here. We only allow known gossipsub topics.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
pub enum GossipKind {
|
||||
/// Topic for publishing beacon blocks.
|
||||
@@ -44,6 +47,19 @@ pub enum GossipKind {
|
||||
AttesterSlashing,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for GossipKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
GossipKind::BeaconBlock => write!(f, "beacon_block"),
|
||||
GossipKind::BeaconAggregateAndProof => write!(f, "beacon_aggregate_and_proof"),
|
||||
GossipKind::CommitteeIndex(subnet_id) => write!(f, "committee_index_{}", **subnet_id),
|
||||
GossipKind::VoluntaryExit => write!(f, "voluntary_exit"),
|
||||
GossipKind::ProposerSlashing => write!(f, "proposer_slashing"),
|
||||
GossipKind::AttesterSlashing => write!(f, "attester_slashing"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The known encoding types for gossipsub messages.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
pub enum GossipEncoding {
|
||||
@@ -52,8 +68,12 @@ pub enum GossipEncoding {
|
||||
}
|
||||
|
||||
impl GossipTopic {
|
||||
pub fn new(kind: GossipKind, encoding: GossipEncoding) -> Self {
|
||||
GossipTopic { encoding, kind }
|
||||
pub fn new(kind: GossipKind, encoding: GossipEncoding, fork_digest: [u8; 4]) -> Self {
|
||||
GossipTopic {
|
||||
encoding,
|
||||
kind,
|
||||
fork_digest,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the encoding type for the gossipsub topic.
|
||||
@@ -61,6 +81,11 @@ impl GossipTopic {
|
||||
&self.encoding
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the fork digest of the gossipsub topic.
|
||||
pub fn digest(&mut self) -> &mut [u8; 4] {
|
||||
&mut self.fork_digest
|
||||
}
|
||||
|
||||
/// Returns the kind of message expected on the gossipsub topic.
|
||||
pub fn kind(&self) -> &GossipKind {
|
||||
&self.kind
|
||||
@@ -68,12 +93,25 @@ impl GossipTopic {
|
||||
|
||||
pub fn decode(topic: &str) -> Result<Self, String> {
|
||||
let topic_parts: Vec<&str> = topic.split('/').collect();
|
||||
if topic_parts.len() == 4 && topic_parts[1] == TOPIC_PREFIX {
|
||||
let encoding = match topic_parts[3] {
|
||||
if topic_parts.len() == 5 && topic_parts[1] == TOPIC_PREFIX {
|
||||
let digest_bytes = hex::decode(topic_parts[2])
|
||||
.map_err(|e| format!("Could not decode fork_digest hex: {}", e))?;
|
||||
|
||||
if digest_bytes.len() != 4 {
|
||||
return Err(format!(
|
||||
"Invalid gossipsub fork digest size: {}",
|
||||
digest_bytes.len()
|
||||
));
|
||||
}
|
||||
|
||||
let mut fork_digest = [0; 4];
|
||||
fork_digest.copy_from_slice(&digest_bytes);
|
||||
|
||||
let encoding = match topic_parts[4] {
|
||||
SSZ_ENCODING_POSTFIX => GossipEncoding::SSZ,
|
||||
_ => return Err(format!("Unknown encoding: {}", topic)),
|
||||
};
|
||||
let kind = match topic_parts[2] {
|
||||
let kind = match topic_parts[3] {
|
||||
BEACON_BLOCK_TOPIC => GossipKind::BeaconBlock,
|
||||
BEACON_AGGREGATE_AND_PROOF_TOPIC => GossipKind::BeaconAggregateAndProof,
|
||||
VOLUNTARY_EXIT_TOPIC => GossipKind::VoluntaryExit,
|
||||
@@ -85,7 +123,11 @@ impl GossipTopic {
|
||||
},
|
||||
};
|
||||
|
||||
return Ok(GossipTopic { encoding, kind });
|
||||
return Ok(GossipTopic {
|
||||
encoding,
|
||||
kind,
|
||||
fork_digest,
|
||||
});
|
||||
}
|
||||
|
||||
Err(format!("Unknown topic: {}", topic))
|
||||
@@ -115,7 +157,13 @@ impl Into<String> for GossipTopic {
|
||||
COMMITEE_INDEX_TOPIC_PREFIX, *index, COMMITEE_INDEX_TOPIC_POSTFIX
|
||||
),
|
||||
};
|
||||
format!("/{}/{}/{}", TOPIC_PREFIX, kind, encoding)
|
||||
format!(
|
||||
"/{}/{}/{}/{}",
|
||||
TOPIC_PREFIX,
|
||||
hex::encode(self.fork_digest),
|
||||
kind,
|
||||
encoding
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,9 +33,13 @@ fn test_gossipsub_forward() {
|
||||
message: empty_block,
|
||||
signature: Signature::empty_signature(),
|
||||
};
|
||||
let data = PubsubData::BeaconBlock(Box::new(signed_block));
|
||||
let pubsub_message = PubsubMessage::new(GossipEncoding::SSZ, data);
|
||||
let publishing_topic: String = "/eth2/beacon_block/ssz".into();
|
||||
let pubsub_message = PubsubMessage::BeaconBlock(Box::new(signed_block));
|
||||
let publishing_topic: String = pubsub_message
|
||||
.topics(GossipEncoding::SSZ, [0, 0, 0, 0])
|
||||
.first()
|
||||
.unwrap()
|
||||
.clone()
|
||||
.into();
|
||||
let mut subscribed_count = 0;
|
||||
tokio::run(futures::future::poll_fn(move || -> Result<_, ()> {
|
||||
for node in nodes.iter_mut() {
|
||||
@@ -65,10 +69,8 @@ fn test_gossipsub_forward() {
|
||||
}
|
||||
}
|
||||
Async::Ready(Some(Libp2pEvent::PeerSubscribed(_, topic))) => {
|
||||
// Received topics is one of subscribed eth2 topics
|
||||
assert!(topic.clone().into_string().starts_with("/eth2/"));
|
||||
// Publish on beacon block topic
|
||||
if topic == TopicHash::from_raw("/eth2/beacon_block/ssz") {
|
||||
if topic == TopicHash::from_raw(publishing_topic.clone()) {
|
||||
subscribed_count += 1;
|
||||
// Every node except the corner nodes are connected to 2 nodes.
|
||||
if subscribed_count == (num_nodes * 2) - 2 {
|
||||
@@ -104,9 +106,13 @@ fn test_gossipsub_full_mesh_publish() {
|
||||
message: empty_block,
|
||||
signature: Signature::empty_signature(),
|
||||
};
|
||||
let data = PubsubData::BeaconBlock(Box::new(signed_block));
|
||||
let pubsub_message = PubsubMessage::new(GossipEncoding::SSZ, data);
|
||||
let publishing_topic: String = "/eth2/beacon_block/ssz".into();
|
||||
let pubsub_message = PubsubMessage::BeaconBlock(Box::new(signed_block));
|
||||
let publishing_topic: String = pubsub_message
|
||||
.topics(GossipEncoding::SSZ, [0, 0, 0, 0])
|
||||
.first()
|
||||
.unwrap()
|
||||
.clone()
|
||||
.into();
|
||||
let mut subscribed_count = 0;
|
||||
let mut received_count = 0;
|
||||
tokio::run(futures::future::poll_fn(move || -> Result<_, ()> {
|
||||
@@ -132,10 +138,8 @@ fn test_gossipsub_full_mesh_publish() {
|
||||
while let Async::Ready(Some(Libp2pEvent::PeerSubscribed(_, topic))) =
|
||||
publishing_node.poll().unwrap()
|
||||
{
|
||||
// Received topics is one of subscribed eth2 topics
|
||||
assert!(topic.clone().into_string().starts_with("/eth2/"));
|
||||
// Publish on beacon block topic
|
||||
if topic == TopicHash::from_raw("/eth2/beacon_block/ssz") {
|
||||
if topic == TopicHash::from_raw(publishing_topic.clone()) {
|
||||
subscribed_count += 1;
|
||||
if subscribed_count == num_nodes - 1 {
|
||||
publishing_node.swarm.publish(vec![pubsub_message.clone()]);
|
||||
|
||||
@@ -29,7 +29,7 @@ fn test_status_rpc() {
|
||||
|
||||
// Dummy STATUS RPC message
|
||||
let rpc_request = RPCRequest::Status(StatusMessage {
|
||||
fork_version: [0; 4],
|
||||
fork_digest: [0; 4],
|
||||
finalized_root: Hash256::from_low_u64_be(0),
|
||||
finalized_epoch: Epoch::new(1),
|
||||
head_root: Hash256::from_low_u64_be(0),
|
||||
@@ -38,7 +38,7 @@ fn test_status_rpc() {
|
||||
|
||||
// Dummy STATUS RPC message
|
||||
let rpc_response = RPCResponse::Status(StatusMessage {
|
||||
fork_version: [0; 4],
|
||||
fork_digest: [0; 4],
|
||||
finalized_root: Hash256::from_low_u64_be(0),
|
||||
finalized_epoch: Epoch::new(1),
|
||||
head_root: Hash256::from_low_u64_be(0),
|
||||
@@ -142,7 +142,6 @@ fn test_blocks_by_range_chunked_rpc() {
|
||||
|
||||
// BlocksByRange Request
|
||||
let rpc_request = RPCRequest::BlocksByRange(BlocksByRangeRequest {
|
||||
head_block_root: Hash256::from_low_u64_be(0),
|
||||
start_slot: 0,
|
||||
count: messages_to_send,
|
||||
step: 0,
|
||||
@@ -275,7 +274,6 @@ fn test_blocks_by_range_single_empty_rpc() {
|
||||
|
||||
// BlocksByRange Request
|
||||
let rpc_request = RPCRequest::BlocksByRange(BlocksByRangeRequest {
|
||||
head_block_root: Hash256::from_low_u64_be(0),
|
||||
start_slot: 0,
|
||||
count: 10,
|
||||
step: 0,
|
||||
|
||||
Reference in New Issue
Block a user