Skip to content
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
913b56b
feat: add tracing
varex83 Dec 5, 2025
51a691f
fix: linting, cargo-deny
varex83 Dec 5, 2025
e3ece2e
Merge remote-tracking branch 'origin/main' into bohdan/tracing
varex83 Dec 8, 2025
85a9b9c
feat: add p2p config
varex83 Dec 8, 2025
65d5ae7
fix: remove comment
varex83 Dec 9, 2025
a312796
Merge remote-tracking branch 'origin/main' into bohdan/p2p-config
varex83 Dec 9, 2025
1cc2a29
feat: add p2p metrics
varex83 Dec 9, 2025
c432d86
fix: rename label in example
varex83 Dec 9, 2025
aeaa971
fix: linting
varex83 Dec 9, 2025
65d3a49
Merge branch 'bohdan/p2p-config' into bohdan/p2p-create-node
varex83 Dec 10, 2025
a82bbc7
feat: [wip] add relay/ping
varex83 Dec 18, 2025
a2fe791
feat: add behaviours module, add relay client support
varex83 Dec 22, 2025
30a550b
fix: linter warnings
varex83 Dec 22, 2025
df9fdce
Merge remote-tracking branch 'origin/main' into bohdan/p2p-create-node
varex83 Dec 22, 2025
ddbb57b
fix: remove wildcard dependencies
varex83 Dec 22, 2025
3c5d3fe
feat: add connection gater
varex83 Dec 23, 2025
c663f61
fix: linter warnings
varex83 Dec 23, 2025
a244063
fix: linter warnings
varex83 Dec 23, 2025
adfc1ef
feat: create behaviour builder
varex83 Dec 24, 2025
31c4e27
feat: add relay server functionality
varex83 Dec 29, 2025
152fbcf
chore: fix linter, polish code
varex83 Dec 29, 2025
529d794
fix: cargo deny dependency issue
varex83 Dec 29, 2025
1eb752e
chore: add docs
varex83 Dec 30, 2025
7b72369
refactor: move relay-server to a different crate
varex83 Dec 30, 2025
90584d0
Merge remote-tracking branch 'origin/main' into bohdan/p2p-relay-server
varex83 Jan 12, 2026
b4a7be2
fix: remove unnecessary tick skip
varex83 Jan 12, 2026
c6ee116
fix: remove unused fn, drop lock after usage
varex83 Jan 12, 2026
8a0b118
fix: linter
varex83 Jan 12, 2026
5fd2e1b
fix: module visibility
varex83 Jan 12, 2026
a1ca920
feat: add bon support
varex83 Jan 12, 2026
f0a1830
feat: make ping config unconfigurable, use default ping config instead
varex83 Jan 12, 2026
281cc0e
fix: set ping timeout to 10 seconds
varex83 Jan 12, 2026
f7590f0
feat: add user agent support
varex83 Jan 12, 2026
efbaa5b
feat: make relay identify protocol const
varex83 Jan 13, 2026
231a80a
Merge remote-tracking branch 'origin/main' into bohdan/p2p-relay-server
varex83 Jan 13, 2026
42bfce4
Merge remote-tracking branch 'origin/main' into bohdan/p2p-relay-server
varex83 Jan 13, 2026
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
313 changes: 222 additions & 91 deletions Cargo.lock

Large diffs are not rendered by default.

14 changes: 9 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ members = [
"crates/charon-p2p",
"crates/charon-testutil",
"crates/tracing",
"crates/relay-server"
]
resolver = "3"

Expand All @@ -23,13 +24,14 @@ license = "Apache-2.0" # TODO(template) updat
publish = false

[workspace.dependencies]
alloy = { version = "1.3", features = ["essentials"] }
built = { version = "0.8.0", features = ["git2", "chrono"] }
blst = "0.3"
anyhow = "1"
axum = "0.8.6"
blst = "0.3.13"
cancellation = "0.1.0"
chrono = { version = "0.4", features = ["serde"] }
clap = { version = "4.5", features = ["derive", "env", "cargo"] }
clap = { version = "4.5.53", features = ["derive", "env", "cargo"] }
alloy = { version = "1.3", features = ["essentials"] }
built = { version = "0.8.0", features = ["git2", "chrono"] }
crossbeam = "0.8.4"
backon = "1.6.0"
hex = { version = "0.4.3" }
Expand All @@ -42,7 +44,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0" }
thiserror = "2.0"
tokio = { version = "1", features = ["full"] }
tokio-util = { version = "0.7" }
tokio-util = "0.7.11"
libp2p = { version = "0.56", features = ["full", "secp256k1"] }
url = "2.5"
uuid = { version = "1.19", features = ["serde", "v4"] }
Expand All @@ -58,6 +60,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing-loki = "0.2"
vise-exporter = "0.3"
vise = "0.3"
bon = "3.8"

# Crates in the workspace
charon = { path = "crates/charon" }
Expand All @@ -69,6 +72,7 @@ charon-k1util = { path = "crates/charon-k1util" }
charon-p2p = { path = "crates/charon-p2p" }
charon-testutil = { path = "crates/charon-testutil" }
charon-tracing = { path = "crates/tracing" }
charon-relay-server = { path = "crates/relay-server" }

[workspace.lints.rust]
missing_docs = "deny"
Expand Down
7 changes: 7 additions & 0 deletions crates/charon-p2p/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ license.workspace = true
publish.workspace = true

[dependencies]
axum.workspace = true
chrono.workspace = true
libp2p.workspace = true
thiserror.workspace = true
Expand All @@ -15,8 +16,14 @@ charon-eth2.workspace = true
charon-k1util.workspace = true
vise.workspace = true
tokio.workspace = true
tokio-util.workspace = true
rand.workspace = true
tempfile.workspace = true
charon-tracing.workspace = true
tracing.workspace = true
serde.workspace = true
serde_json.workspace = true
charon-core.workspace = true

[dev-dependencies]
charon-testutil.workspace = true
Expand Down
2 changes: 0 additions & 2 deletions crates/charon-p2p/examples/p2p.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use charon_p2p::{
pluto_mdns::{PlutoMdnsBehaviour, PlutoMdnsBehaviourEvent},
},
config::P2PConfig,
gater::ConnGater,
p2p::{Node, NodeType},
};
use clap::Parser;
Expand Down Expand Up @@ -40,7 +39,6 @@ async fn main() -> Result<()> {
let mut p2p: Node<_> = Node::new(
P2PConfig::default(),
key.clone(),
ConnGater::new_open_gater(),
false,
NodeType::QUIC,
PlutoMdnsBehaviour::new,
Expand Down
85 changes: 73 additions & 12 deletions crates/charon-p2p/src/behaviours/pluto.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
//! Pluto behaviour.

use std::time::Duration;
use std::sync::LazyLock;

use libp2p::{identify, identity::Keypair, ping, relay, swarm::NetworkBehaviour};

use crate::gater::ConnGater;
use crate::{config::default_ping_config, gater::ConnGater};

/// Pluto network behaviour.
#[derive(NetworkBehaviour)]
pub struct PlutoBehaviour {
/// Connection gater behaviour.
Expand All @@ -19,20 +20,80 @@ pub struct PlutoBehaviour {
}

impl PlutoBehaviour {
/// Creates a new Pluto behaviour.
/// Creates a new Pluto behaviour with default configuration.
pub fn new(key: &Keypair, relay_client: relay::client::Behaviour) -> Self {
PlutoBehaviourBuilder::default().build(key, relay_client)
}

/// Returns a new builder for configuring a PlutoBehaviour.
pub fn builder() -> PlutoBehaviourBuilder {
PlutoBehaviourBuilder::default()
}
}

/// The default user agent for the Pluto network.
pub static DEFAULT_USER_AGENT: LazyLock<String> =
LazyLock::new(|| format!("pluto/{}", *charon_core::version::VERSION));

/// The default identify protocol for the Pluto network.
pub static DEFAULT_IDENTIFY_PROTOCOL: LazyLock<String> =
LazyLock::new(|| format!("/pluto/{}", *charon_core::version::VERSION));

/// Builder for [`PlutoBehaviour`].
#[derive(Debug, Clone)]
pub struct PlutoBehaviourBuilder {
gater: Option<ConnGater>,
identify_protocol: String,
Comment thread
emlautarom1 marked this conversation as resolved.
user_agent: String,
}

impl Default for PlutoBehaviourBuilder {
fn default() -> Self {
Self {
gater: None,
identify_protocol: DEFAULT_IDENTIFY_PROTOCOL.clone(),
user_agent: DEFAULT_USER_AGENT.clone(),
}
}
}

impl PlutoBehaviourBuilder {
/// Creates a new builder with default configuration.
pub fn new() -> Self {
Self::default()
}

/// Sets the connection gater.
pub fn with_gater(mut self, gater: ConnGater) -> Self {
self.gater = Some(gater);
self
}

/// Sets the identify protocol string.
pub fn with_identify_protocol(mut self, protocol: impl Into<String>) -> Self {
self.identify_protocol = protocol.into();
self
}

/// Sets the user agent string.
pub fn with_user_agent(mut self, user_agent: impl Into<String>) -> Self {
self.user_agent = user_agent.into();
self
}

/// Builds the [`PlutoBehaviour`] with the provided keypair and relay
/// client.
pub fn build(self, key: &Keypair, relay_client: relay::client::Behaviour) -> PlutoBehaviour {
PlutoBehaviour {
gater: self
.gater
.unwrap_or_else(|| ConnGater::new_conn_gater(vec![], vec![])),
relay: relay_client,
identify: identify::Behaviour::new(identify::Config::new(
"/pluto/1.0.0-alpha".into(),
key.public(),
)),
ping: ping::Behaviour::new(
ping::Config::new()
.with_interval(Duration::from_secs(1))
.with_timeout(Duration::from_secs(2)),
identify: identify::Behaviour::new(
identify::Config::new(self.identify_protocol, key.public())
.with_agent_version(self.user_agent),
),
gater: ConnGater::new_conn_gater(vec![], vec![]),
ping: ping::Behaviour::new(default_ping_config()),
}
}
}
70 changes: 64 additions & 6 deletions crates/charon-p2p/src/behaviours/pluto_mdns.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
//! Pluto Mdns behaviour.

use libp2p::{identity::Keypair, mdns, relay, swarm::NetworkBehaviour};

use crate::behaviours::pluto::PlutoBehaviour;
use crate::behaviours::pluto::{PlutoBehaviour, PlutoBehaviourBuilder};

/// Pluto network behaviour.
/// Pluto network behaviour with mDNS discovery.
#[derive(NetworkBehaviour)]
pub struct PlutoMdnsBehaviour {
/// Pluto behaviour.
Expand All @@ -13,11 +14,68 @@ pub struct PlutoMdnsBehaviour {
}

impl PlutoMdnsBehaviour {
/// Creates a new Pluto Mdns behaviour.
/// Creates a new Pluto Mdns behaviour with default configuration.
pub fn new(key: &Keypair, relay_client: relay::client::Behaviour) -> Self {
Self {
pluto: PlutoBehaviour::new(key, relay_client),
mdns: mdns::tokio::Behaviour::new(mdns::Config::default(), key.public().to_peer_id())
PlutoMdnsBehaviourBuilder::default().build(key, relay_client)
}

/// Returns a new builder for configuring a PlutoMdnsBehaviour.
pub fn builder() -> PlutoMdnsBehaviourBuilder {
PlutoMdnsBehaviourBuilder::default()
}
}

/// Builder for [`PlutoMdnsBehaviour`].
#[derive(Default, Debug, Clone)]
pub struct PlutoMdnsBehaviourBuilder {
pluto: PlutoBehaviourBuilder,
mdns_config: mdns::Config,
}

impl PlutoMdnsBehaviourBuilder {
/// Creates a new builder with default configuration.
pub fn new() -> Self {
Self::default()
}

/// Replaces the inner [`PlutoBehaviourBuilder`] entirely.
pub fn with_pluto(mut self, pluto: PlutoBehaviourBuilder) -> Self {
self.pluto = pluto;
self
}

/// Configures the inner [`PlutoBehaviourBuilder`] via a closure.
///
/// This is ergonomic for inline configuration:
/// ```ignore
/// PlutoMdnsBehaviourBuilder::new()
/// .configure_pluto(|p| p.with_ping_interval(Duration::from_secs(5)))
/// .build(&key, relay_client)
/// ```
pub fn configure_pluto(
mut self,
f: impl FnOnce(PlutoBehaviourBuilder) -> PlutoBehaviourBuilder,
) -> Self {
self.pluto = f(self.pluto);
self
}

/// Sets the mDNS configuration.
pub fn with_mdns_config(mut self, config: mdns::Config) -> Self {
self.mdns_config = config;
self
}

/// Builds the [`PlutoMdnsBehaviour`] with the provided keypair and relay
/// client.
pub fn build(
self,
key: &Keypair,
relay_client: relay::client::Behaviour,
) -> PlutoMdnsBehaviour {
PlutoMdnsBehaviour {
pluto: self.pluto.build(key, relay_client),
mdns: mdns::tokio::Behaviour::new(self.mdns_config, key.public().to_peer_id())
.expect("Failed to create mDNS behaviour"),
}
}
Expand Down
80 changes: 77 additions & 3 deletions crates/charon-p2p/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
use std::{
net::{IpAddr, SocketAddr},
str::FromStr,
time::Duration,
};

use libp2p::{Multiaddr, multiaddr};
use libp2p::{Multiaddr, multiaddr, ping};

/// P2P configuration error.
#[derive(Debug, thiserror::Error)]
Expand Down Expand Up @@ -59,10 +60,10 @@ pub struct P2PConfig {
pub relays: Vec<Multiaddr>,

/// The external IP address of the node.
pub external_ip: String,
pub external_ip: Option<String>,

/// The external host of the node.
pub external_host: String,
pub external_host: Option<String>,

/// The TCP addresses of the node.
pub tcp_addrs: Vec<String>,
Expand Down Expand Up @@ -98,6 +99,79 @@ impl P2PConfig {

addrs.into_iter().map(multi_addr_from_ip_tcp_port).collect()
}

/// Returns a new builder for configuring a P2P configuration.
pub fn builder() -> P2PConfigBuilder {
P2PConfigBuilder::new()
}
}

/// Builder for [`P2PConfig`].
#[derive(Default, Debug, Clone)]
pub struct P2PConfigBuilder {
config: P2PConfig,
}

impl P2PConfigBuilder {
/// Creates a new builder with default configuration.
pub fn new() -> Self {
Self {
config: P2PConfig::default(),
}
}

/// Sets the relay multiaddrs.
pub fn with_relays(mut self, relays: Vec<Multiaddr>) -> Self {
self.config.relays = relays;
self
}

/// Sets the external IP address.
pub fn with_external_ip(mut self, external_ip: String) -> Self {
self.config.external_ip = Some(external_ip);
self
}

/// Sets the external host.
pub fn with_external_host(mut self, external_host: String) -> Self {
self.config.external_host = Some(external_host);
self
}

/// Sets the TCP addresses.
pub fn with_tcp_addrs(mut self, tcp_addrs: Vec<String>) -> Self {
self.config.tcp_addrs = tcp_addrs;
self
}

/// Sets the UDP addresses.
pub fn with_udp_addrs(mut self, udp_addrs: Vec<String>) -> Self {
self.config.udp_addrs = udp_addrs;
self
}

/// Sets whether to disable the reuse port.
pub fn with_disable_reuse_port(mut self, disable_reuse_port: bool) -> Self {
self.config.disable_reuse_port = disable_reuse_port;
self
}

/// Builds the [`P2PConfig`].
pub fn build(self) -> P2PConfig {
self.config
}
}

/// The default ping interval.
pub const DEFAULT_PING_INTERVAL: Duration = Duration::from_secs(1);
/// The default ping timeout.
pub const DEFAULT_PING_TIMEOUT: Duration = Duration::from_secs(10);

/// Returns the default ping configuration.
pub fn default_ping_config() -> ping::Config {
ping::Config::new()
.with_interval(DEFAULT_PING_INTERVAL)
.with_timeout(DEFAULT_PING_TIMEOUT)
}

/// Resolves a TCP address string to a SocketAddr, ensuring the IP is specified.
Expand Down
Loading