lnsocket_lib.js (11045B)
1 2 async function lnsocket_init() { 3 const module = await Module() 4 5 function SocketImpl(host) { 6 if (!(this instanceof SocketImpl)) 7 return new SocketImpl(host) 8 9 if (typeof WebSocket !== 'undefined') { 10 console.log("WebSocket", typeof WebSocket) 11 const ok = host.startsWith("ws://") || host.startsWith("wss://") 12 if (!ok) 13 throw new Error("host must start with ws:// or wss://") 14 const ws = new WebSocket(host) 15 ws.ondata = function(fn) { 16 ws.onmessage = (v) => { 17 const data = v.data.arrayBuffer() 18 fn(data) 19 } 20 } 21 return ws 22 } 23 24 // 25 // we're in nodejs 26 // 27 const net = require('net') 28 let [hostname,port] = host.split(":") 29 port = +port || 9735 30 const socket = net.createConnection(port, hostname, () => { 31 socket.emit("open") 32 }) 33 socket.addEventListener = socket.on.bind(socket) 34 35 if (socket.onmessage) 36 throw new Error("socket already has onmessage?") 37 38 socket.ondata = (fn) => { 39 socket.on('data', fn) 40 } 41 42 socket.close = () => { 43 socket.destroy() 44 } 45 46 if (socket.send) 47 throw new Error("socket already has send?") 48 49 socket.send = function socket_send(data) { 50 return new Promise((resolve, reject) => { 51 socket.write(data, resolve) 52 }); 53 } 54 55 return socket 56 } 57 58 const ACT_ONE_SIZE = 50 59 const ACT_TWO_SIZE = 50 60 const ACT_THREE_SIZE = 66 61 const DEFAULT_TIMEOUT = 15000 62 63 const COMMANDO_REPLY_CONTINUES = 0x594b 64 const COMMANDO_REPLY_TERM = 0x594d 65 66 const lnsocket_create = module.cwrap("lnsocket_create", "number") 67 const lnsocket_destroy = module.cwrap("lnsocket_destroy", "number") 68 const lnsocket_encrypt = module.cwrap("lnsocket_encrypt", "number", ["int", "array", "int", "int"]) 69 const lnsocket_decrypt = module.cwrap("lnsocket_decrypt", "number", ["int", "array", "int"]) 70 const lnsocket_decrypt_header = module.cwrap("lnsocket_decrypt_header", "number", ["number", "array"]) 71 const lnsocket_msgbuf = module.cwrap("lnsocket_msgbuf", "number", ["int"]) 72 const lnsocket_act_one = module.cwrap("lnsocket_act_one", "number", ["number", "string"]) 73 const lnsocket_act_two = module.cwrap("lnsocket_act_two", "number", ["number", "array"]) 74 const lnsocket_print_errors = module.cwrap("lnsocket_print_errors", "int") 75 const lnsocket_genkey = module.cwrap("lnsocket_genkey", null, ["number"]) 76 const lnsocket_setkey = module.cwrap("lnsocket_setkey", "number", ["number", "array"]) 77 const lnsocket_make_default_initmsg = module.cwrap("lnsocket_make_default_initmsg", "int", ["int", "int"]) 78 const lnsocket_make_ping_msg = module.cwrap("lnsocket_make_ping_msg", "int", ["int", "int", "int", "int"]) 79 const commando_make_rpc_msg = module.cwrap("commando_make_rpc_msg", "int", ["string", "string", "string", "number", "int", "int"]) 80 81 function concat_u8_arrays(arrays) { 82 // sum of individual array lengths 83 let totalLength = arrays.reduce((acc, value) => 84 acc + (value.length || value.byteLength) 85 , 0); 86 87 if (!arrays.length) return null; 88 89 let result = new Uint8Array(totalLength); 90 91 let length = 0; 92 for (let array of arrays) { 93 if (array instanceof ArrayBuffer) 94 result.set(new Uint8Array(array), length); 95 else 96 result.set(array, length); 97 98 length += (array.length || array.byteLength); 99 } 100 101 return result; 102 } 103 104 function parse_msgtype(buf) { 105 return buf[0] << 8 | buf[1] 106 } 107 108 function wasm_free(buf) { 109 module._free(buf); 110 } 111 112 function char_to_hex(cstr) { 113 const c = cstr.charCodeAt(0) 114 // c >= 0 && c <= 9 115 if (c >= 48 && c <= 57) { 116 return c - 48; 117 } 118 // c >= a && c <= f 119 if (c >= 97 && c <= 102) { 120 return c - 97 + 10; 121 } 122 // c >= A && c <= F 123 if (c >= 65 && c <= 70) { 124 return c - 65 + 10; 125 } 126 return -1; 127 } 128 129 130 function hex_decode(str, buflen) 131 { 132 let bufsize = buflen || 33 133 let c1, c2 134 let i = 0 135 let j = 0 136 let buf = new Uint8Array(bufsize) 137 let slen = str.length 138 while (slen > 1) { 139 if (-1==(c1 = char_to_hex(str[j])) || -1==(c2 = char_to_hex(str[j+1]))) 140 return null; 141 if (!bufsize) 142 return null; 143 j += 2 144 slen -= 2 145 buf[i++] = (c1 << 4) | c2 146 bufsize--; 147 } 148 149 return buf 150 } 151 152 function wasm_alloc(len) { 153 const buf = module._malloc(len); 154 module.HEAPU8.set(Uint8Array, buf); 155 return buf 156 } 157 158 function wasm_mem(ptr, size) { 159 return new Uint8Array(module.HEAPU8.buffer, ptr, size); 160 } 161 162 function LNSocket(opts) { 163 if (!(this instanceof LNSocket)) 164 return new LNSocket(opts) 165 166 this.opts = opts || { 167 timeout: DEFAULT_TIMEOUT 168 } 169 this.queue = [] 170 this.ln = lnsocket_create() 171 } 172 173 LNSocket.prototype.queue_recv = function() { 174 let self = this 175 return new Promise((resolve, reject) => { 176 const checker = setInterval(() => { 177 const val = self.queue.shift() 178 if (val) { 179 clearInterval(checker) 180 resolve(val) 181 } else if (!self.connected) { 182 clearInterval(checker) 183 reject() 184 } 185 }, 5); 186 }) 187 } 188 189 LNSocket.prototype.print_errors = function _lnsocket_print_errors() { 190 lnsocket_print_errors(this.ln) 191 } 192 193 LNSocket.prototype.genkey = function _lnsocket_genkey() { 194 lnsocket_genkey(this.ln) 195 } 196 197 LNSocket.prototype.setkeyraw = function lnsocket_setkeyraw(rawkey) { 198 return lnsocket_setkey(this.ln, rawkey) 199 } 200 201 LNSocket.prototype.setkey = function _lnsocket_setkey(key) { 202 const rawkey = hex_decode(key) 203 return this.setkeyraw(rawkey) 204 } 205 206 LNSocket.prototype.act_one_data = function _lnsocket_act_one(node_id) { 207 const act_one_ptr = lnsocket_act_one(this.ln, node_id) 208 if (act_one_ptr === 0) 209 return null 210 return wasm_mem(act_one_ptr, ACT_ONE_SIZE) 211 } 212 213 LNSocket.prototype.act_two = function _lnsocket_act_two(act2) { 214 const act_three_ptr = lnsocket_act_two(this.ln, new Uint8Array(act2)) 215 if (act_three_ptr === 0) { 216 this.print_errors() 217 return null 218 } 219 return wasm_mem(act_three_ptr, ACT_THREE_SIZE) 220 } 221 222 LNSocket.prototype.connect = async function lnsocket_connect(node_id, host) { 223 await handle_connect(this, node_id, host) 224 225 const act1 = this.act_one_data(node_id) 226 this.ws.send(act1) 227 const act2 = await this.read_all(ACT_TWO_SIZE) 228 if (act2.length != ACT_TWO_SIZE) { 229 throw new Error(`expected act2 to be ${ACT_TWO_SIZE} long, got ${act2.length}`) 230 } 231 const act3 = this.act_two(act2) 232 this.ws.send(act3) 233 } 234 235 LNSocket.prototype.connect_and_init = async function _connect_and_init(node_id, host) { 236 await this.connect(node_id, host) 237 await this.perform_init() 238 } 239 240 LNSocket.prototype.read_all = async function read_all(n) { 241 let count = 0 242 let chunks = [] 243 if (!this.connected) 244 throw new Error("read_all: not connected") 245 while (true) { 246 let res = await this.queue_recv() 247 248 const remaining = n - count 249 250 if (res.byteLength > remaining) { 251 chunks.push(res.slice(0, remaining)) 252 this.queue.unshift(res.slice(remaining)) 253 break 254 } else if (res.byteLength === remaining) { 255 chunks.push(res) 256 break 257 } 258 259 chunks.push(res) 260 count += res.byteLength 261 } 262 263 return concat_u8_arrays(chunks) 264 } 265 266 LNSocket.prototype.read_header = async function read_header() { 267 const header = await this.read_all(18) 268 if (header.length != 18) 269 throw new Error("Failed to read header") 270 return lnsocket_decrypt_header(this.ln, header) 271 } 272 273 LNSocket.prototype.rpc = async function lnsocket_rpc(opts) { 274 const msg = this.make_commando_msg(opts) 275 this.write(msg) 276 const res = await this.read_all_rpc() 277 return JSON.parse(res) 278 } 279 280 LNSocket.prototype.recv = async function lnsocket_recv() { 281 const msg = await this.read() 282 const msgtype = parse_msgtype(msg.slice(0,2)) 283 const res = [msgtype, msg.slice(2)] 284 return res 285 } 286 287 LNSocket.prototype.read_all_rpc = async function read_all_rpc() { 288 let chunks = [] 289 while (true) { 290 const [typ, msg] = await this.recv() 291 switch (typ) { 292 case COMMANDO_REPLY_TERM: 293 chunks.push(msg.slice(8)) 294 return new TextDecoder().decode(concat_u8_arrays(chunks)); 295 case COMMANDO_REPLY_CONTINUES: 296 chunks.push(msg.slice(8)) 297 break 298 default: 299 console.log("got unknown type", typ) 300 continue 301 } 302 } 303 } 304 305 LNSocket.prototype.make_commando_msg = function _lnsocket_make_commando_msg(opts) { 306 const buflen = 4096 307 let len = 0; 308 const buf = wasm_alloc(buflen); 309 310 const params = JSON.stringify(opts.params||{}) 311 if (!(len = commando_make_rpc_msg(opts.method, params, opts.rune, 312 0, buf, buflen))) { 313 throw new Error("couldn't make commando msg"); 314 } 315 316 const dat = wasm_mem(buf, len) 317 wasm_free(buf); 318 return dat 319 } 320 321 LNSocket.prototype.make_ping_msg = function _lnsocket_make_ping_msg(num_pong_bytes=1, ignored_bytes=1) { 322 const buflen = 32 323 let len = 0; 324 const buf = wasm_alloc(buflen) 325 326 if (!(len = lnsocket_make_ping_msg(buf, buflen, num_pong_bytes, ignored_bytes))) 327 throw new Error("couldn't make ping msg"); 328 329 const dat = wasm_mem(buf, len) 330 wasm_free(buf); 331 return dat 332 } 333 334 LNSocket.prototype.encrypt = function _lnsocket_encrypt(dat) { 335 const len = lnsocket_encrypt(this.ln, dat, dat.length) 336 if (len === 0) { 337 this.print_errors() 338 throw new Error("encrypt error") 339 } 340 const enc = wasm_mem(lnsocket_msgbuf(this.ln), len) 341 return enc 342 } 343 344 LNSocket.prototype.decrypt = function _lnsocket_decrypt(dat) { 345 const len = lnsocket_decrypt(this.ln, dat, dat.length) 346 if (len === 0) { 347 this.print_errors() 348 throw new Error("decrypt error") 349 } 350 return wasm_mem(lnsocket_msgbuf(this.ln), len) 351 } 352 353 LNSocket.prototype.write = function _lnsocket_write(dat) { 354 this.ws.send(this.encrypt(dat)) 355 } 356 357 LNSocket.prototype.read = async function _lnsocket_read() { 358 const size = await this.read_header() 359 const enc = await this.read_all(size+16) 360 return this.decrypt(enc) 361 } 362 363 LNSocket.prototype.make_default_initmsg = function _lnsocket_make_default_initmsg() { 364 const buflen = 1024 365 let len = 0; 366 const buf = module._malloc(buflen); 367 module.HEAPU8.set(Uint8Array, buf); 368 369 if (!(len = lnsocket_make_default_initmsg(buf, buflen))) 370 throw new Error("couldn't make initmsg"); 371 372 const dat = wasm_mem(buf, len) 373 module._free(buf); 374 return dat 375 } 376 377 LNSocket.prototype.perform_init = async function lnsocket_connect() { 378 await this.read() 379 const our_init = this.make_default_initmsg() 380 this.write(our_init) 381 } 382 383 LNSocket.prototype.ping_pong = async function lnsocket_ping_pong() { 384 const pingmsg = this.make_ping_msg() 385 this.write(pingmsg) 386 return await this.read() 387 } 388 389 LNSocket.prototype.disconnect = function lnsocket_disconnect() { 390 if (this.connected === true && this.ws) { 391 this.ws.close() 392 return true 393 } 394 return false 395 } 396 397 LNSocket.prototype.destroy = function _lnsocket_destroy() { 398 this.disconnect() 399 lnsocket_destroy(this.ln) 400 } 401 402 function handle_connect(ln, node_id, host) { 403 const ws = new SocketImpl(host) 404 return new Promise((resolve, reject) => { 405 const timeout = ln.opts.timeout || DEFAULT_TIMEOUT 406 const timer = setTimeout(reject, timeout); 407 408 ws.ondata((v) => { 409 ln.queue.push(v) 410 }); 411 412 ws.addEventListener('open', function(ev) { 413 ln.ws = ws 414 ln.connected = true 415 clearTimeout(timer) 416 resolve(ws) 417 }); 418 419 ws.addEventListener('close', function(ev) { 420 ln.connected = false 421 }); 422 }) 423 } 424 425 return LNSocket 426 } 427 428 Module.init = Module.lnsocket_init = lnsocket_init