Skip to content

Commit

Permalink
feat(net): Pass in Gossipsub Config (#49)
Browse files Browse the repository at this point in the history
### Description

Allows the `NetworkDriver` builder to pass in a
`libp2p::gosipsub::Config` into the builder. This replaces the idea of
"extending the ConfigBuilder methods" to be more forwards-compatible.

Closes #38
  • Loading branch information
refcell authored Aug 27, 2024
1 parent f615c2d commit 13278aa
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 21 deletions.
11 changes: 9 additions & 2 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,17 @@ default:
test:
cargo nextest run --locked --workspace -E "kind(lib) | kind(bin) | kind(proc-macro)"

# Test docs
doc:
# Runs both `doc-tests` and `doc-lints` recipes
doc: doc-tests doc-lints

# Doc Tests
doc-tests:
cargo test --locked --workspace --doc

# Doc Lints
doc-lints:
cargo doc --document-private-items

# Lint
lint:
cargo clippy --workspace --examples --tests --benches --all-features \
Expand Down
117 changes: 101 additions & 16 deletions crates/net/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::net::SocketAddr;
use tokio::sync::watch::channel;

use libp2p::{
gossipsub::ConfigBuilder, noise::Config as NoiseConfig, tcp::Config as TcpConfig,
gossipsub::Config as GossipConfig, noise::Config as NoiseConfig, tcp::Config as TcpConfig,
yamux::Config as YamuxConfig, Multiaddr, SwarmBuilder,
};
use libp2p_identity::Keypair;
Expand All @@ -22,21 +22,21 @@ use crate::{
#[derive(Default)]
pub struct NetworkDriverBuilder {
/// The chain ID of the network.
chain_id: Option<u64>,
pub chain_id: Option<u64>,
/// The unsafe block signer.
unsafe_block_signer: Option<Address>,
pub unsafe_block_signer: Option<Address>,
/// The socket address that the service is listening on.
socket: Option<SocketAddr>,
/// The [ConfigBuilder] constructs the config for `gossipsub`.
inner: Option<ConfigBuilder>,
pub socket: Option<SocketAddr>,
/// The [GossipConfig] constructs the config for `gossipsub`.
pub gossip_config: Option<GossipConfig>,
/// The [Keypair] for the node.
keypair: Option<Keypair>,
pub keypair: Option<Keypair>,
/// The [TcpConfig] for the swarm.
tcp_config: Option<TcpConfig>,
pub tcp_config: Option<TcpConfig>,
/// The [NoiseConfig] for the swarm.
noise_config: Option<NoiseConfig>,
pub noise_config: Option<NoiseConfig>,
/// The [YamuxConfig] for the swarm.
yamux_config: Option<YamuxConfig>,
pub yamux_config: Option<YamuxConfig>,
}

impl NetworkDriverBuilder {
Expand Down Expand Up @@ -87,18 +87,73 @@ impl NetworkDriverBuilder {
self
}

// TODO: extend builder with [ConfigBuilder] methods.

/// Specifies the [ConfigBuilder] for the `gossipsub` configuration.
pub fn with_gossip_config(&mut self, inner: ConfigBuilder) -> &mut Self {
self.inner = Some(inner);
/// Specifies the [GossipConfig] for the `gossipsub` configuration.
///
/// If not set, the [NetworkDriverBuilder] will use the default gossipsub
/// configuration defined in [config::default_config]. These defaults can
/// be extended by using the [config::default_config_builder] method to
/// build a custom [GossipConfig].
///
/// ## Example
///
/// ```rust,ignore
/// use op_net::gossip::config;
/// use op_net::NetworkDriverBuilder;
/// use std::net::{IpAddr, Ipv4Addr, SocketAddr};
///
/// let chain_id = 10;
/// let signer = Address::random();
/// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9099);
///
/// // Let's say we want to enable flood publishing and use all other default settings.
/// let cfg = config::default_config_builder().flood_publish(true).build().unwrap();
/// let mut builder = NetworkDriverBuilder::new()
/// .with_unsafe_block_signer(signer)
/// .with_chain_id(chain_id)
/// .with_socket(socket)
/// .with_gossip_config(cfg);
/// .build()
/// .unwrap();
/// ```
pub fn with_gossip_config(&mut self, cfg: GossipConfig) -> &mut Self {
self.gossip_config = Some(cfg);
self
}

/// Builds the [NetworkDriver].
///
/// ## Errors
///
/// Returns an error if any of the following required fields are not set:
/// - [NetworkDriverBuilder::unsafe_block_signer]
/// - [NetworkDriverBuilder::chain_id]
/// - [NetworkDriverBuilder::socket]
///
/// Set these fields using the respective methods on the [NetworkDriverBuilder]
/// before calling this method.
///
/// ## Example
///
/// ```rust,ignore
/// use std::net::{IpAddr, Ipv4Addr, SocketAddr};
/// use op_net::NetworkDriverBuilder;
///
/// let chain_id = 10;
/// let signer = Address::random();
/// let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9099);
/// let driver = NetworkDriverBuilder::new()
/// .with_unsafe_block_signer(signer)
/// .with_chain_id(chain_id)
/// .with_socket(socket)
/// .build()
/// .unwrap();
/// ```
pub fn build(&mut self) -> Result<NetworkDriver> {
// Build the config for gossipsub.
let config = self.inner.take().unwrap_or(config::default_config_builder()).build()?;
let config = match self.gossip_config.take() {
Some(cfg) => cfg,
None => config::default_config()?,
};
let unsafe_block_signer =
self.unsafe_block_signer.ok_or_else(|| eyre::eyre!("unsafe block signer not set"))?;
let chain_id = self.chain_id.ok_or_else(|| eyre::eyre!("chain ID not set"))?;
Expand Down Expand Up @@ -172,6 +227,36 @@ mod tests {
assert_eq!(err.to_string(), "socket address not set");
}

#[test]
fn test_build_custom_gossip_config() {
let id = 10;
let signer = Address::random();
let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 9099);
let cfg = config::default_config_builder().flood_publish(true).build().unwrap();
let driver = NetworkDriverBuilder::new()
.with_unsafe_block_signer(signer)
.with_chain_id(id)
.with_socket(socket)
.with_gossip_config(cfg)
.build()
.unwrap();
let signer_net_addr = NetworkAddress::try_from(socket).expect("network address");
let signer_multiaddr = Multiaddr::from(signer_net_addr);

// Driver Assertions
assert_eq!(driver.gossip.addr, signer_multiaddr);
assert_eq!(driver.discovery.chain_id, id);

// Block Handler Assertions
assert_eq!(driver.gossip.handler.chain_id, id);
let v1 = IdentTopic::new(format!("/optimism/{}/0/blocks", id));
assert_eq!(driver.gossip.handler.blocks_v1_topic.hash(), v1.hash());
let v2 = IdentTopic::new(format!("/optimism/{}/1/blocks", id));
assert_eq!(driver.gossip.handler.blocks_v2_topic.hash(), v2.hash());
let v3 = IdentTopic::new(format!("/optimism/{}/2/blocks", id));
assert_eq!(driver.gossip.handler.blocks_v3_topic.hash(), v3.hash());
}

#[test]
fn test_build_default_network_driver() {
let id = 10;
Expand Down
7 changes: 6 additions & 1 deletion crates/net/src/gossip/config.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Gossipsub Configuration
use lazy_static::lazy_static;
use libp2p::gossipsub::{ConfigBuilder, Message, MessageId};
use libp2p::gossipsub::{Config, ConfigBuilder, ConfigBuilderError, Message, MessageId};
use openssl::sha::sha256;
use snap::raw::Decoder;
use std::time::Duration;
Expand Down Expand Up @@ -91,6 +91,11 @@ pub fn default_config_builder() -> ConfigBuilder {
builder
}

/// Returns the default [Config] for gossipsub.
pub fn default_config() -> Result<Config, ConfigBuilderError> {
default_config_builder().build()
}

/// Computes the [MessageId] of a `gossipsub` message.
fn compute_message_id(msg: &Message) -> MessageId {
let mut decoder = Decoder::new();
Expand Down
9 changes: 7 additions & 2 deletions crates/net/src/gossip/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::gossip::{
use eyre::Result;
use futures::stream::StreamExt;
use libp2p::{swarm::SwarmEvent, Multiaddr, Swarm};
use tracing::{debug, error, info};

/// A [libp2p::Swarm] instance with an associated address to listen on.
pub struct GossipDriver {
Expand All @@ -28,6 +29,7 @@ impl GossipDriver {
/// Listens on the address.
pub fn listen(&mut self) -> Result<()> {
self.swarm.listen_on(self.addr.clone()).map_err(|_| eyre::eyre!("swarm listen failed"))?;
info!("Swarm listening on: {:?}", self.addr);
Ok(())
}

Expand All @@ -47,14 +49,14 @@ impl GossipDriver {
return;
};
if let Err(e) = self.dial(addr).await {
tracing::error!("Failed to dial peer: {:?}", e);
error!("Failed to dial peer: {:?}", e);
}
}

/// Dials the given [Multiaddr].
pub async fn dial(&mut self, peer: impl Into<Multiaddr>) -> Result<()> {
let addr: Multiaddr = peer.into();
self.swarm.dial(addr).map_err(|_| eyre::eyre!("dial failed"))?;
self.swarm.dial(addr).map_err(|e| eyre::eyre!("dial failed: {:?}", e))?;
Ok(())
}

Expand All @@ -66,8 +68,11 @@ impl GossipDriver {
message,
})) = event
{
debug!("Received message with topic: {}", message.topic);
if self.handler.topics().contains(&message.topic) {
debug!("Handling message with topic: {}", message.topic);
let status = self.handler.handle(message);
debug!("Reporting message validation result: {:?}", status);
_ = self
.swarm
.behaviour_mut()
Expand Down

0 comments on commit 13278aa

Please sign in to comment.