Skip to content

Connect to Peers

In this section you'll learn how to join the lightning network.

Firstly we need to have the ability to do high performance I/O operations. LDK provides default implementations for initializing all of your networking needs. If you are using Rust, you can use our simple socket handling library lightning_net_tokio. In Kotlin/Java you can use the NioPeerHandler which uses Java's NIO I/O interface.

In TypeScript there is no networking module for non-Node.js (browser) environments: WASM cannot open raw TCP sockets. Instead you implement a SocketDescriptor that bridges LDK to a transport you do have — typically a WebSocket talking to a WebSocket-to-TCP proxy you run server-side — and feed bytes to the PeerManager yourself. (Node.js users can use the separate lightningdevkit-node-net package instead.)

What it's used for: making peer connections, facilitating peer data to and from LDK

rust
use lightning_net_tokio; // use LDK's sample networking module

let listen_port = 9735;
let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", listen_port))
    .await.unwrap();
loop {
    let tcp_stream = listener.accept().await.unwrap().0;
    let peer_manager = peer_manager.clone();
    tokio::spawn(async move {
        // Use LDK's supplied networking battery to facilitate inbound
        // connections.
        lightning_net_tokio::setup_inbound(
            peer_manager,
            tcp_stream.into_std().unwrap(),
        )
        .await;
    });
}
java
val nioPeerHandler = channelManagerConstructor.nio_peer_handler
val port = 9777
nioPeerHandler.bind_listener(InetSocketAddress("127.0.0.1", port))
typescript
import * as ldk from "lightningdevkit";

// Bridge a transport (here a WebSocket to a WS->TCP proxy) to LDK by
// implementing SocketDescriptor. There is no raw TCP in the browser.
function makeSocketDescriptor(ws: WebSocket, id: bigint): ldk.SocketDescriptor {
  return ldk.SocketDescriptor.new_impl({
    // Return how many bytes you accepted; buffer the rest if back-pressured.
    send_data(data: Uint8Array, _resume_read: boolean): number {
      ws.send(data);
      return data.length;
    },
    disconnect_socket(): void { ws.close(); },
    eq(other: ldk.SocketDescriptor): boolean { return other.hash() === id; },
    hash(): bigint { return id; },
  } as ldk.SocketDescriptorInterface);
}

// On an inbound connection from the proxy:
const descriptor = makeSocketDescriptor(ws, 1n);
peerManager.new_inbound_connection(
  descriptor,
  ldk.Option_SocketAddressZ.constructor_none()
);

// Forward every chunk you receive from the socket into LDK, then flush:
ws.onmessage = (ev) => {
  peerManager.read_event(descriptor, new Uint8Array(ev.data));
  peerManager.process_events();
};

Connections to other peers are established with PeerManager. You'll need to know the pubkey and address of another node that you want as a peer. Once the connection is established and the handshake is complete, PeerManager will show the peer's pubkey in its list of peers.

rust
match lightning_net_tokio::connect_outbound(peer_manager.clone(), pubkey, address).await {
	Some(connection_closed_future) => {
		let mut connection_closed_future = Box::pin(connection_closed_future);
		loop {
			// Make sure the connection is still established.
			match futures::poll!(&mut connection_closed_future) {
				std::task::Poll::Ready(_) => {
					panic!("ERROR: Peer disconnected before handshake completed");
				}
				std::task::Poll::Pending => {}
			}

			// Wait for the handshake to complete. `get_peer_node_ids` was
			// replaced by `list_peers`, which returns rich `PeerDetails`.
			match peer_manager.list_peers().iter().find(|p| p.counterparty_node_id == pubkey) {
				Some(_) => break,
				None => tokio::time::sleep(std::time::Duration::from_millis(10)).await,
			}
		}
	}
	None => panic!("ERROR: Failed to connect to peer"),
}
java
try {
    // Connect and wait for the handshake to complete.
    val address: SocketAddress = InetSocketAddress(hostname, port)
    nioPeerHandler.connect(pubkeyHex.toByteArray(), address, 5555)

    // The peer's pubkey will be present in the list of peers. (`get_peer_node_ids`
    // was removed in favour of `list_peers`, which returns `PeerDetails`.)
    val peerManager: PeerManager = channelManagerConstructor.peer_manager
    val peerNodeIds = peerManager.list_peers().map { it._counterparty_node_id }

    } catch (e: IOException) {
    // Handle failure when connecting to a peer.

}
typescript
import * as ldk from "lightningdevkit";

// `pubkey` is the peer's 33-byte node id. Kick off the outbound handshake;
// `new_outbound_connection` returns the first bytes to send to the peer.
const descriptor = makeSocketDescriptor(ws, 2n);
const initialSend = peerManager.new_outbound_connection(
  pubkey,
  descriptor,
  ldk.Option_SocketAddressZ.constructor_none()
);
if (initialSend instanceof ldk.Result_CVec_u8ZPeerHandleErrorZ_OK) {
  ws.send(initialSend.res);
}

// Feed received bytes in (as in the inbound example), then check the handshake
// completed by looking for the peer in the list. (`get_peer_node_ids` was
// removed in favour of `list_peers`, which returns `PeerDetails`.)
const connected = peerManager
  .list_peers()
  .some((p) => p.get_counterparty_node_id().toString() === pubkey.toString());

Dependencies: PeerManager

References: Rust lightning-net-tokio docs, Rust PeerManager docs, Java/Kotlin NioPeerHandler bindings, Java/Kotlin PeerManager bindings, TypeScript SocketDescriptor bindings