paid-services 3.5.0 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -1
- package/capacity/accept_capacity_change.js +2 -0
- package/capacity/ask_for_change_details.js +9 -10
- package/capacity/ask_for_decrease.js +167 -49
- package/capacity/fee_for_replacement.js +3 -7
- package/capacity/get_capacity_replacement.js +56 -8
- package/capacity/initiate_capacity_change.js +0 -1
- package/capacity/propose_capacity_change.js +207 -92
- package/capacity/propose_channel_open.js +54 -0
- package/index.js +4 -0
- package/package.json +9 -9
- package/services/response_for_relay.js +0 -1
- package/test/integration/test_accept_capacity_change.js +3 -4
- package/test/integration/test_change_channel_capacity.js +4 -1
- package/test/integration/test_decrease_channel_capacity.js +7 -2
- package/test/integration/test_increase_channel_capacity.js +0 -1
- package/test/integration/test_split_channel_capacity.js +241 -0
- package/trades/accept_trade.js +12 -3
- package/trades/create_trade.js +82 -50
- package/trades/decode_anchored_trade.js +56 -0
- package/trades/encode_anchored_trade.js +35 -0
- package/trades/finalize_trade_secret.js +7 -0
- package/trades/find_trade.js +28 -3
- package/trades/index.js +3 -1
- package/trades/manage_trade.js +15 -4
- package/trades/manage_trades.js +209 -2
- package/trades/request_trade_by_id.js +7 -1
- package/trades/service_anchored_trades.js +206 -0
- package/trades/service_open_trade.js +172 -0
- package/trades/service_trade_requests.js +106 -35
- package/trades/trade_from_invoice.js +54 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
# Versions
|
|
2
2
|
|
|
3
|
-
## Version 3.
|
|
3
|
+
## Version 3.8.0
|
|
4
|
+
|
|
5
|
+
- `encodeTrade`: Add method to encode a trade-secret
|
|
6
|
+
- `serviceAnchoredTrades`: Add method to serve open trade-secrets
|
|
7
|
+
|
|
8
|
+
## Version 3.7.0
|
|
9
|
+
|
|
10
|
+
- `changeChannelCapacity`: Add support for decreasing funds into a new channel
|
|
11
|
+
|
|
12
|
+
## Version 3.6.0
|
|
13
|
+
|
|
14
|
+
- `manageTrades`: Add support for persistent open trades
|
|
15
|
+
- `manageTrades`: Add expiration dates to open trades
|
|
16
|
+
- `manageTrades`: Support longer-lived open trade scenarios
|
|
17
|
+
- `manageTrades`: Show final encoded trade when requesting open trade
|
|
18
|
+
- `manageTrades`: Check for RPC signer support before trading
|
|
19
|
+
|
|
20
|
+
## Version 3.5.1
|
|
4
21
|
|
|
5
22
|
- `changeChannelCapacity`: Allow changing private/public status of channel
|
|
6
23
|
|
|
@@ -212,6 +212,8 @@ module.exports = ({channel, from, id, increase, lnd, logger}, cbk) => {
|
|
|
212
212
|
return failure([503, 'FailedToParseSignChangeRequest']);
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
+
logger.info({closing_and_reopening_channel: true});
|
|
216
|
+
|
|
215
217
|
// Trigger the replacement flow and sign the unsigned tx
|
|
216
218
|
return getReplacementSignature({
|
|
217
219
|
channel,
|
|
@@ -48,8 +48,9 @@ const weightBuffer = 150;
|
|
|
48
48
|
cltv_delta: <Locktime Delta Number>
|
|
49
49
|
coop_close_address: <Cooperative Close Address String>
|
|
50
50
|
decrease: [{
|
|
51
|
-
address: <Decrease Address String>
|
|
52
|
-
|
|
51
|
+
[address]: <Decrease Address String>
|
|
52
|
+
[node]: <Create Channel with Node With Public Key Hex String>
|
|
53
|
+
[output]: <Output Script Hex String>
|
|
53
54
|
tokens: <Decrease Tokens Number>
|
|
54
55
|
}]
|
|
55
56
|
estimated_capacity: <Estimated Tokens For New Channel Capacity Number>
|
|
@@ -172,6 +173,7 @@ module.exports = ({ask, id, lnd}, cbk) => {
|
|
|
172
173
|
const disabled = [
|
|
173
174
|
!!channel.cooperative_close_address,
|
|
174
175
|
!channel.is_active,
|
|
176
|
+
!!channel.pending_payments.length,
|
|
175
177
|
];
|
|
176
178
|
|
|
177
179
|
return {
|
|
@@ -344,7 +346,7 @@ module.exports = ({ask, id, lnd}, cbk) => {
|
|
|
344
346
|
return cbk(err);
|
|
345
347
|
}
|
|
346
348
|
|
|
347
|
-
if (decreases.find(n => n.address === res.address)) {
|
|
349
|
+
if (!!res.address && decreases.find(n => n.address === res.address)) {
|
|
348
350
|
return cbk([400, 'ExpectedUniqueAddressForSpend']);
|
|
349
351
|
}
|
|
350
352
|
|
|
@@ -356,11 +358,8 @@ module.exports = ({ask, id, lnd}, cbk) => {
|
|
|
356
358
|
return cbk(err);
|
|
357
359
|
}
|
|
358
360
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
.map(({address, output, tokens}) => ({address, output, tokens}));
|
|
362
|
-
|
|
363
|
-
return cbk(null, final);
|
|
361
|
+
// Only include decreases that spend funds
|
|
362
|
+
return cbk(null, decreases.filter(n => !!n.tokens));
|
|
364
363
|
}
|
|
365
364
|
);
|
|
366
365
|
}],
|
|
@@ -413,10 +412,10 @@ module.exports = ({ask, id, lnd}, cbk) => {
|
|
|
413
412
|
return type;
|
|
414
413
|
}
|
|
415
414
|
|
|
416
|
-
return `${type} (
|
|
415
|
+
return `${type} (keep current status)`;
|
|
417
416
|
}),
|
|
418
417
|
default: channel.is_private ? 'Private' : 'Public',
|
|
419
|
-
message: '
|
|
418
|
+
message: 'Replacement channel type?',
|
|
420
419
|
name: 'type',
|
|
421
420
|
type: 'list',
|
|
422
421
|
},
|
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
const {address} = require('bitcoinjs-lib');
|
|
2
2
|
const asyncAuto = require('async/auto');
|
|
3
3
|
const {createChainAddress} = require('ln-service');
|
|
4
|
+
const {getIdentity} = require('ln-service');
|
|
5
|
+
const {acceptsChannelOpen} = require('ln-sync');
|
|
4
6
|
const {getNetwork} = require('ln-sync');
|
|
5
7
|
const {networks} = require('bitcoinjs-lib');
|
|
6
8
|
const {returnResult} = require('asyncjs-util');
|
|
7
9
|
|
|
10
|
+
const proposeChannelOpen = require('./propose_channel_open');
|
|
11
|
+
|
|
12
|
+
const askForChanSize = max => `Capacity of the new channel? (max: ${max})`;
|
|
13
|
+
const askForSendSize = max => `Amount you want to send out? (max: ${max})`;
|
|
8
14
|
const bufferAsHex = buffer => buffer.toString('hex');
|
|
9
15
|
const dust = 550;
|
|
16
|
+
const {isInteger} = Number;
|
|
10
17
|
const isNumber = n => !isNaN(n);
|
|
18
|
+
const isPublicKey = n => !!n && /^0[2-3][0-9A-F]{64}$/i.test(n);
|
|
19
|
+
const minChannelCapacity = 20000;
|
|
20
|
+
const minSpendTokens = 0;
|
|
21
|
+
const openChannelAction = 'open_channel';
|
|
22
|
+
const spendToExternalAddressAction = 'external_spend_funds';
|
|
23
|
+
const spendToInternalAddressAction = 'internal_spend_funds';
|
|
11
24
|
const {toOutputScript} = address;
|
|
25
|
+
const tokensAsBigUnit = tokens => (tokens / 1e8).toFixed(8);
|
|
12
26
|
|
|
13
27
|
/** Ask for details about a decrease
|
|
14
28
|
|
|
@@ -22,6 +36,7 @@ const {toOutputScript} = address;
|
|
|
22
36
|
{
|
|
23
37
|
[address]: <Send Funds to Address String>
|
|
24
38
|
is_final: <Decrease is Final Decrease Bool>
|
|
39
|
+
[node]: <Create Channel with Node With Public Key Hex String>
|
|
25
40
|
[output]: <Output Script Hex String>
|
|
26
41
|
tokens: <Withdraw Tokens Number>
|
|
27
42
|
}
|
|
@@ -46,66 +61,149 @@ module.exports = ({ask, lnd, max}, cbk) => {
|
|
|
46
61
|
return cbk();
|
|
47
62
|
},
|
|
48
63
|
|
|
49
|
-
//
|
|
50
|
-
|
|
64
|
+
// Select the type of decrease
|
|
65
|
+
askForDecreaseType: ['validate', ({}, cbk) => {
|
|
51
66
|
return ask({
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
67
|
+
choices: [
|
|
68
|
+
{
|
|
69
|
+
name: `Send decreased funds to the internal chain wallet`,
|
|
70
|
+
value: spendToInternalAddressAction,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: `Send decreased funds to an external chain address`,
|
|
74
|
+
value: spendToExternalAddressAction,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
disabled: max < minChannelCapacity,
|
|
78
|
+
name: `Spend decreased funds into new channel with another peer`,
|
|
79
|
+
value: openChannelAction,
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
message: 'How do you want to change the channel capacity?',
|
|
83
|
+
name: 'decrease',
|
|
84
|
+
type: 'list',
|
|
85
|
+
},
|
|
86
|
+
({decrease}) => cbk(null, decrease));
|
|
87
|
+
}],
|
|
88
|
+
|
|
89
|
+
// Get the node identity key to make sure a channel open isn't with self
|
|
90
|
+
getIdentity: ['validate', ({}, cbk) => getIdentity({lnd}, cbk)],
|
|
91
|
+
|
|
92
|
+
// Get network name to validate addresses against
|
|
93
|
+
getNetwork: ['validate', ({}, cbk) => getNetwork({lnd}, cbk)],
|
|
94
|
+
|
|
95
|
+
// Ask for the public key of the node to trade with
|
|
96
|
+
askForNodeId: [
|
|
97
|
+
'askForDecreaseType',
|
|
98
|
+
'getIdentity',
|
|
99
|
+
({askForDecreaseType, getIdentity}, cbk) =>
|
|
100
|
+
{
|
|
101
|
+
// Exit early when not opening a new channel
|
|
102
|
+
if (askForDecreaseType !== openChannelAction) {
|
|
103
|
+
return cbk();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return ask({
|
|
107
|
+
name: 'key',
|
|
108
|
+
message: 'Public key of node to open channel with?',
|
|
55
109
|
type: 'input',
|
|
56
110
|
validate: input => {
|
|
57
|
-
if (!
|
|
111
|
+
if (!input) {
|
|
58
112
|
return false;
|
|
59
113
|
}
|
|
60
114
|
|
|
61
|
-
if (
|
|
62
|
-
return
|
|
115
|
+
if (!isPublicKey(input)) {
|
|
116
|
+
return 'Expected public key of node to open channel with';
|
|
63
117
|
}
|
|
64
118
|
|
|
65
|
-
if (
|
|
66
|
-
return
|
|
119
|
+
if (input === getIdentity.public_key) {
|
|
120
|
+
return 'Expected public key of other node';
|
|
67
121
|
}
|
|
68
122
|
|
|
69
123
|
return true;
|
|
70
124
|
},
|
|
71
125
|
},
|
|
72
|
-
({
|
|
126
|
+
({key}) => cbk(null, key));
|
|
73
127
|
}],
|
|
74
128
|
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
askForExternal: ['askForAmount', ({askForAmount}, cbk) => {
|
|
80
|
-
if (!Number(askForAmount)) {
|
|
129
|
+
// Create a decrease address to withdraw funds out to
|
|
130
|
+
createAddress: ['askForDecreaseType', ({askForDecreaseType}, cbk) => {
|
|
131
|
+
// Exit early when there is no need to create an internal address
|
|
132
|
+
if (askForDecreaseType !== spendToInternalAddressAction) {
|
|
81
133
|
return cbk();
|
|
82
134
|
}
|
|
83
135
|
|
|
136
|
+
return createChainAddress({lnd}, cbk);
|
|
137
|
+
}],
|
|
138
|
+
|
|
139
|
+
// Ask for the amount to decrease
|
|
140
|
+
askForAmount: ['askForNodeId', ({askForNodeId}, cbk) => {
|
|
84
141
|
return ask({
|
|
85
|
-
default:
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
142
|
+
default: !askForNodeId ? minSpendTokens : minChannelCapacity,
|
|
143
|
+
message: !askForNodeId ? askForSendSize(max) : askForChanSize(max),
|
|
144
|
+
name: 'amount',
|
|
145
|
+
validate: input => {
|
|
146
|
+
// A numeric input is required
|
|
147
|
+
if (!isNumber(input) || !isInteger(Number(input))) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Zero value is acceptable for a decrease when not opening channel
|
|
152
|
+
if (!askForNodeId && !Number(input)) {
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// On-chain values must always be above dust
|
|
157
|
+
if (!!Number(input) && Number(input) < dust) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Channel opens must always be above the minimum channel size
|
|
162
|
+
if (!!askForNodeId && Number(input) < minChannelCapacity) {
|
|
163
|
+
return `The minimum channel capacity is ${minChannelCapacity}`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// A decrease cannot spend more funds than are available
|
|
167
|
+
if (!!Number(input) && Number(input) > max) {
|
|
168
|
+
return `The maximum possible to decrease is ${max}`;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return true;
|
|
172
|
+
},
|
|
89
173
|
},
|
|
90
|
-
({
|
|
174
|
+
({amount}) => cbk(null, Number(amount)));
|
|
91
175
|
}],
|
|
92
176
|
|
|
93
177
|
// Ask for an external address
|
|
94
178
|
askForAddress: [
|
|
95
179
|
'askForAmount',
|
|
96
|
-
'
|
|
97
|
-
|
|
180
|
+
'askForDecreaseType',
|
|
181
|
+
'getNetwork',
|
|
182
|
+
({askForAmount, askForDecreaseType, getNetwork}, cbk) =>
|
|
98
183
|
{
|
|
99
|
-
// Exit early when the withdraw address is
|
|
100
|
-
if (
|
|
184
|
+
// Exit early when the withdraw address is not external
|
|
185
|
+
if (askForDecreaseType !== spendToExternalAddressAction) {
|
|
101
186
|
return cbk();
|
|
102
187
|
}
|
|
103
188
|
|
|
104
189
|
return ask({
|
|
105
190
|
name: 'address',
|
|
106
|
-
message: `Address to spend ${askForAmount} to?`,
|
|
191
|
+
message: `Address to spend ${tokensAsBigUnit(askForAmount)} to?`,
|
|
107
192
|
type: 'input',
|
|
108
|
-
validate: input =>
|
|
193
|
+
validate: input => {
|
|
194
|
+
if (!input) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Make sure that the entered address can be derived to an output
|
|
199
|
+
try {
|
|
200
|
+
toOutputScript(input, networks[getNetwork.bitcoinjs]);
|
|
201
|
+
} catch (err) {
|
|
202
|
+
return 'Failed to parse address. Try a standard one?';
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return true;
|
|
206
|
+
},
|
|
109
207
|
},
|
|
110
208
|
({address}) => cbk(null, {address}));
|
|
111
209
|
}],
|
|
@@ -113,31 +211,46 @@ module.exports = ({ask, lnd, max}, cbk) => {
|
|
|
113
211
|
// Ask if this is the last output
|
|
114
212
|
askForAddition: [
|
|
115
213
|
'askForAddress',
|
|
116
|
-
'
|
|
117
|
-
|
|
214
|
+
'askForAmount',
|
|
215
|
+
'askForDecreaseType',
|
|
216
|
+
({askForAmount, askForDecreaseType}, cbk) =>
|
|
118
217
|
{
|
|
119
|
-
// Exit early
|
|
120
|
-
if (
|
|
218
|
+
// Exit early and only ask for addition when decrease is a "spend" type
|
|
219
|
+
if (askForDecreaseType === spendToInternalAddressAction) {
|
|
220
|
+
return cbk();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Exit early when there are no more funds to spend
|
|
224
|
+
if (max - askForAmount < dust) {
|
|
121
225
|
return cbk();
|
|
122
226
|
}
|
|
123
227
|
|
|
124
228
|
return ask({
|
|
125
229
|
default: false,
|
|
126
230
|
name: 'add',
|
|
127
|
-
message: '
|
|
231
|
+
message: 'Spend additional funds from the channel capacity?',
|
|
128
232
|
type: 'confirm',
|
|
129
233
|
},
|
|
130
234
|
({add}) => cbk(null, add));
|
|
131
235
|
}],
|
|
132
236
|
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
237
|
+
// Propose a channel open to confirm the peer will accept the channel
|
|
238
|
+
checkChannelAcceptance: [
|
|
239
|
+
'askForAmount',
|
|
240
|
+
'askForNodeId',
|
|
241
|
+
({askForAmount, askForNodeId}, cbk) =>
|
|
242
|
+
{
|
|
243
|
+
// Exit early if pubkey was not entered
|
|
244
|
+
if (!askForNodeId) {
|
|
137
245
|
return cbk();
|
|
138
246
|
}
|
|
139
247
|
|
|
140
|
-
return
|
|
248
|
+
return proposeChannelOpen({
|
|
249
|
+
lnd,
|
|
250
|
+
capacity: askForAmount,
|
|
251
|
+
id: askForNodeId,
|
|
252
|
+
},
|
|
253
|
+
cbk);
|
|
141
254
|
}],
|
|
142
255
|
|
|
143
256
|
// Final decrease output details
|
|
@@ -145,38 +258,43 @@ module.exports = ({ask, lnd, max}, cbk) => {
|
|
|
145
258
|
'askForAddition',
|
|
146
259
|
'askForAddress',
|
|
147
260
|
'askForAmount',
|
|
261
|
+
'checkChannelAcceptance',
|
|
148
262
|
'createAddress',
|
|
149
263
|
'getNetwork',
|
|
150
264
|
({
|
|
151
265
|
askForAddition,
|
|
152
266
|
askForAddress,
|
|
153
267
|
askForAmount,
|
|
268
|
+
askForNodeId,
|
|
269
|
+
checkChannelAcceptance,
|
|
154
270
|
createAddress,
|
|
155
271
|
getNetwork,
|
|
156
272
|
},
|
|
157
273
|
cbk) =>
|
|
158
274
|
{
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (!address) {
|
|
163
|
-
return cbk(null, {is_final: true, tokens: Number(askForAmount)});
|
|
275
|
+
// Make sure that a channel proposal didn't fail
|
|
276
|
+
if (!!checkChannelAcceptance && !checkChannelAcceptance.is_accepted) {
|
|
277
|
+
return cbk([503, 'RemoteNodeRejectedNewChannelProposal']);
|
|
164
278
|
}
|
|
165
279
|
|
|
166
|
-
//
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
280
|
+
// Exit early when decreasing to a new channel
|
|
281
|
+
if (!!askForNodeId) {
|
|
282
|
+
return cbk(null, {
|
|
283
|
+
is_final: !askForAddition,
|
|
284
|
+
node: askForNodeId,
|
|
285
|
+
tokens: askForAmount,
|
|
286
|
+
});
|
|
171
287
|
}
|
|
172
288
|
|
|
289
|
+
const {address} = (createAddress || askForAddress);
|
|
290
|
+
|
|
173
291
|
const output = toOutputScript(address, networks[getNetwork.bitcoinjs]);
|
|
174
292
|
|
|
175
293
|
return cbk(null, {
|
|
176
294
|
address,
|
|
177
295
|
is_final: !askForAddition,
|
|
178
296
|
output: bufferAsHex(output),
|
|
179
|
-
tokens:
|
|
297
|
+
tokens: askForAmount,
|
|
180
298
|
});
|
|
181
299
|
}],
|
|
182
300
|
},
|
|
@@ -13,7 +13,7 @@ const dummySignature = Buffer.alloc(74);
|
|
|
13
13
|
const dummyTokens = 0;
|
|
14
14
|
const dummyVout = 0;
|
|
15
15
|
const hexAsBuffer = hex => Buffer.from(hex, 'hex');
|
|
16
|
-
const increaseBuffer =
|
|
16
|
+
const increaseBuffer = 250;
|
|
17
17
|
const {max} = Math;
|
|
18
18
|
const weightAsVBytes = weight => weight / 4;
|
|
19
19
|
|
|
@@ -24,7 +24,7 @@ const weightAsVBytes = weight => weight / 4;
|
|
|
24
24
|
commit_transaction_fee: <Commit Transaction Fee Tokens Number>
|
|
25
25
|
commit_transaction_weight: <Commit Transaction Weight Units Number>
|
|
26
26
|
decrease: [{
|
|
27
|
-
output: <Output Script Hex String>
|
|
27
|
+
[output]: <Output Script Hex String>
|
|
28
28
|
tokens: <Spend Value to Chain Address Tokens Number>
|
|
29
29
|
}]
|
|
30
30
|
[increase]: <Add Funds Tokens Number>
|
|
@@ -51,16 +51,12 @@ module.exports = args => {
|
|
|
51
51
|
|
|
52
52
|
// Add the decreases as outputs
|
|
53
53
|
args.decrease.forEach(({output, tokens}) => {
|
|
54
|
-
return tx.addOutput(hexAsBuffer(output), tokens);
|
|
54
|
+
return tx.addOutput(hexAsBuffer(output || dummyP2wsh), tokens);
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
// Add an output to represent the new channel output
|
|
58
58
|
tx.addOutput(dummyP2wsh, args.capacity);
|
|
59
59
|
|
|
60
|
-
args.decrease.forEach(({output}) => {
|
|
61
|
-
return tx.addOutput(hexAsBuffer(output), dummyTokens);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
60
|
// Add an input to represent the old channel input
|
|
65
61
|
tx.addInput(dummyHash, dummyVout);
|
|
66
62
|
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
const {address} = require('bitcoinjs-lib');
|
|
1
2
|
const asyncAuto = require('async/auto');
|
|
3
|
+
const asyncEach = require('async/each');
|
|
2
4
|
const {closeChannel} = require('ln-service');
|
|
5
|
+
const {connectPeer} = require('ln-sync');
|
|
3
6
|
const {decodeChanId} = require('bolt07');
|
|
4
7
|
const {getChainFeeRate} = require('ln-service');
|
|
5
8
|
const {getChainTransactions} = require('ln-service');
|
|
6
9
|
const {getHeight} = require('ln-service');
|
|
7
10
|
const {getPendingChannels} = require('ln-service');
|
|
11
|
+
const {networks} = require('bitcoinjs-lib');
|
|
8
12
|
const {openChannels} = require('ln-service');
|
|
9
13
|
const {returnResult} = require('asyncjs-util');
|
|
10
14
|
const {Transaction} = require('bitcoinjs-lib');
|
|
@@ -26,6 +30,7 @@ const notFoundIndex = -1;
|
|
|
26
30
|
const positive = n => Math.max(1, n);
|
|
27
31
|
const slowTarget = 1000;
|
|
28
32
|
const sumOf = arr => arr.reduce((sum, n) => sum + n, 0);
|
|
33
|
+
const {toOutputScript} = address;
|
|
29
34
|
const txIdAsHash = id => Buffer.from(id, 'hex').reverse();
|
|
30
35
|
|
|
31
36
|
/** Close a channel and get a capacity replacement transaction
|
|
@@ -36,7 +41,8 @@ const txIdAsHash = id => Buffer.from(id, 'hex').reverse();
|
|
|
36
41
|
bitcoinjs_network: <BitcoinJs Network Name String>
|
|
37
42
|
decrease: [{
|
|
38
43
|
address: <Spend to Chain Address String>
|
|
39
|
-
|
|
44
|
+
[node]: <Node to Open Channel to Public Key Hex String>
|
|
45
|
+
[output]: <Spend to Output Script Hex String>
|
|
40
46
|
tokens: <Spend Value Number>
|
|
41
47
|
}]
|
|
42
48
|
id: <Original Channel Standard Format Channel Id String>
|
|
@@ -51,10 +57,12 @@ const txIdAsHash = id => Buffer.from(id, 'hex').reverse();
|
|
|
51
57
|
{
|
|
52
58
|
[add_funds_vin]: <Funding Spend Input Index Number>
|
|
53
59
|
capacity: <Replacement Channel Capacity Tokens Number>
|
|
60
|
+
force_close_tx: <Raw Force Close Transaction Hex String>
|
|
61
|
+
open_pending_ids: [<Proposed Additional Channel Id String>]
|
|
62
|
+
pending_channel_id: <Proposed Replacement Channel Pending Id Hex String>
|
|
54
63
|
remote_balance: <Initial Balance for Peer Tokens Number>
|
|
55
64
|
signature: <Signature for Replacement Transaction Hex Encoded String>
|
|
56
65
|
signing_key: <Signing Public Key Hex String>
|
|
57
|
-
pending_channel_id: <Proposed Channel Pending Id Hex String>
|
|
58
66
|
transaction_id: <Replacement Channel Transaction Id Hex String>
|
|
59
67
|
transaction_vin: <Original Channel Input Index Number>
|
|
60
68
|
transaction_vout: <Replacement Channel Transaction Output Index Number>
|
|
@@ -102,8 +110,34 @@ module.exports = (args, cbk) => {
|
|
|
102
110
|
// Get the current chain height
|
|
103
111
|
getHeight: ['validate', ({}, cbk) => getHeight({lnd: args.lnd}, cbk)],
|
|
104
112
|
|
|
113
|
+
// Connect to nodes to open channels to
|
|
114
|
+
connect: ['validate', ({}, cbk) => {
|
|
115
|
+
return asyncEach(args.decrease.filter(n => !!n.node), (spend, cbk) => {
|
|
116
|
+
return connectPeer({id: spend.node, lnd: args.lnd}, cbk);
|
|
117
|
+
},
|
|
118
|
+
cbk);
|
|
119
|
+
}],
|
|
120
|
+
|
|
121
|
+
// Propose any additional channels to nodes
|
|
122
|
+
openChannels: ['connect', ({}, cbk) => {
|
|
123
|
+
// Exit early when there are no nodes to propose channels to
|
|
124
|
+
if (!args.decrease.filter(n => !!n.node).length) {
|
|
125
|
+
return cbk(null, {pending: []});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return openChannels({
|
|
129
|
+
channels: args.decrease.filter(n => !!n.node).map(decrease => ({
|
|
130
|
+
capacity: decrease.tokens,
|
|
131
|
+
partner_public_key: decrease.node,
|
|
132
|
+
})),
|
|
133
|
+
is_avoiding_broadcast: true,
|
|
134
|
+
lnd: args.lnd,
|
|
135
|
+
},
|
|
136
|
+
cbk);
|
|
137
|
+
}],
|
|
138
|
+
|
|
105
139
|
// Force close the channel in order to get a non-final sequence number
|
|
106
|
-
channelClose: ['getFeeRate', ({}, cbk) => {
|
|
140
|
+
channelClose: ['getFeeRate','openChannels', ({}, cbk) => {
|
|
107
141
|
return closeChannel({
|
|
108
142
|
lnd: args.lnd,
|
|
109
143
|
is_force_close: true,
|
|
@@ -266,7 +300,6 @@ module.exports = (args, cbk) => {
|
|
|
266
300
|
give_tokens: pendingChannel.remote_balance,
|
|
267
301
|
is_private: args.is_private,
|
|
268
302
|
partner_public_key: pendingChannel.partner_public_key,
|
|
269
|
-
is_private: args.is_private,
|
|
270
303
|
}],
|
|
271
304
|
is_avoiding_broadcast: true,
|
|
272
305
|
lnd: args.lnd,
|
|
@@ -278,19 +311,31 @@ module.exports = (args, cbk) => {
|
|
|
278
311
|
replacementTx: [
|
|
279
312
|
'closeTx',
|
|
280
313
|
'newCapacity',
|
|
314
|
+
'openChannels',
|
|
281
315
|
'proposeChannel',
|
|
282
|
-
({closeTx, newCapacity, proposeChannel}, cbk) =>
|
|
316
|
+
({closeTx, newCapacity, openChannels, proposeChannel}, cbk) =>
|
|
283
317
|
{
|
|
284
318
|
// The replacement tx pays to the proposed channel multisig address
|
|
285
319
|
const [{address}] = proposeChannel.pending;
|
|
286
320
|
|
|
321
|
+
const network = networks[args.bitcoinjs_network];
|
|
322
|
+
|
|
323
|
+
// Map pending channel opens to match the spends
|
|
324
|
+
const openAsSpends = openChannels.pending.map(({address, tokens}) => ({
|
|
325
|
+
tokens,
|
|
326
|
+
output: bufferAsHex(toOutputScript(address, network)),
|
|
327
|
+
}));
|
|
328
|
+
|
|
329
|
+
// Decreases are spends to regular addresses plus channel outputs
|
|
330
|
+
const decreases = [].concat(args.decrease).concat(openAsSpends);
|
|
331
|
+
|
|
287
332
|
try {
|
|
288
333
|
const replacement = assembleReplacementTx({
|
|
289
334
|
add_funds_transaction_id: args.add_funds_transaction_id,
|
|
290
335
|
add_funds_transaction_vout: args.add_funds_transaction_vout,
|
|
291
336
|
bitcoinjs_network: args.bitcoinjs_network,
|
|
292
337
|
close_transaction: closeTx.transaction,
|
|
293
|
-
decrease:
|
|
338
|
+
decrease: decreases.filter(n => !!n.output).map(decrease => ({
|
|
294
339
|
output: decrease.output,
|
|
295
340
|
tokens: decrease.tokens,
|
|
296
341
|
})),
|
|
@@ -335,6 +380,7 @@ module.exports = (args, cbk) => {
|
|
|
335
380
|
proposedReplacement: [
|
|
336
381
|
'closeTx',
|
|
337
382
|
'newCapacity',
|
|
383
|
+
'openChannels',
|
|
338
384
|
'pendingChannel',
|
|
339
385
|
'proposeChannel',
|
|
340
386
|
'replacementTx',
|
|
@@ -342,6 +388,7 @@ module.exports = (args, cbk) => {
|
|
|
342
388
|
({
|
|
343
389
|
closeTx,
|
|
344
390
|
newCapacity,
|
|
391
|
+
openChannels,
|
|
345
392
|
pendingChannel,
|
|
346
393
|
proposeChannel,
|
|
347
394
|
replacementTx,
|
|
@@ -354,11 +401,12 @@ module.exports = (args, cbk) => {
|
|
|
354
401
|
return cbk(null, {
|
|
355
402
|
add_funds_vin: replacementTx.add_funds_vin,
|
|
356
403
|
capacity: newCapacity,
|
|
357
|
-
|
|
404
|
+
force_close_tx: closeTx.transaction,
|
|
405
|
+
open_pending_ids: openChannels.pending.map(n => n.id),
|
|
406
|
+
pending_channel_id: pending.id,
|
|
358
407
|
remote_balance: pendingChannel.remote_balance,
|
|
359
408
|
signature: signReplacement.signature,
|
|
360
409
|
signing_key: signReplacement.key,
|
|
361
|
-
pending_channel_id: pending.id,
|
|
362
410
|
transaction_id: replacementTx.transaction_id,
|
|
363
411
|
transaction_vin: replacementTx.transaction_vin,
|
|
364
412
|
transaction_vout: replacementTx.transaction_vout,
|
|
@@ -47,7 +47,6 @@ const rebroadcastTimeMs = 1000 * 5;
|
|
|
47
47
|
const recordsAsObject = a => a.reduce((sum, n) => sum[n.type] = n.value, {});
|
|
48
48
|
const requestCapacityChangeIntervalMs = 2000;
|
|
49
49
|
const requestCapacityChangeTimeoutMs = 1000 * 60 * 60 * 6;
|
|
50
|
-
const sumOf = arr => arr.reduce((sum, n) => sum + n, Number());
|
|
51
50
|
const testMessage = '00';
|
|
52
51
|
const tokensAsBigUnit = tokens => (tokens / 1e8).toFixed(8);
|
|
53
52
|
const uniq = arr => Array.from(new Set(arr));
|