Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(net): Networking Crate #34

Merged
merged 16 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,678 changes: 1,502 additions & 176 deletions Cargo.lock

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ categories = ["cryptography", "cryptography::cryptocurrencies"]
members = [
"bin/hera",
"bin/op-rs",
"crates/net",
"crates/kona-providers",
"crates/rollup",
"crates/ser",
Expand Down Expand Up @@ -115,6 +116,9 @@ codegen-units = 1
incremental = false

[workspace.dependencies]
# Workspace
op-net = { path = "crates/net" }

# Optimism
superchain-registry = { version = "0.2.6", default-features = false }
kona-primitives = { git = "https://github.com/ethereum-optimism/kona", default-features = true }
Expand All @@ -133,6 +137,9 @@ alloy-rlp = "0.3.4"
# Tokio
tokio = { version = "1.21", default-features = false }

# SSZ
ssz_rs = "0.9.0"

# Reth
reth = { git = "https://github.com/paradigmxyz/reth", version = "1.0.5" }
reth-chainspec = { git = "https://github.com/paradigmxyz/reth", version = "1.0.5" }
Expand All @@ -149,12 +156,22 @@ reth-revm = { git = "https://github.com/paradigmxyz/reth", version = "1.0.5" }
reth-evm = { git = "https://github.com/paradigmxyz/reth", version = "1.0.5" }
reth-tracing = { git = "https://github.com/paradigmxyz/reth", version = "1.0.5" }

# Networking
snap = "1.1.1"
discv5 = "0.6.0"
openssl = { version = "0.10.66", features = ["vendored"] }
libp2p-identity = { version = "0.2.9", features = [ "secp256k1" ] }
libp2p = { version = "0.54.0", features = ["macros", "tokio", "tcp", "noise", "gossipsub", "ping", "yamux"] }

# Misc
tracing = "0.1.0"
eyre = "0.6.12"
clap = "4"
lazy_static = "1.5.0"
futures = "0.3.30"
async-trait = "0.1.81"
hashbrown = "0.14.5"
parking_lot = "0.12.3"
unsigned-varint = "0.8.0"
rand = { version = "0.8.3", features = ["small_rng"], default-features = false }
url = "2.5.2"
35 changes: 35 additions & 0 deletions crates/net/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[package]
name = "op-net"
description = "Networking library for OP Stack Consensus"
version = "0.0.0"
edition.workspace = true
authors.workspace = true
license.workspace = true
keywords.workspace = true
repository.workspace = true
categories.workspace = true
rust-version.workspace = true

[dependencies]
# Alloy
alloy.workspace = true
alloy-rlp.workspace = true

# Kona
kona-primitives.workspace = true

# Networking
ssz_rs.workspace = true
snap.workspace = true
futures.workspace = true
discv5.workspace = true
libp2p.workspace = true
openssl.workspace = true
libp2p-identity.workspace = true

# Misc
eyre.workspace = true
tokio.workspace = true
tracing.workspace = true
lazy_static.workspace = true
unsigned-varint.workspace = true
7 changes: 7 additions & 0 deletions crates/net/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Network Library

Contains a gossipsub driver to run discv5 peer discovery and block gossip.

## Acknowledgements

Largely based off [magi][https://github.com/a16z/magi]'s [p2p module](https://github.com/a16z/magi/tree/master/src/network).
139 changes: 139 additions & 0 deletions crates/net/src/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
//! Network Builder Module.

use alloy::primitives::Address;
use eyre::Result;
use std::net::SocketAddr;
use tokio::sync::watch::channel;

use libp2p::{
gossipsub::ConfigBuilder, noise::Config as NoiseConfig, tcp::Config as TcpConfig,
yamux::Config as YamuxConfig, Multiaddr, SwarmBuilder,
};
use libp2p_identity::Keypair;

use crate::{
discovery::builder::DiscoveryBuilder,
driver::NetworkDriver,
gossip::{behaviour::Behaviour, config, driver::GossipDriver, handler::BlockHandler},
types::address::NetworkAddress,
};

/// Constructs a [NetworkDriver] for Optimism's consensus-layer.
#[derive(Default)]
pub struct NetworkDriverBuilder {
/// The chain ID of the network.
chain_id: Option<u64>,
/// The unsafe block signer.
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>,
/// The [Keypair] for the node.
keypair: Option<Keypair>,
/// The [TcpConfig] for the swarm.
tcp_config: Option<TcpConfig>,
// /// The [NoiseConfig] for the swarm.
// noise_config: Option<NoiseConfig>,
/// The [YamuxConfig] for the swarm.
yamux_config: Option<YamuxConfig>,
}

impl NetworkDriverBuilder {
/// Creates a new [NetworkDriverBuilder].
pub fn new() -> Self {
Self::default()
}

/// Specifies the chain ID of the network.
pub fn with_chain_id(&mut self, chain_id: u64) -> &mut Self {
self.chain_id = Some(chain_id);
self
}

/// Specifies the unsafe block signer.
pub fn with_unsafe_block_signer(&mut self, unsafe_block_signer: Address) -> &mut Self {
self.unsafe_block_signer = Some(unsafe_block_signer);
self
}

/// Specifies the socket address that the service is listening on.
pub fn with_socket(&mut self, socket: SocketAddr) -> &mut Self {
self.socket = Some(socket);
self
}

/// Specifies the keypair for the node.
pub fn with_keypair(&mut self, keypair: Keypair) -> &mut Self {
self.keypair = Some(keypair);
self
}

/// Specifies the [TcpConfig] for the swarm.
pub fn with_tcp_config(&mut self, tcp_config: TcpConfig) -> &mut Self {
self.tcp_config = Some(tcp_config);
self
}

// /// Specifies the [NoiseConfig] for the swarm.
// pub fn with_noise_config(&mut self, noise_config: NoiseConfig) -> &mut Self {
// self.noise_config = Some(noise_config);
// self
// }

/// Specifies the [YamuxConfig] for the swarm.
pub fn with_yamux_config(&mut self, yamux_config: YamuxConfig) -> &mut Self {
self.yamux_config = Some(yamux_config);
self
}

// TODO: extend builder with [ConfigBuilder] methods.
refcell marked this conversation as resolved.
Show resolved Hide resolved

/// Specifies the [ConfigBuilder] for the `gossipsub` configuration.
pub fn with_gossip_config(&mut self, inner: ConfigBuilder) -> &mut Self {
self.inner = Some(inner);
self
}

/// Builds the [NetworkDriver].
pub fn build(self) -> Result<NetworkDriver> {
// Build the config for gossipsub.
let config = self.inner.unwrap_or(config::default_config_builder()).build()?;
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"))?;

// Create the block handler.
let (unsafe_block_signer_sender, unsafe_block_signer_recv) = channel(unsafe_block_signer);
let (handler, unsafe_block_recv) = BlockHandler::new(chain_id, unsafe_block_signer_recv);

// Construct the gossipsub behaviour.
let behaviour = Behaviour::new(config, &[Box::new(handler.clone())])?;

// Build the swarm.
let keypair = self.keypair.unwrap_or(Keypair::generate_secp256k1());
let swarm = SwarmBuilder::with_existing_identity(keypair)
.with_tokio()
.with_tcp(self.tcp_config.unwrap_or_default(), NoiseConfig::new, || {
self.yamux_config.unwrap_or_default()
})?
.with_behaviour(|_| behaviour)?
.build();
let addr = self.socket.ok_or_else(|| eyre::eyre!("socket address not set"))?;
let addr = NetworkAddress::try_from(addr)?;
let swarm_addr = Multiaddr::from(addr);
let swarm = GossipDriver::new(swarm, swarm_addr);

// Build the discovery service
let discovery =
DiscoveryBuilder::new().with_address(addr).with_chain_id(chain_id).build()?;

Ok(NetworkDriver {
unsafe_block_recv,
unsafe_block_signer_sender,
handler,
swarm,
discovery,
})
}
}
22 changes: 22 additions & 0 deletions crates/net/src/discovery/bootnodes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//! Bootnodes for consensus network discovery.

use discv5::enr::{CombinedKey, Enr};
use lazy_static::lazy_static;
use std::str::FromStr;

lazy_static! {
/// Default bootnodes to use.
pub static ref BOOTNODES: Vec<Enr<CombinedKey>> = [
// Optimism Mainnet Bootnodes
Enr::from_str("enr:-J64QBbwPjPLZ6IOOToOLsSjtFUjjzN66qmBZdUexpO32Klrc458Q24kbty2PdRaLacHM5z-cZQr8mjeQu3pik6jPSOGAYYFIqBfgmlkgnY0gmlwhDaRWFWHb3BzdGFja4SzlAUAiXNlY3AyNTZrMaECmeSnJh7zjKrDSPoNMGXoopeDF4hhpj5I0OsQUUt4u8uDdGNwgiQGg3VkcIIkBg").unwrap(),
Enr::from_str("enr:-J64QAlTCDa188Hl1OGv5_2Kj2nWCsvxMVc_rEnLtw7RPFbOfqUOV6khXT_PH6cC603I2ynY31rSQ8sI9gLeJbfFGaWGAYYFIrpdgmlkgnY0gmlwhANWgzCHb3BzdGFja4SzlAUAiXNlY3AyNTZrMaECkySjcg-2v0uWAsFsZZu43qNHppGr2D5F913Qqs5jDCGDdGNwgiQGg3VkcIIkBg").unwrap(),
Enr::from_str("enr:-J24QGEzN4mJgLWNTUNwj7riVJ2ZjRLenOFccl2dbRFxHHOCCZx8SXWzgf-sLzrGs6QgqSFCvGXVgGPBkRkfOWlT1-iGAYe6Cu93gmlkgnY0gmlwhCJBEUSHb3BzdGFja4OkAwCJc2VjcDI1NmsxoQLuYIwaYOHg3CUQhCkS-RsSHmUd1b_x93-9yQ5ItS6udIN0Y3CCIyuDdWRwgiMr").unwrap(),

// Base Mainnet Bootnodes
Enr::from_str("enr:-J24QNz9lbrKbN4iSmmjtnr7SjUMk4zB7f1krHZcTZx-JRKZd0kA2gjufUROD6T3sOWDVDnFJRvqBBo62zuF-hYCohOGAYiOoEyEgmlkgnY0gmlwhAPniryHb3BzdGFja4OFQgCJc2VjcDI1NmsxoQKNVFlCxh_B-716tTs-h1vMzZkSs1FTu_OYTNjgufplG4N0Y3CCJAaDdWRwgiQG").unwrap(),
Enr::from_str("enr:-J24QH-f1wt99sfpHy4c0QJM-NfmsIfmlLAMMcgZCUEgKG_BBYFc6FwYgaMJMQN5dsRBJApIok0jFn-9CS842lGpLmqGAYiOoDRAgmlkgnY0gmlwhLhIgb2Hb3BzdGFja4OFQgCJc2VjcDI1NmsxoQJ9FTIv8B9myn1MWaC_2lJ-sMoeCDkusCsk4BYHjjCq04N0Y3CCJAaDdWRwgiQG").unwrap(),
Enr::from_str("enr:-J24QDXyyxvQYsd0yfsN0cRr1lZ1N11zGTplMNlW4xNEc7LkPXh0NAJ9iSOVdRO95GPYAIc6xmyoCCG6_0JxdL3a0zaGAYiOoAjFgmlkgnY0gmlwhAPckbGHb3BzdGFja4OFQgCJc2VjcDI1NmsxoQJwoS7tzwxqXSyFL7g0JM-KWVbgvjfB8JA__T7yY_cYboN0Y3CCJAaDdWRwgiQG").unwrap(),
Enr::from_str("enr:-J24QHmGyBwUZXIcsGYMaUqGGSl4CFdx9Tozu-vQCn5bHIQbR7On7dZbU61vYvfrJr30t0iahSqhc64J46MnUO2JvQaGAYiOoCKKgmlkgnY0gmlwhAPnCzSHb3BzdGFja4OFQgCJc2VjcDI1NmsxoQINc4fSijfbNIiGhcgvwjsjxVFJHUstK9L1T8OTKUjgloN0Y3CCJAaDdWRwgiQG").unwrap(),
Enr::from_str("enr:-J24QG3ypT4xSu0gjb5PABCmVxZqBjVw9ca7pvsI8jl4KATYAnxBmfkaIuEqy9sKvDHKuNCsy57WwK9wTt2aQgcaDDyGAYiOoGAXgmlkgnY0gmlwhDbGmZaHb3BzdGFja4OFQgCJc2VjcDI1NmsxoQIeAK_--tcLEiu7HvoUlbV52MspE0uCocsx1f_rYvRenIN0Y3CCJAaDdWRwgiQG").unwrap(),
].to_vec();
}
59 changes: 59 additions & 0 deletions crates/net/src/discovery/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//! Contains a builder for the discovery service.

use crate::{
discovery::driver::DiscoveryDriver,
types::{address::NetworkAddress, enr::OpStackEnr},
};
use discv5::{
enr::{CombinedKey, Enr},
ConfigBuilder, Discv5, ListenConfig,
};
use eyre::Result;

use crate::types::enr::OP_CL_KEY;

/// Discovery service builder.
#[derive(Debug, Default, Clone)]
pub struct DiscoveryBuilder {
/// The discovery service address.
address: Option<NetworkAddress>,
/// The chain ID of the network.
chain_id: Option<u64>,
}

impl DiscoveryBuilder {
/// Creates a new discovery builder.
pub fn new() -> Self {
Self::default()
}

/// Sets the discovery service address.
pub fn with_address(mut self, address: NetworkAddress) -> Self {
self.address = Some(address);
self
}

/// Sets the chain ID of the network.
pub fn with_chain_id(mut self, chain_id: u64) -> Self {
self.chain_id = Some(chain_id);
self
}

/// Builds a [DiscoveryDriver].
pub fn build(self) -> Result<DiscoveryDriver> {
let addr = self.address.ok_or_else(|| eyre::eyre!("address not set"))?;
let chain_id = self.chain_id.ok_or_else(|| eyre::eyre!("chain ID not set"))?;
let opstack = OpStackEnr::new(chain_id, 0);
let opstack_data: Vec<u8> = opstack.into();

let key = CombinedKey::generate_secp256k1();
let enr = Enr::builder().add_value_rlp(OP_CL_KEY, opstack_data.into()).build(&key)?;
let listen_config = ListenConfig::from_ip(addr.ip.into(), addr.port);
let config = ConfigBuilder::new(listen_config).build();

let disc = Discv5::new(enr, key, config)
.map_err(|_| eyre::eyre!("could not create disc service"))?;

Ok(DiscoveryDriver::new(disc, chain_id))
}
}
Loading