450 lines
30 KiB
JavaScript
450 lines
30 KiB
JavaScript
|
|
||
|
var Module = (() => {
|
||
|
var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;
|
||
|
if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename;
|
||
|
return (
|
||
|
function(Module) {
|
||
|
Module = Module || {};
|
||
|
|
||
|
var Module=typeof Module!="undefined"?Module:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});Module.getRandomValue=function(){const window_="object"===typeof window?window:this;const crypto_=typeof window_.crypto!=="undefined"?window_.crypto:window_.msCrypto;let randomBytesNode;let fn;if(!crypto_){randomBytesNode=require("crypto").randomBytes;fn=randomValuesNode}else{fn=randomValuesStandard}function randomValuesNode(){return randomBytesNode(1)[0]>>>0}function randomValuesStandard(){var buf=new Uint32Array(1);crypto_.getRandomValues(buf);return buf[0]>>>0}return fn}();var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;function logExceptionOnExit(e){if(e instanceof ExitStatus)return;let toLog=e;err("exiting due to exception: "+toLog)}var fs;var nodePath;var requireNodeFS;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}requireNodeFS=(()=>{if(!nodePath){fs=require("fs");nodePath=require("path")}});read_=function shell_read(filename,binary){requireNodeFS();filename=nodePath["normalize"](filename);return fs.readFileSync(filename,binary?undefined:"utf8")};readBinary=(filename=>{var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}return ret});readAsync=((filename,onload,onerror)=>{requireNodeFS();filename=nodePath["normalize"](filename);fs.readFile(filename,function(err,data){if(err)onerror(err);else onload(data.buffer)})});if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",function(reason){throw reason});quit_=((status,toThrow)=>{if(keepRuntimeAlive()){process["exitCode"]=status;throw toThrow}logExceptionOnExit(toThrow);process["exit"](status)});Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=(url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText});if(ENVIRONMENT_IS_WORKER){readBinary=(url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)})}readAsync=((url,onload,onerror)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=(()=>{if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()});xhr.onerror=onerror;xhr.send(null)})}setWindowTitle=(title=>document.title=title)}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime=Module["noExitRuntime"]||true;if(typeof WebAssembly!="object"){abort("no native wasm support detected")}var
|
||
|
|
||
|
|
||
|
return Module.ready
|
||
|
}
|
||
|
);
|
||
|
})();
|
||
|
if (typeof exports === 'object' && typeof module === 'object')
|
||
|
module.exports = Module;
|
||
|
else if (typeof define === 'function' && define['amd'])
|
||
|
define([], function() { return Module; });
|
||
|
else if (typeof exports === 'object')
|
||
|
exports["Module"] = Module;
|
||
|
|
||
|
async function lnsocket_init() {
|
||
|
const module = await Module()
|
||
|
|
||
|
function SocketImpl(host) {
|
||
|
if (!(this instanceof SocketImpl))
|
||
|
return new SocketImpl(host)
|
||
|
|
||
|
if (typeof WebSocket !== 'undefined') {
|
||
|
console.log("WebSocket", typeof WebSocket)
|
||
|
const ok = host.startsWith("ws://") || host.startsWith("wss://")
|
||
|
if (!ok)
|
||
|
throw new Error("host must start with ws:// or wss://")
|
||
|
const ws = new WebSocket(host)
|
||
|
ws.ondata = function(fn) {
|
||
|
ws.onmessage = (v) => {
|
||
|
const data = v.data.arrayBuffer()
|
||
|
fn(data)
|
||
|
}
|
||
|
}
|
||
|
return ws
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// we're in nodejs
|
||
|
//
|
||
|
const net = require('net')
|
||
|
let [hostname,port] = host.split(":")
|
||
|
port = +port || 9735
|
||
|
const socket = net.createConnection(port, hostname, () => {
|
||
|
socket.emit("open")
|
||
|
})
|
||
|
socket.addEventListener = socket.on.bind(socket)
|
||
|
|
||
|
if (socket.onmessage)
|
||
|
throw new Error("socket already has onmessage?")
|
||
|
|
||
|
socket.ondata = (fn) => {
|
||
|
socket.on('data', fn)
|
||
|
}
|
||
|
|
||
|
socket.close = () => {
|
||
|
socket.destroy()
|
||
|
}
|
||
|
|
||
|
if (socket.send)
|
||
|
throw new Error("socket already has send?")
|
||
|
|
||
|
socket.send = function socket_send(data) {
|
||
|
return new Promise((resolve, reject) => {
|
||
|
socket.write(data, resolve)
|
||
|
});
|
||
|
}
|
||
|
|
||
|
return socket
|
||
|
}
|
||
|
|
||
|
const ACT_ONE_SIZE = 50
|
||
|
const ACT_TWO_SIZE = 50
|
||
|
const ACT_THREE_SIZE = 66
|
||
|
const DEFAULT_TIMEOUT = 15000
|
||
|
|
||
|
const COMMANDO_REPLY_CONTINUES = 0x594b
|
||
|
const COMMANDO_REPLY_TERM = 0x594d
|
||
|
|
||
|
const lnsocket_create = module.cwrap("lnsocket_create", "number")
|
||
|
const lnsocket_destroy = module.cwrap("lnsocket_destroy", "number")
|
||
|
const lnsocket_encrypt = module.cwrap("lnsocket_encrypt", "number", ["int", "array", "int", "int"])
|
||
|
const lnsocket_decrypt = module.cwrap("lnsocket_decrypt", "number", ["int", "array", "int"])
|
||
|
const lnsocket_decrypt_header = module.cwrap("lnsocket_decrypt_header", "number", ["number", "array"])
|
||
|
const lnsocket_msgbuf = module.cwrap("lnsocket_msgbuf", "number", ["int"])
|
||
|
const lnsocket_act_one = module.cwrap("lnsocket_act_one", "number", ["number", "string"])
|
||
|
const lnsocket_act_two = module.cwrap("lnsocket_act_two", "number", ["number", "array"])
|
||
|
const lnsocket_print_errors = module.cwrap("lnsocket_print_errors", "int")
|
||
|
const lnsocket_genkey = module.cwrap("lnsocket_genkey", null, ["number"])
|
||
|
const lnsocket_setkey = module.cwrap("lnsocket_setkey", "number", ["number", "array"])
|
||
|
const lnsocket_make_default_initmsg = module.cwrap("lnsocket_make_default_initmsg", "int", ["int", "int"])
|
||
|
const lnsocket_make_ping_msg = module.cwrap("lnsocket_make_ping_msg", "int", ["int", "int", "int", "int"])
|
||
|
const commando_make_rpc_msg = module.cwrap("commando_make_rpc_msg", "int", ["string", "string", "string", "number", "int", "int"])
|
||
|
|
||
|
function concat_u8_arrays(arrays) {
|
||
|
// sum of individual array lengths
|
||
|
let totalLength = arrays.reduce((acc, value) =>
|
||
|
acc + (value.length || value.byteLength)
|
||
|
, 0);
|
||
|
|
||
|
if (!arrays.length) return null;
|
||
|
|
||
|
let result = new Uint8Array(totalLength);
|
||
|
|
||
|
let length = 0;
|
||
|
for (let array of arrays) {
|
||
|
if (array instanceof ArrayBuffer)
|
||
|
result.set(new Uint8Array(array), length);
|
||
|
else
|
||
|
result.set(array, length);
|
||
|
|
||
|
length += (array.length || array.byteLength);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
function parse_msgtype(buf) {
|
||
|
return buf[0] << 8 | buf[1]
|
||
|
}
|
||
|
|
||
|
function wasm_free(buf) {
|
||
|
module._free(buf);
|
||
|
}
|
||
|
|
||
|
function char_to_hex(cstr) {
|
||
|
const c = cstr.charCodeAt(0)
|
||
|
// c >= 0 && c <= 9
|
||
|
if (c >= 48 && c <= 57) {
|
||
|
return c - 48;
|
||
|
}
|
||
|
// c >= a && c <= f
|
||
|
if (c >= 97 && c <= 102) {
|
||
|
return c - 97 + 10;
|
||
|
}
|
||
|
// c >= A && c <= F
|
||
|
if (c >= 65 && c <= 70) {
|
||
|
return c - 65 + 10;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
function hex_decode(str, buflen)
|
||
|
{
|
||
|
let bufsize = buflen || 33
|
||
|
let c1, c2
|
||
|
let i = 0
|
||
|
let j = 0
|
||
|
let buf = new Uint8Array(bufsize)
|
||
|
let slen = str.length
|
||
|
while (slen > 1) {
|
||
|
if (-1==(c1 = char_to_hex(str[j])) || -1==(c2 = char_to_hex(str[j+1])))
|
||
|
return null;
|
||
|
if (!bufsize)
|
||
|
return null;
|
||
|
j += 2
|
||
|
slen -= 2
|
||
|
buf[i++] = (c1 << 4) | c2
|
||
|
bufsize--;
|
||
|
}
|
||
|
|
||
|
return buf
|
||
|
}
|
||
|
|
||
|
function wasm_alloc(len) {
|
||
|
const buf = module._malloc(len);
|
||
|
module.HEAPU8.set(Uint8Array, buf);
|
||
|
return buf
|
||
|
}
|
||
|
|
||
|
function wasm_mem(ptr, size) {
|
||
|
return new Uint8Array(module.HEAPU8.buffer, ptr, size);
|
||
|
}
|
||
|
|
||
|
function LNSocket(opts) {
|
||
|
if (!(this instanceof LNSocket))
|
||
|
return new LNSocket(opts)
|
||
|
|
||
|
this.opts = opts || {
|
||
|
timeout: DEFAULT_TIMEOUT
|
||
|
}
|
||
|
this.queue = []
|
||
|
this.ln = lnsocket_create()
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.queue_recv = function() {
|
||
|
let self = this
|
||
|
return new Promise((resolve, reject) => {
|
||
|
const checker = setInterval(() => {
|
||
|
const val = self.queue.shift()
|
||
|
if (val) {
|
||
|
clearInterval(checker)
|
||
|
resolve(val)
|
||
|
} else if (!self.connected) {
|
||
|
clearInterval(checker)
|
||
|
reject()
|
||
|
}
|
||
|
}, 5);
|
||
|
})
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.print_errors = function _lnsocket_print_errors() {
|
||
|
lnsocket_print_errors(this.ln)
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.genkey = function _lnsocket_genkey() {
|
||
|
lnsocket_genkey(this.ln)
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.setkeyraw = function lnsocket_setkeyraw(rawkey) {
|
||
|
return lnsocket_setkey(this.ln, rawkey)
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.setkey = function _lnsocket_setkey(key) {
|
||
|
const rawkey = hex_decode(key)
|
||
|
return this.setkeyraw(rawkey)
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.act_one_data = function _lnsocket_act_one(node_id) {
|
||
|
const act_one_ptr = lnsocket_act_one(this.ln, node_id)
|
||
|
if (act_one_ptr === 0)
|
||
|
return null
|
||
|
return wasm_mem(act_one_ptr, ACT_ONE_SIZE)
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.act_two = function _lnsocket_act_two(act2) {
|
||
|
const act_three_ptr = lnsocket_act_two(this.ln, new Uint8Array(act2))
|
||
|
if (act_three_ptr === 0) {
|
||
|
this.print_errors()
|
||
|
return null
|
||
|
}
|
||
|
return wasm_mem(act_three_ptr, ACT_THREE_SIZE)
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.connect = async function lnsocket_connect(node_id, host) {
|
||
|
await handle_connect(this, node_id, host)
|
||
|
|
||
|
const act1 = this.act_one_data(node_id)
|
||
|
this.ws.send(act1)
|
||
|
const act2 = await this.read_all(ACT_TWO_SIZE)
|
||
|
if (act2.length != ACT_TWO_SIZE) {
|
||
|
throw new Error(`expected act2 to be ${ACT_TWO_SIZE} long, got ${act2.length}`)
|
||
|
}
|
||
|
const act3 = this.act_two(act2)
|
||
|
this.ws.send(act3)
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.connect_and_init = async function _connect_and_init(node_id, host) {
|
||
|
await this.connect(node_id, host)
|
||
|
await this.perform_init()
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.read_all = async function read_all(n) {
|
||
|
let count = 0
|
||
|
let chunks = []
|
||
|
if (!this.connected)
|
||
|
throw new Error("read_all: not connected")
|
||
|
while (true) {
|
||
|
let res = await this.queue_recv()
|
||
|
|
||
|
const remaining = n - count
|
||
|
|
||
|
if (res.byteLength > remaining) {
|
||
|
chunks.push(res.slice(0, remaining))
|
||
|
this.queue.unshift(res.slice(remaining))
|
||
|
break
|
||
|
} else if (res.byteLength === remaining) {
|
||
|
chunks.push(res)
|
||
|
break
|
||
|
}
|
||
|
|
||
|
chunks.push(res)
|
||
|
count += res.byteLength
|
||
|
}
|
||
|
|
||
|
return concat_u8_arrays(chunks)
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.read_header = async function read_header() {
|
||
|
const header = await this.read_all(18)
|
||
|
if (header.length != 18)
|
||
|
throw new Error("Failed to read header")
|
||
|
return lnsocket_decrypt_header(this.ln, header)
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.rpc = async function lnsocket_rpc(opts) {
|
||
|
const msg = this.make_commando_msg(opts)
|
||
|
this.write(msg)
|
||
|
const res = await this.read_all_rpc()
|
||
|
return JSON.parse(res)
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.recv = async function lnsocket_recv() {
|
||
|
const msg = await this.read()
|
||
|
const msgtype = parse_msgtype(msg.slice(0,2))
|
||
|
const res = [msgtype, msg.slice(2)]
|
||
|
return res
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.read_all_rpc = async function read_all_rpc() {
|
||
|
let chunks = []
|
||
|
while (true) {
|
||
|
const [typ, msg] = await this.recv()
|
||
|
switch (typ) {
|
||
|
case COMMANDO_REPLY_TERM:
|
||
|
chunks.push(msg.slice(8))
|
||
|
return new TextDecoder().decode(concat_u8_arrays(chunks));
|
||
|
case COMMANDO_REPLY_CONTINUES:
|
||
|
chunks.push(msg.slice(8))
|
||
|
break
|
||
|
default:
|
||
|
console.log("got unknown type", typ)
|
||
|
continue
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.make_commando_msg = function _lnsocket_make_commando_msg(opts) {
|
||
|
const buflen = 4096
|
||
|
let len = 0;
|
||
|
const buf = wasm_alloc(buflen);
|
||
|
|
||
|
const params = JSON.stringify(opts.params||{})
|
||
|
if (!(len = commando_make_rpc_msg(opts.method, params, opts.rune,
|
||
|
0, buf, buflen))) {
|
||
|
throw new Error("couldn't make commando msg");
|
||
|
}
|
||
|
|
||
|
const dat = wasm_mem(buf, len)
|
||
|
wasm_free(buf);
|
||
|
return dat
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.make_ping_msg = function _lnsocket_make_ping_msg(num_pong_bytes=1, ignored_bytes=1) {
|
||
|
const buflen = 32
|
||
|
let len = 0;
|
||
|
const buf = wasm_alloc(buflen)
|
||
|
|
||
|
if (!(len = lnsocket_make_ping_msg(buf, buflen, num_pong_bytes, ignored_bytes)))
|
||
|
throw new Error("couldn't make ping msg");
|
||
|
|
||
|
const dat = wasm_mem(buf, len)
|
||
|
wasm_free(buf);
|
||
|
return dat
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.encrypt = function _lnsocket_encrypt(dat) {
|
||
|
const len = lnsocket_encrypt(this.ln, dat, dat.length)
|
||
|
if (len === 0) {
|
||
|
this.print_errors()
|
||
|
throw new Error("encrypt error")
|
||
|
}
|
||
|
const enc = wasm_mem(lnsocket_msgbuf(this.ln), len)
|
||
|
return enc
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.decrypt = function _lnsocket_decrypt(dat) {
|
||
|
const len = lnsocket_decrypt(this.ln, dat, dat.length)
|
||
|
if (len === 0) {
|
||
|
this.print_errors()
|
||
|
throw new Error("decrypt error")
|
||
|
}
|
||
|
return wasm_mem(lnsocket_msgbuf(this.ln), len)
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.write = function _lnsocket_write(dat) {
|
||
|
this.ws.send(this.encrypt(dat))
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.read = async function _lnsocket_read() {
|
||
|
const size = await this.read_header()
|
||
|
const enc = await this.read_all(size+16)
|
||
|
return this.decrypt(enc)
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.make_default_initmsg = function _lnsocket_make_default_initmsg() {
|
||
|
const buflen = 1024
|
||
|
let len = 0;
|
||
|
const buf = module._malloc(buflen);
|
||
|
module.HEAPU8.set(Uint8Array, buf);
|
||
|
|
||
|
if (!(len = lnsocket_make_default_initmsg(buf, buflen)))
|
||
|
throw new Error("couldn't make initmsg");
|
||
|
|
||
|
const dat = wasm_mem(buf, len)
|
||
|
module._free(buf);
|
||
|
return dat
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.perform_init = async function lnsocket_connect() {
|
||
|
await this.read()
|
||
|
const our_init = this.make_default_initmsg()
|
||
|
this.write(our_init)
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.ping_pong = async function lnsocket_ping_pong() {
|
||
|
const pingmsg = this.make_ping_msg()
|
||
|
this.write(pingmsg)
|
||
|
return await this.read()
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.disconnect = function lnsocket_disconnect() {
|
||
|
if (this.connected === true && this.ws) {
|
||
|
this.ws.close()
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
LNSocket.prototype.destroy = function _lnsocket_destroy() {
|
||
|
this.disconnect()
|
||
|
lnsocket_destroy(this.ln)
|
||
|
}
|
||
|
|
||
|
function handle_connect(ln, node_id, host) {
|
||
|
const ws = new SocketImpl(host)
|
||
|
return new Promise((resolve, reject) => {
|
||
|
const timeout = ln.opts.timeout || DEFAULT_TIMEOUT
|
||
|
const timer = setTimeout(reject, timeout);
|
||
|
|
||
|
ws.ondata((v) => {
|
||
|
ln.queue.push(v)
|
||
|
});
|
||
|
|
||
|
ws.addEventListener('open', function(ev) {
|
||
|
ln.ws = ws
|
||
|
ln.connected = true
|
||
|
clearTimeout(timer)
|
||
|
resolve(ws)
|
||
|
});
|
||
|
|
||
|
ws.addEventListener('close', function(ev) {
|
||
|
ln.connected = false
|
||
|
});
|
||
|
})
|
||
|
}
|
||
|
|
||
|
return LNSocket
|
||
|
}
|
||
|
|
||
|
Module.init = Module.lnsocket_init = lnsocket_init
|