paid-services 3.2.0 → 3.3.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/capacity/accept_capacity_change.js +284 -0
  3. package/capacity/ask_for_change_details.js +505 -0
  4. package/capacity/ask_for_decrease.js +185 -0
  5. package/capacity/assemble_replacement_tx.js +171 -0
  6. package/capacity/capacity_change_requests.js +76 -0
  7. package/capacity/change_channel_capacity.js +183 -0
  8. package/capacity/encode_change_request.js +55 -0
  9. package/capacity/encode_sign_capacity_response.js +32 -0
  10. package/capacity/fee_for_replacement.js +108 -0
  11. package/capacity/finalize_capacity_replacement.js +67 -0
  12. package/capacity/get_capacity_change_requests.js +98 -0
  13. package/capacity/get_capacity_replacement.js +371 -0
  14. package/capacity/get_replacement_signature.js +232 -0
  15. package/capacity/index.js +4 -0
  16. package/capacity/initiate_capacity_change.js +506 -0
  17. package/capacity/interim_replacement_psbt.js +57 -0
  18. package/capacity/is_open_witness_script.js +40 -0
  19. package/capacity/parse_accept_request.js +58 -0
  20. package/capacity/parse_accept_response.js +52 -0
  21. package/capacity/parse_capacity_change_request.js +101 -0
  22. package/capacity/parse_sign_capacity_request.js +59 -0
  23. package/capacity/propose_capacity_change.js +361 -0
  24. package/capacity/service_types.json +6 -0
  25. package/capacity/sign_capacity_replacement.js +119 -0
  26. package/capacity/witness_script_from_close_txs.js +50 -0
  27. package/index.js +2 -0
  28. package/p2p/encode_peer_response.js +0 -1
  29. package/p2p/make_peer_request.js +3 -3
  30. package/package.json +6 -4
  31. package/test/capacity/test_assemble_replacement_tx.js +46 -0
  32. package/test/capacity/test_capacity_change_requests.js +54 -0
  33. package/test/capacity/test_encode_change_request.js +49 -0
  34. package/test/capacity/test_encode_sign_capacity_response.js +47 -0
  35. package/test/capacity/test_fee_for_replacement.js +40 -0
  36. package/test/capacity/test_finalize_capacity_replacement.js +45 -0
  37. package/test/capacity/test_interim_replacement_psbt.js +44 -0
  38. package/test/capacity/test_is_open_witness_script.js +40 -0
  39. package/test/capacity/test_parse_accept_request.js +42 -0
  40. package/test/capacity/test_parse_capacity_change_request.js +51 -0
  41. package/test/capacity/test_parse_sign_capacity_request.js +49 -0
  42. package/test/capacity/test_witness_script_from_close_txs.js +53 -0
  43. package/test/integration/test_accept_capacity_change.js +152 -0
  44. package/test/integration/test_change_channel_capacity.js +196 -0
  45. package/test/integration/test_decrease_channel_capacity.js +206 -0
  46. package/test/integration/test_increase_channel_capacity.js +197 -0
  47. package/test/integration/test_sign_capacity_replacement.js +142 -0
  48. package/test/p2p/test_encode_peer_request.js +53 -0
  49. package/test/p2p/test_encode_peer_response.js +38 -0
  50. package/test/p2p/test_encode_response_code.js +48 -0
  51. package/trades/network_from_network_record.js +6 -0
  52. package/trades/network_record_from_request.js +5 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Versions
2
2
 
3
+ ## Version 3.3.0
4
+
5
+ - `changeChannelCapacity`: Add method to change a channel's capacity
6
+
3
7
  ## Version 3.2.0
4
8
 
5
9
  - `makePeerRequest`: Add method to make peer messaging requests
@@ -0,0 +1,284 @@
1
+ const asyncAuto = require('async/auto');
2
+ const {getChainTransactions} = require('ln-service');
3
+ const {getChannel} = require('ln-service');
4
+ const {getChannels} = require('ln-service');
5
+ const {returnResult} = require('asyncjs-util');
6
+ const {stopAllHtlcs} = require('ln-sync');
7
+ const {Transaction} = require('bitcoinjs-lib');
8
+
9
+ const encodeSignCapacityResponse = require('./encode_sign_capacity_response');
10
+ const getReplacementSignature = require('./get_replacement_signature');
11
+ const {makePeerRequest} = require('./../p2p');
12
+ const parseAcceptResponse = require('./parse_accept_response');
13
+ const parseSignCapacityRequest = require('./parse_sign_capacity_request');
14
+ const {servicePeerRequests} = require('./../p2p');
15
+ const {serviceTypeAcceptCapacityChange} = require('./service_types');
16
+ const {serviceTypeSignCapacityChange} = require('./service_types');
17
+ const {serviceTypeWaitOnConfirmation} = require('./service_types');
18
+
19
+ const acceptChangeTimeoutMs = 1000 * 45;
20
+ const bufferAsHex = buffer => buffer.toString('hex');
21
+ const findId = records => (records.find(n => n.type === '0') || {}).value;
22
+ const {fromHex} = Transaction;
23
+ const idType = '0';
24
+ const openTxType = '1';
25
+
26
+ /** Accept a request from a peer to change a channel's capacity
27
+
28
+ {
29
+ channel: <Standard Format Channel Id String>
30
+ from: <Accept Request From Public Key Hex String>
31
+ id: <Request Id Hex String>
32
+ [increase]: <Increase Tokens Number>
33
+ lnd: <Authenticated LND API Object>
34
+ logger: <Winston Logger Object>
35
+ }
36
+
37
+ @returns via cbk or Promise
38
+ {
39
+ base_fee_mtokens: <Original Forwarding Base Fee Millitokens String>
40
+ cltv_delta: <Original Forwarding CLTV Delta Number>
41
+ fee_rate: <Original Forwarding Parts Per Million Fee Rate Number>
42
+ transaction_id: <Replacement Channel Transaction Id Hex String>
43
+ transaction_vout: <Replacement Channel Transaction Output Index Number>
44
+ }
45
+ */
46
+ module.exports = ({channel, from, id, increase, lnd, logger}, cbk) => {
47
+ return new Promise((resolve, reject) => {
48
+ return asyncAuto({
49
+ // Check arguments
50
+ validate: cbk => {
51
+ if (!channel) {
52
+ return cbk([400, 'ExpectedChannelIdToAcceptCapacityChange']);
53
+ }
54
+
55
+ if (!from) {
56
+ return cbk([400, 'ExpectedFromPublicKeyToAcceptCapacityChange']);
57
+ }
58
+
59
+ if (!id) {
60
+ return cbk([400, 'ExpectedRequestIdToAcceptCapacityChange']);
61
+ }
62
+
63
+ if (!lnd) {
64
+ return cbk([400, 'ExpectedAuthenticatedLndToAcceptCapacityChange']);
65
+ }
66
+
67
+ if (!logger) {
68
+ return cbk([400, 'ExpectedLoggerObjectToAcceptCapacityChange']);
69
+ }
70
+
71
+ return cbk();
72
+ },
73
+
74
+ // Get the fee policies on the channel to set it back on success
75
+ getChannel: ['validate', ({}, cbk) => {
76
+ return getChannel({lnd, id: channel}, cbk);
77
+ }],
78
+
79
+ // Get all channel ids with the peer in order to stop HTLCs on them
80
+ getChannels: ['validate', ({}, cbk) => {
81
+ return getChannels({lnd, partner_public_key: from}, cbk);
82
+ }],
83
+
84
+ // Get chain transactions to look for the channel open tx
85
+ getChainTxs: ['validate', ({}, cbk) => {
86
+ return getChainTransactions({lnd}, cbk);
87
+ }],
88
+
89
+ // Policy for the channel
90
+ policy: ['getChannel', ({getChannel}, cbk) => {
91
+ const policy = getChannel.policies.find(n => n.public_key !== from);
92
+
93
+ if (!policy.cltv_delta) {
94
+ return cbk([404, 'FailedToFindChannelRoutingPolicyDetails']);
95
+ }
96
+
97
+ return cbk(null, {
98
+ base_fee_mtokens: policy.base_fee_mtokens,
99
+ cltv_delta: policy.cltv_delta,
100
+ fee_rate: policy.fee_rate,
101
+ transaction_id: getChannel.transaction_id,
102
+ transaction_vout: getChannel.transaction_vout,
103
+ });
104
+ }],
105
+
106
+ // Send the initial proposal acceptance to the peer
107
+ acceptChange: [
108
+ 'getChainTxs',
109
+ 'getChannel',
110
+ 'getChannels',
111
+ 'policy',
112
+ ({getChainTxs, getChannel}, cbk) =>
113
+ {
114
+ const records = [{type: idType, value: id}];
115
+
116
+ const tx = getChainTxs.transactions.find(transaction => {
117
+ return transaction.id === getChannel.transaction_id;
118
+ });
119
+
120
+ const txRecordValue = !!tx && !!tx.transaction ? tx.transaction : null;
121
+
122
+ if (!!txRecordValue) {
123
+ records.push({type: openTxType, value: txRecordValue});
124
+ }
125
+
126
+ // Kick off the capacity change
127
+ return makePeerRequest({
128
+ lnd,
129
+ records,
130
+ timeout: acceptChangeTimeoutMs,
131
+ to: from,
132
+ type: serviceTypeAcceptCapacityChange,
133
+ },
134
+ (err, res) => {
135
+ if (!!err) {
136
+ return cbk(err);
137
+ }
138
+
139
+ try {
140
+ const {transaction} = parseAcceptResponse({
141
+ records: res.records,
142
+ transaction_id: getChannel.transaction_id,
143
+ });
144
+
145
+ if (!transaction && !txRecordValue) {
146
+ return cbk([404, 'MissingTransactionOpenRawTxData']);
147
+ }
148
+
149
+ return cbk(null, {transaction: transaction || txRecordValue});
150
+ } catch (err) {
151
+ return cbk([503, err.message]);
152
+ }
153
+ });
154
+ }],
155
+
156
+ // Stop HTLCs with the peer until the channel is closed
157
+ stopHtlcs: ['getChannels', ({getChannels}, cbk) => {
158
+ return stopAllHtlcs({
159
+ lnd,
160
+ id: channel,
161
+ ids: getChannels.channels.map(n => n.id),
162
+ peer: from,
163
+ },
164
+ cbk);
165
+ }],
166
+
167
+ // Lookup the open transaction and get the output script
168
+ output: [
169
+ 'acceptChange',
170
+ 'getChannel',
171
+ ({acceptChange, getChannel}, cbk) =>
172
+ {
173
+ const {outs} = fromHex(acceptChange.transaction);
174
+
175
+ const output = outs[getChannel.transaction_vout];
176
+
177
+ if (!output) {
178
+ return cbk([503, 'ExpectedOpenTxFundingTransactionOutput']);
179
+ }
180
+
181
+ return cbk(null, bufferAsHex(output.script));
182
+ }],
183
+
184
+ // Ask to change capacity and return the final signature when needed
185
+ changeCapacity: ['output', 'policy', ({output, policy}, cbk) => {
186
+ logger.info({starting_capacity_change: true});
187
+
188
+ const service = servicePeerRequests({lnd});
189
+
190
+ // Listen for close-and-replace signals
191
+ service.request({type: serviceTypeSignCapacityChange}, (req, res) => {
192
+ // Exit early when the request is from a different peer/change
193
+ if (req.from !== from || findId(req.records) !== id) {
194
+ return;
195
+ }
196
+
197
+ const {failure, success} = res;
198
+
199
+ const parameters = {
200
+ increase,
201
+ id: policy.transaction_id,
202
+ records: req.records,
203
+ vout: policy.transaction_vout,
204
+ };
205
+
206
+ // Check to make sure that the request records make sense
207
+ try {
208
+ parseSignCapacityRequest(parameters);
209
+ } catch (err) {
210
+ logger.error({err: [503, err.message]});
211
+
212
+ return failure([503, 'FailedToParseSignChangeRequest']);
213
+ }
214
+
215
+ // Trigger the replacement flow and sign the unsigned tx
216
+ return getReplacementSignature({
217
+ channel,
218
+ lnd,
219
+ output,
220
+ id: parameters.id,
221
+ unsigned: parseSignCapacityRequest(parameters).unsigned,
222
+ vout: parameters.vout,
223
+ },
224
+ (err, res) => {
225
+ if (!!err) {
226
+ logger.error({err});
227
+
228
+ return failure([503, 'UnexpectedErrorGettingSignature']);
229
+ }
230
+
231
+ // Stop listening for sign capacity change requests
232
+ service.stop({});
233
+
234
+ // Encode the signature into records
235
+ const {records} = encodeSignCapacityResponse({
236
+ id: parameters.id,
237
+ signature: res.signature,
238
+ });
239
+
240
+ // Send the replacement signature for the change request
241
+ success({records});
242
+
243
+ logger.info({new_channel_transaction_id: res.transaction_id});
244
+
245
+ // Return the replacement funding outpoint
246
+ return cbk(null, {
247
+ transaction_id: res.transaction_id,
248
+ transaction_vout: res.transaction_vout,
249
+ });
250
+ });
251
+ });
252
+
253
+ // Listen for wait-for-confirmation signals
254
+ service.request({type: serviceTypeWaitOnConfirmation}, (req, res) => {
255
+ // Exit early when the request is from a different peer/change
256
+ if (req.from !== from || findId(req.records) !== id) {
257
+ return;
258
+ }
259
+
260
+ logger.info({peer_is_looking_for_confirmation: new Date()});
261
+
262
+ // Return an ack that the waiting signal was heard
263
+ return res.success({});
264
+ });
265
+ }],
266
+
267
+ // Accepted the capacity change
268
+ accepted: [
269
+ 'changeCapacity',
270
+ 'policy',
271
+ ({changeCapacity, policy}, cbk) =>
272
+ {
273
+ return cbk(null, {
274
+ base_fee_mtokens: policy.base_fee_mtokens,
275
+ cltv_delta: policy.cltv_delta,
276
+ fee_rate: policy.fee_rate,
277
+ transaction_id: changeCapacity.transaction_id,
278
+ transaction_vout: changeCapacity.transaction_vout,
279
+ });
280
+ }],
281
+ },
282
+ returnResult({reject, resolve, of: 'accepted'}, cbk));
283
+ });
284
+ };