paid-services 3.3.0 → 3.4.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 (35) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/capacity/accept_capacity_change.js +3 -3
  3. package/capacity/get_capacity_change_requests.js +1 -1
  4. package/capacity/get_capacity_replacement.js +1 -1
  5. package/capacity/initiate_capacity_change.js +5 -4
  6. package/capacity/propose_capacity_change.js +3 -1
  7. package/p2p/service_peer_requests.js +10 -2
  8. package/package.json +3 -2
  9. package/service_types.json +16 -0
  10. package/test/integration/test_accept_capacity_change.js +50 -13
  11. package/test/integration/test_change_channel_capacity.js +3 -0
  12. package/test/trades/test_decode_basic_trade.js +40 -0
  13. package/test/trades/test_decode_open_trade.js +50 -0
  14. package/test/trades/test_encode_open_trade.js +35 -0
  15. package/test/trades/test_encode_trade.js +37 -0
  16. package/trades/accept_trade.js +52 -0
  17. package/trades/create_trade.js +124 -44
  18. package/trades/decode_basic_trade.js +45 -0
  19. package/trades/decode_open_trade.js +69 -0
  20. package/trades/decode_trade.js +48 -66
  21. package/trades/decode_trade_secret.js +89 -0
  22. package/trades/encode_open_trade.js +89 -0
  23. package/trades/encode_trade.js +48 -33
  24. package/trades/encode_trade_secret.js +50 -0
  25. package/trades/finalize_trade_secret.js +116 -0
  26. package/trades/find_trade.js +314 -0
  27. package/trades/manage_trade.js +46 -35
  28. package/trades/network_from_network_record.js +3 -3
  29. package/trades/network_record_from_network.js +36 -0
  30. package/trades/network_record_from_request.js +2 -1
  31. package/trades/node_as_record.js +98 -0
  32. package/trades/record_as_node.js +113 -0
  33. package/trades/request_trade_by_id.js +89 -0
  34. package/trades/service_trade_requests.js +184 -0
  35. package/capacity/service_types.json +0 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Versions
2
2
 
3
+ ## Version 3.4.0
4
+
5
+ - `changeChannelCapacity`: Fix broken preservation of channel announce status
6
+ - `manageTrades`: Add support for open-ended trades
7
+
3
8
  ## Version 3.3.0
4
9
 
5
10
  - `changeChannelCapacity`: Add method to change a channel's capacity
@@ -12,9 +12,9 @@ const {makePeerRequest} = require('./../p2p');
12
12
  const parseAcceptResponse = require('./parse_accept_response');
13
13
  const parseSignCapacityRequest = require('./parse_sign_capacity_request');
14
14
  const {servicePeerRequests} = require('./../p2p');
15
- const {serviceTypeAcceptCapacityChange} = require('./service_types');
16
- const {serviceTypeSignCapacityChange} = require('./service_types');
17
- const {serviceTypeWaitOnConfirmation} = require('./service_types');
15
+ const {serviceTypeAcceptCapacityChange} = require('./../service_types');
16
+ const {serviceTypeSignCapacityChange} = require('./../service_types');
17
+ const {serviceTypeWaitOnConfirmation} = require('./../service_types');
18
18
 
19
19
  const acceptChangeTimeoutMs = 1000 * 45;
20
20
  const bufferAsHex = buffer => buffer.toString('hex');
@@ -4,7 +4,7 @@ const {returnResult} = require('asyncjs-util');
4
4
  const {subscribeToPeerMessages} = require('ln-service');
5
5
 
6
6
  const capacityChangeRequests = require('./capacity_change_requests');
7
- const {serviceTypeChangeCapacity} = require('./service_types');
7
+ const {serviceTypeChangeCapacity} = require('./../service_types');
8
8
  const parseCapacityChangeRequest = require('./parse_capacity_change_request');
9
9
  const {servicePeerRequests} = require('./../p2p');
10
10
 
@@ -264,10 +264,10 @@ module.exports = (args, cbk) => {
264
264
  channels: [{
265
265
  capacity: newCapacity,
266
266
  give_tokens: pendingChannel.remote_balance,
267
+ is_private: args.is_private,
267
268
  partner_public_key: pendingChannel.partner_public_key,
268
269
  }],
269
270
  is_avoiding_broadcast: true,
270
- is_private: args.is_private,
271
271
  lnd: args.lnd,
272
272
  },
273
273
  cbk);
@@ -23,10 +23,10 @@ const {makePeerRequest} = require('./../p2p');
23
23
  const parseAcceptRequest = require('./parse_accept_request');
24
24
  const proposeCapacityChange = require('./propose_capacity_change');
25
25
  const {servicePeerRequests} = require('./../p2p');
26
- const {serviceTypeAcceptCapacityChange} = require('./service_types');
27
- const {serviceTypeChangeCapacity} = require('./service_types');
28
- const {serviceTypeSignCapacityChange} = require('./service_types');
29
- const {serviceTypeWaitOnConfirmation} = require('./service_types');
26
+ const {serviceTypeAcceptCapacityChange} = require('./../service_types');
27
+ const {serviceTypeChangeCapacity} = require('./../service_types');
28
+ const {serviceTypeSignCapacityChange} = require('./../service_types');
29
+ const {serviceTypeWaitOnConfirmation} = require('./../service_types');
30
30
 
31
31
  const bufferAsHex = buffer => buffer.toString('hex');
32
32
  const capacityChangeRequestIdType = '0';
@@ -438,6 +438,7 @@ module.exports = ({ask, lnd, logger}, cbk) => {
438
438
  increase_transaction_id: getFunding.id,
439
439
  increase_transaction_vout: getFunding.vout,
440
440
  increase_witness_script: getFunding.script,
441
+ is_private: channel.is_private,
441
442
  open_transaction: sendBasicRequest,
442
443
  partner_public_key: askForChangeDetails.partner_public_key,
443
444
  transaction_id: channel.transaction_id,
@@ -17,7 +17,7 @@ const finalizeCapacityReplacement = require('./finalize_capacity_replacement');
17
17
  const getCapacityReplacement = require('./get_capacity_replacement');
18
18
  const interimReplacementPsbt = require('./interim_replacement_psbt');
19
19
  const {makePeerRequest} = require('./../p2p');
20
- const {serviceTypeSignCapacityChange} = require('./service_types');
20
+ const {serviceTypeSignCapacityChange} = require('./../service_types');
21
21
 
22
22
  const capacityChangeIdType = '0';
23
23
  const findSignatureRecord = records => records.find(n => n.type === '1');
@@ -40,6 +40,7 @@ const unsignedTransactionType = '1';
40
40
  tokens: <Spend Value Number>
41
41
  }]
42
42
  id: <Capacity Change Request Id Hex String>
43
+ is_private: <Channel is Private Bool>
43
44
  [increase]: <Increase Channel Size Tokens Number>
44
45
  [increase_key_index]: <Increase Funds Transit Key Number>
45
46
  [increase_output_script]: <Increase Funds Output Script Hex String>
@@ -119,6 +120,7 @@ module.exports = (args, cbk) => {
119
120
  bitcoinjs_network: args.bitcoinjs_network,
120
121
  decrease: args.decrease,
121
122
  id: args.channel,
123
+ is_private: args.is_private,
122
124
  increase: args.increase,
123
125
  lnd: args.lnd,
124
126
  open_transaction: args.open_transaction,
@@ -16,6 +16,7 @@ const returnSuccessResponse = require('./return_success_response');
16
16
 
17
17
  @returns
18
18
  {
19
+ end: <Add a Service Ended Function>
19
20
  error: <Add an Error Listener Function>
20
21
  request: <Add A Request Listener For Type Function>
21
22
  stop: <Stop Listening Function> ({}) => {};
@@ -55,9 +56,15 @@ const returnSuccessResponse = require('./return_success_response');
55
56
  */
56
57
  module.exports = ({lnd}) => {
57
58
  const listeners = {};
58
- const service = {error: () => {}};
59
+ const service = {end: () => {}, error: () => {}};
59
60
  const sub = subscribeToPeerMessages({lnd});
60
61
 
62
+ sub.on('error', error => {
63
+ service.error(error);
64
+
65
+ return service.end();
66
+ });
67
+
61
68
  sub.on('message_received', received => {
62
69
  const from = received.public_key;
63
70
  const {message} = received;
@@ -93,7 +100,8 @@ module.exports = ({lnd}) => {
93
100
  });
94
101
 
95
102
  return {
96
- error: cbk => service[error] = cbk,
103
+ end: cbk => service.end = cbk,
104
+ error: cbk => service.error = cbk,
97
105
  request: ({type}, cbk) => listeners[type] = cbk,
98
106
  stop: ({}) => sub.removeAllListeners(),
99
107
  };
package/package.json CHANGED
@@ -10,6 +10,7 @@
10
10
  "async": "3.2.2",
11
11
  "asyncjs-util": "1.2.7",
12
12
  "bolt01": "1.2.3",
13
+ "bolt07": "1.8.0",
13
14
  "invoices": "2.0.2",
14
15
  "ln-service": "53.2.0",
15
16
  "ln-sync": "3.6.0"
@@ -37,7 +38,7 @@
37
38
  },
38
39
  "scripts": {
39
40
  "integration-tests": "tap --branches=1 --functions=1 --lines=1 --statements=1 -t 120 test/integration/*.js",
40
- "test": "tap --branches=1 --functions=1 --lines=1 --statements=1 -t 60 test/actions/*.js test/capacity/*.js test/client/*.js test/config/*.js test/p2p/*.js test/records/*.js test/respond/*.js test/server/*.js test/server/*.js test/services/*.js"
41
+ "test": "tap --branches=1 --functions=1 --lines=1 --statements=1 -t 60 test/actions/*.js test/capacity/*.js test/client/*.js test/config/*.js test/p2p/*.js test/records/*.js test/respond/*.js test/server/*.js test/server/*.js test/services/*.js test/trades/*.js"
41
42
  },
42
- "version": "3.3.0"
43
+ "version": "3.4.0"
43
44
  }
@@ -0,0 +1,16 @@
1
+ {
2
+ "serviceTypeAcceptCapacityChange": "8050002",
3
+ "serviceTypeChangeCapacity": "8050001",
4
+ "serviceTypeReceiveTrades": "8050006",
5
+ "serviceTypeRequestTrades": "8050005",
6
+ "serviceTypeSignCapacityChange": "8050004",
7
+ "serviceTypeWaitOnConfirmation": "8050003",
8
+ "types": {
9
+ "8050001": "serviceTypeChangeCapacity",
10
+ "8050002": "serviceTypeAcceptCapacityChange",
11
+ "8050003": "serviceTypeWaitOnConfirmation",
12
+ "8050004": "serviceTypeSignCapacityChange",
13
+ "8050005": "serviceTypeRequestTrades",
14
+ "8050006": "serviceTypeReceiveTrades"
15
+ }
16
+ }
@@ -111,9 +111,12 @@ test(`Accept capacity replacement`, async ({end, equal, strictSame}) => {
111
111
  transaction_id: channel.transaction_id,
112
112
  transaction_vout: channel.transaction_vout,
113
113
  });
114
+
115
+ proposeDone = true;
116
+
117
+ return;
114
118
  },
115
119
 
116
- // Wait for a
117
120
  accept: async () => {
118
121
  await acceptCapacityChange({
119
122
  id,
@@ -123,25 +126,59 @@ test(`Accept capacity replacement`, async ({end, equal, strictSame}) => {
123
126
  lnd: target.lnd,
124
127
  });
125
128
  },
126
- });
127
129
 
128
- // Wait for the new channel to be active
129
- const recreated = await asyncRetry({interval, times}, async () => {
130
- const [channel] = (await getChannels({lnd, is_active: true})).channels;
130
+ confirmTarget: ['accept', async ({}) => {
131
+ // Wait for the new channel to be active
132
+ const recreated = await asyncRetry({interval, times}, async () => {
133
+ const controlChannels = (await getChannels({lnd, is_active: true}));
131
134
 
132
- if (!!channel) {
133
- return channel;
134
- }
135
+ const [channel] = controlChannels.channels;
135
136
 
136
- await generate({});
137
+ const targetChans = await getChannels({
138
+ is_active: true,
139
+ lnd: target.lnd,
140
+ });
137
141
 
138
- throw new Error('ExpectedChannelActivation');
139
- });
142
+ const targetChan = targetChans.channels;
143
+
144
+ if (!!channel && !!targetChan && channel.capacity < capacity) {
145
+ return channel;
146
+ }
140
147
 
141
- strictSame(recreated.is_active, true, 'Recreated channel is active');
148
+ await generate({});
142
149
 
143
- // Recreate the channel but in reverse roles
150
+ throw new Error('ExpectedChannelActivation');
151
+ });
152
+
153
+ strictSame(recreated.is_active, true, 'Recreated channel is active');
154
+ }],
155
+
156
+ confirmControl: ['accept', 'propose', async ({}) => {
157
+ // Wait for the new channel to be active
158
+ const recreated = await asyncRetry({interval, times}, async () => {
159
+ const controlChannels = (await getChannels({lnd, is_active: true}));
160
+
161
+ const [channel] = controlChannels.channels;
162
+
163
+ const targetChans = await getChannels({
164
+ is_active: true,
165
+ lnd: target.lnd,
166
+ });
167
+
168
+ const targetChan = targetChans.channels;
144
169
 
170
+ if (!!channel && !!targetChan && channel.capacity < capacity) {
171
+ return channel;
172
+ }
173
+
174
+ await generate({});
175
+
176
+ throw new Error('ExpectedChannelActivation');
177
+ });
178
+
179
+ strictSame(recreated.is_active, true, 'Recreated channel is active');
180
+ }],
181
+ });
145
182
  } catch (err) {
146
183
  strictSame(err, null, 'Expected no failure');
147
184
  } finally {
@@ -49,6 +49,7 @@ test(`Accept capacity replacement`, async ({end, equal, strictSame}) => {
49
49
  // Open up a new channel
50
50
  const channelOpen = await openChannel({
51
51
  lnd,
52
+ is_private: true,
52
53
  local_tokens: capacity,
53
54
  partner_public_key: target.id,
54
55
  partner_socket: target.socket,
@@ -159,6 +160,8 @@ test(`Accept capacity replacement`, async ({end, equal, strictSame}) => {
159
160
  throw new Error('ExpectedChannelActivation');
160
161
  }
161
162
 
163
+ equal(channel.is_private, true, 'Channel is private');
164
+
162
165
  {
163
166
  const {policies} = await getChannel({lnd, id: channel.id});
164
167
 
@@ -0,0 +1,40 @@
1
+ const {test} = require('@alexbosworth/tap');
2
+
3
+ const method = require('./../../trades/decode_basic_trade');
4
+
5
+ const tests = [
6
+ {
7
+ args: {},
8
+ description: 'Records are expected',
9
+ error: 'ExpectedArrayOfRecordsToDecodeBasicTrade',
10
+ },
11
+ {
12
+ args: {records: []},
13
+ description: 'A description record is expected',
14
+ error: 'ExpectedDescriptionRecordToDecodeBasicTrade',
15
+ },
16
+ {
17
+ args: {records: [{type: '2', value: '00'}]},
18
+ description: 'An id record is expected',
19
+ error: 'ExpectedIdRecordToDecodeBasicTradeDetails',
20
+ },
21
+ {
22
+ args: {records: [{type: '1', value: '00'}, {type: '2', value: '00'}]},
23
+ description: 'Basic trade is encoded',
24
+ expected: {description: '\u0000', id: '00'},
25
+ },
26
+ ];
27
+
28
+ tests.forEach(({args, description, error, expected}) => {
29
+ return test(description, async ({end, strictSame, throws}) => {
30
+ if (!!error) {
31
+ throws(() => method(args), new Error(error), 'Got error');
32
+ } else {
33
+ const res = method(args);
34
+
35
+ strictSame(res, expected, 'Got expected result');
36
+ }
37
+
38
+ return end();
39
+ });
40
+ });
@@ -0,0 +1,50 @@
1
+ const {test} = require('@alexbosworth/tap');
2
+
3
+ const method = require('./../../trades/decode_open_trade');
4
+
5
+ const tests = [
6
+ {
7
+ args: {},
8
+ description: 'A network name is expected',
9
+ error: 'ExpectedNetworkNameToDecodeOpenTrade',
10
+ },
11
+ {
12
+ args: {network: 'btc'},
13
+ description: 'Records are expected',
14
+ error: 'ExpectedArrayOfRecordsToDecodeOpenTrade',
15
+ },
16
+ {
17
+ args: {network: 'btc', records: []},
18
+ description: 'Nodes are expected',
19
+ error: 'ExpectedNodesRecordToDecodeOpenTradeDetails',
20
+ },
21
+ {
22
+ args: {
23
+ network: 'btc',
24
+ records: [
25
+ {type: '0', value: '01'},
26
+ {type: '4', value: '000a01080000010000020003'},
27
+ ],
28
+ },
29
+ description: 'Open trade is decoded',
30
+ expected: {
31
+ id: undefined,
32
+ network: 'btc',
33
+ nodes: [{high_channel: '1x2x3'}],
34
+ },
35
+ },
36
+ ];
37
+
38
+ tests.forEach(({args, description, error, expected}) => {
39
+ return test(description, async ({end, strictSame, throws}) => {
40
+ if (!!error) {
41
+ throws(() => method(args), new Error(error), 'Got error');
42
+ } else {
43
+ const res = method(args);
44
+
45
+ strictSame(res, expected, 'Got expected result');
46
+ }
47
+
48
+ return end();
49
+ });
50
+ });
@@ -0,0 +1,35 @@
1
+ const {test} = require('@alexbosworth/tap');
2
+
3
+ const method = require('./../../trades/encode_open_trade');
4
+
5
+ const tests = [
6
+ {
7
+ args: {
8
+ network: 'btc',
9
+ nodes: [{
10
+ channels: [{
11
+ id: '1x2x3',
12
+ partner_public_key: Buffer.alloc(33, 2).toString('hex'),
13
+ }],
14
+ id: Buffer.alloc(33, 3).toString('hex'),
15
+ sockets: [],
16
+ }],
17
+ },
18
+ description: 'Open trade is encoded',
19
+ expected: {trade: '626f73ff000101040c000a01080000010000020003'},
20
+ },
21
+ ];
22
+
23
+ tests.forEach(({args, description, error, expected}) => {
24
+ return test(description, async ({end, strictSame, throws}) => {
25
+ if (!!error) {
26
+ throws(() => method(args), new Error(error), 'Got error');
27
+ } else {
28
+ const res = method(args);
29
+
30
+ strictSame(res, expected, 'Got expected result');
31
+ }
32
+
33
+ return end();
34
+ });
35
+ });
@@ -0,0 +1,37 @@
1
+ const {test} = require('@alexbosworth/tap');
2
+
3
+ const method = require('./../../trades/encode_trade');
4
+
5
+ const tests = [
6
+ {
7
+ args: {
8
+ connect: {
9
+ network: 'btc',
10
+ nodes: [{
11
+ channels: [{
12
+ id: '1x2x3',
13
+ partner_public_key: Buffer.alloc(33, 2).toString('hex'),
14
+ }],
15
+ id: Buffer.alloc(33, 3).toString('hex'),
16
+ sockets: [],
17
+ }],
18
+ },
19
+ },
20
+ description: 'Trade is encoded',
21
+ expected: {trade: '626f73ff000101040c000a01080000010000020003'},
22
+ },
23
+ ];
24
+
25
+ tests.forEach(({args, description, error, expected}) => {
26
+ return test(description, async ({end, strictSame, throws}) => {
27
+ if (!!error) {
28
+ throws(() => method(args), new Error(error), 'Got error');
29
+ } else {
30
+ const res = method(args);
31
+
32
+ strictSame(res, expected, 'Got expected result');
33
+ }
34
+
35
+ return end();
36
+ });
37
+ });
@@ -0,0 +1,52 @@
1
+ const asyncAuto = require('async/auto');
2
+ const asyncEach = require('async/each');
3
+ const {cancelHodlInvoice} = require('ln-service');
4
+ const {returnResult} = require('asyncjs-util');
5
+ const {settleHodlInvoice} = require('ln-service');
6
+
7
+ const {isArray} = Array;
8
+
9
+ /** Accept an open ended trade
10
+
11
+ {
12
+ cancel: [<Alternative Invoice Id Hex String>]
13
+ lnd: <Authenticated LND API Object>
14
+ secret: <Invoice to Settle Preimage Hex String>
15
+ }
16
+
17
+ @returns via cbk or Promise
18
+ */
19
+ module.exports = ({cancel, lnd, secret}, cbk) => {
20
+ return new Promise((resolve, reject) => {
21
+ return asyncAuto({
22
+ // Check arguments
23
+ validate: cbk => {
24
+ if (!isArray(cancel)) {
25
+ return cbk([400, 'ExpectedArrayOfIdsToCancelToAcceptTrade']);
26
+ }
27
+
28
+ if (!lnd) {
29
+ return cbk([400, 'ExpectedAuthenticatedLndToAcceptTrade']);
30
+ }
31
+
32
+ if (!secret) {
33
+ return cbk([400, 'ExpectedSettlementPreimageToAcceptTrade']);
34
+ }
35
+
36
+ return cbk();
37
+ },
38
+
39
+ // Cancel alternative invoices so that only one resolves as settled
40
+ cancel: ['validate', ({}, cbk) => {
41
+ return asyncEach(cancel, (id, cbk) => {
42
+ return cancelHodlInvoice({id, lnd}, cbk);
43
+ },
44
+ cbk);
45
+ }],
46
+
47
+ // Settle the held invoice with the preimage
48
+ settle: ['cancel', ({}, cbk) => settleHodlInvoice({lnd, secret}, cbk)],
49
+ },
50
+ returnResult({reject, resolve}, cbk));
51
+ });
52
+ };