diff --git a/Cargo.lock b/Cargo.lock index 1b82fb3751..3231215736 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4821,6 +4821,7 @@ dependencies = [ "kona-p2p", "kona-protocol", "kona-supervisor-rpc", + "libp2p", "metrics", "op-alloy-consensus", "op-alloy-rpc-jsonrpsee", diff --git a/crates/node/p2p/src/rpc/request.rs b/crates/node/p2p/src/rpc/request.rs index e2a74877d4..f4789ae8f1 100644 --- a/crates/node/p2p/src/rpc/request.rs +++ b/crates/node/p2p/src/rpc/request.rs @@ -14,7 +14,7 @@ use discv5::{ use libp2p::PeerId; use tokio::sync::oneshot::Sender; -use libp2p::gossipsub::TopicHash; +use libp2p::{Multiaddr, gossipsub::TopicHash}; use super::{ PeerDump, PeerStats, @@ -40,6 +40,11 @@ pub enum P2pRpcRequest { /// Whether to only return connected peers. connected: bool, }, + /// Request to connect to a given peer. + ConnectPeer { + /// The [`Multiaddr`] of the peer to connect to. + address: Multiaddr, + }, /// Request to disconnect the specified peer. DisconnectPeer { /// The peer id to disconnect. @@ -64,9 +69,14 @@ impl P2pRpcRequest { Self::Peers { out, connected } => Self::handle_peers(out, connected, gossip, disc), Self::DisconnectPeer { peer_id } => Self::disconnect_peer(peer_id, gossip), Self::PeerStats(s) => Self::handle_peer_stats(s, gossip, disc), + Self::ConnectPeer { address } => Self::connect_peer(address, gossip), } } + fn connect_peer(address: Multiaddr, gossip: &mut GossipDriver) { + gossip.dial_multiaddr(address) + } + fn disconnect_peer(peer_id: PeerId, gossip: &mut GossipDriver) { if let Err(e) = gossip.swarm.disconnect_peer_id(peer_id) { warn!(target: "p2p::rpc", "Failed to disconnect peer {}: {:?}", peer_id, e); diff --git a/crates/node/rpc/Cargo.toml b/crates/node/rpc/Cargo.toml index 17e3b87b76..af5b0ce742 100644 --- a/crates/node/rpc/Cargo.toml +++ b/crates/node/rpc/Cargo.toml @@ -36,6 +36,7 @@ alloy-eips = { workspace = true, features = ["serde", "std"] } alloy-primitives = { workspace = true, features = ["map", "rlp", "serde", "std"] } # Misc +libp2p.workspace = true tracing.workspace = true thiserror.workspace = true derive_more = { workspace = true, default-features = false, features = [ diff --git a/crates/node/rpc/src/p2p.rs b/crates/node/rpc/src/p2p.rs index ad3556f013..cbd60e097d 100644 --- a/crates/node/rpc/src/p2p.rs +++ b/crates/node/rpc/src/p2p.rs @@ -143,9 +143,14 @@ impl OpP2PApiServer for NetworkRpc { } async fn opp2p_connect_peer(&self, _peer: String) -> RpcResult<()> { + use std::str::FromStr; kona_macros::inc!(gauge, kona_p2p::Metrics::RPC_CALLS, "method" => "opp2p_connectPeer"); - // Method not supported yet. - Err(ErrorObject::from(ErrorCode::MethodNotFound)) + let ma = libp2p::Multiaddr::from_str(&_peer) + .map_err(|_| ErrorObject::from(ErrorCode::InvalidParams))?; + self.sender + .send(P2pRpcRequest::ConnectPeer { address: ma }) + .await + .map_err(|_| ErrorObject::from(ErrorCode::InternalError)) } async fn opp2p_disconnect_peer(&self, peer_id: String) -> RpcResult<()> { @@ -163,3 +168,19 @@ impl OpP2PApiServer for NetworkRpc { .map_err(|_| ErrorObject::from(ErrorCode::InternalError)) } } + +#[cfg(test)] +mod tests { + #[test] + fn test_parse_multiaddr_string() { + use std::str::FromStr; + let ma = "/ip4/127.0.0.1/udt"; + let multiaddr = libp2p::Multiaddr::from_str(ma).unwrap(); + let components = multiaddr.iter().collect::>(); + assert_eq!( + components[0], + libp2p::multiaddr::Protocol::Ip4(std::net::Ipv4Addr::new(127, 0, 0, 1)) + ); + assert_eq!(components[1], libp2p::multiaddr::Protocol::Udt); + } +}