1 import * as ldk from "lightningdevkit";
2 import * as net from "net";
5 * Handles TCP connections using Node.JS's 'net' module given an `ldk.PeerManager`.
7 export class NodeLDKNet {
9 private servers: net.Server[];
10 public constructor(public peer_manager: ldk.PeerManager) {
12 if (peer_manager._node_ldk_net_singleton_check != undefined) {
13 throw "Only one NdoeLDKNet should exist per PeerManager";
16 peer_manager._node_ldk_net_singleton_check = this;
17 this.ping_timer = setInterval(function() {
18 peer_manager.timer_tick_occurred();
19 peer_manager.process_events();
25 * Disconnects all connections and releases all resources for this net handler.
28 clearInterval(this.ping_timer);
29 for (const server of this.servers) {
32 this.peer_manager.disconnect_all_peers();
34 delete this.peer_manager._node_ldk_net_singleton_check;
38 * Processes any pending events for the PeerManager, sending queued messages.
39 * You should call this (or peer_manager.process_events()) any time you take an action which
40 * is likely to generate messages to send (eg send a payment, processing payment forwards,
43 public process_events() { this.peer_manager.process_events(); }
45 private descriptor_count = BigInt(0);
46 private get_descriptor(socket: net.Socket): ldk.SocketDescriptor {
47 const this_index = this.descriptor_count;
48 this.descriptor_count += BigInt(1);
50 socket.setNoDelay(true);
52 const this_pm = this.peer_manager;
53 var sock_write_waiting = false;
55 let descriptor = ldk.SocketDescriptor.new_impl ({
56 send_data(data: Uint8Array, resume_read: boolean): number {
57 if (resume_read) socket.resume();
59 if (sock_write_waiting) return 0;
60 const written = socket.write(data);
61 if (!written) sock_write_waiting = true;
64 disconnect_socket(): void {
67 eq(other: ldk.SocketDescriptor): boolean {
68 return other.hash() == this.hash();
73 } as ldk.SocketDescriptorInterface);
75 socket.on("drain", function() {
76 if (sock_write_waiting) {
77 if (!this_pm.write_buffer_space_avail(descriptor).is_ok()) {
78 descriptor.disconnect_socket();
83 socket.on("data", function(data) {
84 const res = this_pm.read_event(descriptor, data);
85 if (!res.is_ok()) descriptor.disconnect_socket();
86 else if ((res as ldk.Result_boolPeerHandleErrorZ_OK).res) socket.pause();
87 this_pm.process_events();
90 socket.on("close", function() {
91 this_pm.socket_disconnected(descriptor);
93 socket.on("error", function() {
94 this_pm.socket_disconnected(descriptor);
100 private static v4_addr_from_ip(ip: string, port: number): ldk.SocketAddress {
101 const sockaddr = ip.split(".").map(parseFloat);
102 return ldk.SocketAddress.constructor_tcp_ip_v4(new Uint8Array(sockaddr), port);
104 private static v6_addr_from_ip(ip: string, port: number): ldk.SocketAddress {
105 const sockaddr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
106 const halves = ip.split("::"); // either one or two elements
107 const first_half = halves[0].split(":");
108 for (var idx = 0; idx < first_half.length; idx++) {
109 const v = parseInt(first_half[idx], 16);
110 sockaddr[idx*2] = v >> 8;
111 sockaddr[idx*2 + 1] = v & 0xff;
113 if (halves.length == 2) {
114 const second_half = halves[1].split(":");
115 for (var idx = 0; idx < second_half.length; idx++) {
116 const v = parseInt(second_half[second_half.length - idx - 1], 16);
117 sockaddr[14 - idx*2] = v >> 8;
118 sockaddr[15 - idx*2] = v & 0xff;
121 return ldk.SocketAddress.constructor_tcp_ip_v6(new Uint8Array(sockaddr), port);
124 private static get_addr_from_socket(socket: net.Socket): ldk.Option_SocketAddressZ {
125 const addr = socket.remoteAddress;
126 if (addr === undefined)
127 return ldk.Option_SocketAddressZ.constructor_none();
128 if (net.isIPv4(addr)) {
129 return ldk.Option_SocketAddressZ.constructor_some(NodeLDKNet.v4_addr_from_ip(addr, socket.remotePort));
131 if (net.isIPv6(addr)) {
132 return ldk.Option_SocketAddressZ.constructor_some(NodeLDKNet.v6_addr_from_ip(addr, socket.remotePort));
134 return ldk.Option_SocketAddressZ.constructor_none();
138 * Binds a listener on the given host and port, accepting incoming connections.
140 public async bind_listener(host: string, port: number) {
141 const this_handler = this;
142 const server = net.createServer(function(incoming_sock: net.Socket) {
143 const descriptor = this_handler.get_descriptor(incoming_sock);
144 const res = this_handler.peer_manager
145 .new_inbound_connection(descriptor, NodeLDKNet.get_addr_from_socket(incoming_sock));
146 if (!res.is_ok()) descriptor.disconnect_socket();
148 const servers_list = this.servers;
149 return new Promise<void>((resolve, reject) => {
150 server.on("error", function() {
154 server.on("listening", function() {
155 servers_list.push(server);
158 server.listen(port, host);
163 * Establishes an outgoing connection to the given peer at the given host and port.
165 * Note that the peer will not appear in the PeerManager peers list until the socket has
166 * connected and the initial handshake completes.
168 public async connect_peer(host: string, port: number, peer_node_id: Uint8Array) {
169 const this_handler = this;
170 const sock = new net.Socket();
171 const res = new Promise<void>((resolve, reject) => {
172 sock.on("connect", function() { resolve(); });
173 sock.on("error", function() { reject(); });
175 sock.connect(port, host, function() {
176 const descriptor = this_handler.get_descriptor(sock);
177 const res = this_handler.peer_manager
178 .new_outbound_connection(peer_node_id, descriptor, NodeLDKNet.get_addr_from_socket(sock));
179 if (!res.is_ok()) descriptor.disconnect_socket();
181 const bytes = (res as ldk.Result_CVec_u8ZPeerHandleErrorZ_OK).res;
182 const send_res = descriptor.send_data(bytes, true);
183 console.assert(send_res == bytes.length);