forked from BHBNETWORK/lightning-client-js
-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.js
141 lines (115 loc) · 4.51 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
'use strict';
const path = require('path');
const net = require('net');
const fs = require('fs');
const readline = require('readline');
const debug = require('debug')('clightning-client');
const {EventEmitter} = require('events');
const LightningError = require('error/typed')({ type: 'lightning', message: 'lightning-client error' })
const methods = require('./methods');
const defaultRpcPath = path.join(require('os').homedir(), '.lightning')
, fStat = (...p) => fs.statSync(path.join(...p))
, fExists = (...p) => fs.existsSync(path.join(...p))
class LightningClient extends EventEmitter {
constructor(rpcPath=defaultRpcPath) {
if (!path.isAbsolute(rpcPath)) {
throw new Error('The rpcPath must be an absolute path');
}
if (!fExists(rpcPath) || !fStat(rpcPath).isSocket()) {
// network directory provided, use the lightning-rpc within in
if (fExists(rpcPath, 'lightning-rpc')) {
rpcPath = path.join(rpcPath, 'lightning-rpc');
}
// main data directory provided, default to using the bitcoin mainnet subdirectory
// to be removed in v0.2.0
else if (fExists(rpcPath, 'bitcoin', 'lightning-rpc')) {
console.error(`WARN: ${rpcPath}/lightning-rpc is missing, using the bitcoin mainnet subdirectory at ${rpcPath}/bitcoin instead.`)
console.error(`WARN: specifying the main lightning data directory is deprecated, please specify the network directory explicitly.\n`)
rpcPath = path.join(rpcPath, 'bitcoin', 'lightning-rpc')
}
}
debug(`Connecting to ${rpcPath}`);
super();
this.rpcPath = rpcPath;
this.reconnectWait = 0.5;
this.reconnectTimeout = null;
this.reqcount = 0;
const _self = this;
this.client = net.createConnection(rpcPath);
this.rl = readline.createInterface({ input: this.client })
this.clientConnectionPromise = new Promise(resolve => {
_self.client.on('connect', () => {
debug(`Lightning client connected`);
_self.reconnectWait = 1;
resolve();
});
_self.client.on('end', () => {
console.error('Lightning client connection closed, reconnecting');
_self.increaseWaitTime();
_self.reconnect();
});
_self.client.on('error', error => {
console.error(`Lightning client connection error`, error);
_self.emit('error', error);
_self.increaseWaitTime();
_self.reconnect();
});
});
this.rl.on('line', line => {
line = line.trim()
if (!line) return
const data = JSON.parse(line)
debug('#%d <-- %O', data.id, data.error || data.result)
_self.emit('res:' + data.id, data)
})
}
increaseWaitTime() {
if (this.reconnectWait >= 16) {
this.reconnectWait = 16;
} else {
this.reconnectWait *= 2;
}
}
reconnect() {
const _self = this;
if (this.reconnectTimeout) {
return;
}
this.reconnectTimeout = setTimeout(() => {
debug('Trying to reconnect...');
_self.client.connect(_self.rpcPath);
_self.reconnectTimeout = null;
}, this.reconnectWait * 1000);
}
call(method, args = []) {
const _self = this;
const callInt = ++this.reqcount;
const sendObj = {
jsonrpc: '2.0',
method,
params: args,
id: ''+callInt
};
debug('#%d --> %s %o', callInt, method, args)
// Wait for the client to connect
return this.clientConnectionPromise
.then(() => new Promise((resolve, reject) => {
// Wait for a response
this.once('res:' + callInt, res => res.error == null
? resolve(res.result)
: reject(LightningError(res.error))
);
// Send the command
_self.client.write(JSON.stringify(sendObj));
}));
}
}
const protify = s => s.replace(/-([a-z])/g, m => m[1].toUpperCase());
methods.forEach(k => {
LightningClient.prototype[protify(k)] = function (...args) {
return this.call(k, args);
};
});
module.exports = rpcPath => new LightningClient(rpcPath);
module.exports.LightningClient = LightningClient;
module.exports.LightningError = LightningError;