paid-services 3.15.2 → 3.15.3

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Versions
2
2
 
3
- ## Version 3.15.2
3
+ ## Version 3.15.3
4
4
 
5
5
  - `manageSwap`: Add method to execute submarine swaps on testnet
6
6
 
package/package.json CHANGED
@@ -47,5 +47,5 @@
47
47
  "integration-tests": "tap -j 2 --branches=1 --functions=1 --lines=1 --statements=1 -t 180 test/integration/*.js",
48
48
  "test": "tap --branches=1 --functions=1 --lines=1 --statements=1 -t 60 test/actions/*.js test/balanced/*.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"
49
49
  },
50
- "version": "3.15.2"
50
+ "version": "3.15.3"
51
51
  }
@@ -418,8 +418,7 @@ module.exports = (args, cbk) => {
418
418
  });
419
419
 
420
420
  return cbk(null, tx.toHex());
421
- },
422
- cbk);
421
+ });
423
422
  },
424
423
  cbk);
425
424
  }],
@@ -681,7 +680,7 @@ module.exports = (args, cbk) => {
681
680
  transaction,
682
681
  lnd: args.lnd,
683
682
  },
684
- err => {});
683
+ () => {});
685
684
  });
686
685
 
687
686
  subTimeout.on('error', err => done(err));
@@ -1,5 +1,6 @@
1
1
  const asyncAuto = require('async/auto');
2
- const {getNetwork} = require('ln-sync')
2
+ const {getMasterPublicKeys} = require('ln-service');
3
+ const {getNetwork} = require('ln-sync');
3
4
  const {returnResult} = require('asyncjs-util');
4
5
 
5
6
  const recoverResponseToSwapOut = require('./recover_response_to_swap_out');
@@ -7,6 +8,7 @@ const requestSwapOut = require('./request_swap_out');
7
8
  const respondToSwapOutRequest = require('./respond_to_swap_out_request');
8
9
 
9
10
  const allowedNetwork = 'btctestnet';
11
+ const bip86Path = `m/86'/0'/0'`;
10
12
  const recoverRespondAction = 'recover-response';
11
13
  const requestAction = 'request';
12
14
  const respondAction = 'respond';
@@ -46,13 +48,25 @@ module.exports = ({ask, lnd, logger, request}, cbk) => {
46
48
  return cbk();
47
49
  },
48
50
 
51
+ // Look for a p2tr master public key to detect p2tr support
52
+ getMasterPublicKeys: ['validate', ({}, cbk) => {
53
+ return getMasterPublicKeys({lnd}, cbk);
54
+ }],
55
+
49
56
  // Get the network to make sure this is on testnet
50
57
  getNetwork: ['validate', ({}, cbk) => getNetwork({lnd}, cbk)],
51
58
 
59
+ // Determine if taproot is supported or not
60
+ hasTr: ['getMasterPublicKeys', ({getMasterPublicKeys}, cbk) => {
61
+ const {keys} = getMasterPublicKeys;
62
+
63
+ return cbk(null, !!keys.find(n => n.derivation_path === bip86Path));
64
+ }],
65
+
52
66
  // Select a swap action
53
- selectAction: ['getNetwork', ({getNetwork}, cbk) => {
67
+ selectAction: ['getNetwork', 'hasTr', ({getNetwork, hasTr}, cbk) => {
54
68
  // Check to make sure the network is supported
55
- if (getNetwork.network !== allowedNetwork) {
69
+ if (!hasTr && getNetwork.network !== allowedNetwork) {
56
70
  return cbk([501, 'CurrentlyUnsupportedChainForSwap']);
57
71
  }
58
72
 
@@ -79,30 +93,52 @@ module.exports = ({ask, lnd, logger, request}, cbk) => {
79
93
  }],
80
94
 
81
95
  // Make swap request
82
- makeRequest: ['selectAction', ({selectAction}, cbk) => {
96
+ makeRequest: ['hasTr', 'selectAction', ({hasTr, selectAction}, cbk) => {
83
97
  if (selectAction !== requestAction) {
84
98
  return cbk();
85
99
  }
86
100
 
87
- return requestSwapOut({ask, lnd, logger, request}, cbk);
101
+ return requestSwapOut({
102
+ ask,
103
+ lnd,
104
+ logger,
105
+ request: !hasTr ? request : undefined,
106
+ },
107
+ cbk);
88
108
  }],
89
109
 
90
110
  // Respond to swap request
91
- makeResponse: ['selectAction', ({selectAction}, cbk) => {
111
+ makeResponse: ['hasTr', 'selectAction', ({hasTr, selectAction}, cbk) => {
92
112
  if (selectAction !== respondAction) {
93
113
  return cbk();
94
114
  }
95
115
 
96
- return respondToSwapOutRequest({ask, lnd, logger, request}, cbk);
116
+ return respondToSwapOutRequest({
117
+ ask,
118
+ lnd,
119
+ logger,
120
+ request: !hasTr ? request : undefined,
121
+ },
122
+ cbk);
97
123
  }],
98
124
 
99
125
  // Recover responding to swap request
100
- recoverResponse: ['selectAction', ({selectAction}, cbk) => {
126
+ recoverResponse: [
127
+ 'hasTr',
128
+ 'selectAction',
129
+ ({hasTr, selectAction}, cbk) =>
130
+ {
101
131
  if (selectAction !== recoverRespondAction) {
102
132
  return cbk();
103
133
  }
104
134
 
105
- return recoverResponseToSwapOut({ask, lnd, logger, request}, cbk);
135
+ return recoverResponseToSwapOut({
136
+ ask,
137
+ lnd,
138
+ logger,
139
+ request: !hasTr ? request : undefined,
140
+ },
141
+ cbk);
106
142
  }],
107
143
 
108
144
  // Final result
@@ -11,7 +11,7 @@ const completeOnToOffSwap = require('./complete_on_to_off_swap');
11
11
  ask: <Ask Function>
12
12
  lnd: <Authenticated LND API Object>
13
13
  logger: <Winston Logger Object>
14
- request: <Request Function>
14
+ [request]: <Request Function>
15
15
  }
16
16
 
17
17
  @returns via cbk or Promise
@@ -33,10 +33,6 @@ module.exports = ({ask, lnd, logger, request}, cbk) => {
33
33
  return cbk([400, 'ExpectedWinstonLoggerToRecoverResponseToSwapOut']);
34
34
  }
35
35
 
36
- if (!request) {
37
- return cbk([400, 'ExpectedRequestFunctionToRecoverResponseToSwap']);
38
- }
39
-
40
36
  return cbk();
41
37
  },
42
38
 
@@ -22,27 +22,29 @@ const rateDenominator = 1e6;
22
22
 
23
23
  {
24
24
  ask: <Ask Function>
25
+ [is_avoiding_broadcast]: <Avoid Broadcasting Bool>
26
+ [is_uncooperative]: <Avoid Cooperative Resolution Bool>
25
27
  lnd: <Authenticated LND API Object>
26
28
  logger: <Winston Logger Object>
27
- request: <Request Function>
29
+ [request]: <Request Function>
28
30
  }
29
31
 
30
32
  @returns via cbk or Promise
31
33
  */
32
- module.exports = ({ask, lnd, logger, request}, cbk) => {
34
+ module.exports = (args, cbk) => {
33
35
  return new Promise((resolve, reject) => {
34
36
  return asyncAuto({
35
37
  // Check arguments
36
38
  validate: cbk => {
37
- if (!ask) {
39
+ if (!args.ask) {
38
40
  return cbk([400, 'ExpectedAskFunctionToRequestSwapOut']);
39
41
  }
40
42
 
41
- if (!lnd) {
43
+ if (!args.lnd) {
42
44
  return cbk([400, 'ExpectedAuthenticatedLndToRequestSwapOut']);
43
45
  }
44
46
 
45
- if (!logger) {
47
+ if (!args.logger) {
46
48
  return cbk([400, 'ExpectedWinstonLoggerToRequestSwapOut']);
47
49
  }
48
50
 
@@ -51,7 +53,7 @@ module.exports = ({ask, lnd, logger, request}, cbk) => {
51
53
 
52
54
  // Ask for amount
53
55
  askForTokens: ['validate', ({}, cbk) => {
54
- return ask({
56
+ return args.ask({
55
57
  default: defaultSwapAmount,
56
58
  message: 'Amount to swap?',
57
59
  name: 'tokens',
@@ -71,11 +73,11 @@ module.exports = ({ask, lnd, logger, request}, cbk) => {
71
73
  }],
72
74
 
73
75
  // Get the network name
74
- getNetwork: ['validate', ({}, cbk) => getNetwork({lnd}, cbk)],
76
+ getNetwork: ['validate', ({}, cbk) => getNetwork({lnd: args.lnd}, cbk)],
75
77
 
76
78
  // Ask for routing fee rate
77
79
  askForRate: ['askForTokens', ({}, cbk) => {
78
- return ask({
80
+ return args.ask({
79
81
  default: defaultFeeRate,
80
82
  message: 'Max routing fee rate for swap funds in parts per million?',
81
83
  name: 'rate',
@@ -97,8 +99,8 @@ module.exports = ({ask, lnd, logger, request}, cbk) => {
97
99
  // Make a swap request
98
100
  makeRequest: ['askForTokens', ({askForTokens}, cbk) => {
99
101
  return makeRequest({
100
- lnd,
101
- is_external_solo_key: true,
102
+ is_external_solo_key: !!args.request,
103
+ lnd: args.lnd,
102
104
  tokens: askForTokens,
103
105
  },
104
106
  cbk);
@@ -111,9 +113,9 @@ module.exports = ({ask, lnd, logger, request}, cbk) => {
111
113
  'makeRequest',
112
114
  ({getNetwork, makeRequest}, cbk) =>
113
115
  {
114
- logger.info({swap_request: makeRequest.request});
116
+ args.logger.info({swap_request: makeRequest.request});
115
117
 
116
- return ask({
118
+ return args.ask({
117
119
  message: 'Response to swap request?',
118
120
  name: 'response',
119
121
  validate: response => {
@@ -152,7 +154,7 @@ module.exports = ({ask, lnd, logger, request}, cbk) => {
152
154
  const fee = tokens - askForTokens;
153
155
  const pricing = `Execution cost ${deposit}, plus liquidity fee ${fee}`;
154
156
 
155
- return ask({
157
+ return args.ask({
156
158
  default: true,
157
159
  message: `Start swap ${timeout}? ${pricing}?`,
158
160
  name: 'ok',
@@ -176,15 +178,17 @@ module.exports = ({ask, lnd, logger, request}, cbk) => {
176
178
 
177
179
  const emitter = new EventEmitter();
178
180
 
179
- emitter.on('update', update => logger.info(update));
181
+ emitter.on('update', update => args.logger.info(update));
180
182
 
181
183
  return completeOffToOnSwap({
182
184
  emitter,
183
- lnd,
184
- request,
185
+ is_avoiding_broadcast: args.is_avoiding_broadcast,
186
+ is_uncooperative: args.is_uncooperative,
187
+ lnd: args.lnd,
185
188
  max_fee_deposit: defaultMaxFeeForDeposit,
186
189
  max_fee_funding: askForTokens * askForRate / rateDenominator,
187
190
  recovery: makeRequest.recovery,
191
+ request: args.request,
188
192
  response: getResponse,
189
193
  },
190
194
  cbk);
@@ -111,7 +111,7 @@ module.exports = ({ask, lnd, logger, request}, cbk) => {
111
111
  lnd,
112
112
  delta: defaultCltvDelta,
113
113
  deposit: ceil(getRate.tokens_per_vbyte * estimatedVirtualSize),
114
- is_external_solo_key: true,
114
+ is_external_solo_key: !!request,
115
115
  price: floor(tokens * askForRate / rateDenominator),
116
116
  request: askForRequest,
117
117
  },
@@ -14,7 +14,7 @@ const requestSwapOut = require('./../../swaps/request_swap_out');
14
14
  const respondToSwapOut = require('./../../swaps/respond_to_swap_out_request');
15
15
 
16
16
  const capacity = 1e6;
17
- const count = 6;
17
+ const count = 50;
18
18
  const interval = 100;
19
19
  const maturity = 100;
20
20
  const size = 2;
@@ -0,0 +1,152 @@
1
+ const asyncRetry = require('async/retry');
2
+ const {createChainAddress} = require('ln-service');
3
+ const {createInvoice} = require('ln-service');
4
+ const {getChannelBalance} = require('ln-service');
5
+ const {getChannels} = require('ln-service');
6
+ const {getMasterPublicKeys} = require('ln-service');
7
+ const {openChannel} = require('ln-service');
8
+ const {pay} = require('ln-service');
9
+ const {sendToChainAddress} = require('ln-service');
10
+ const {spawnLightningCluster} = require('ln-docker-daemons');
11
+ const {test} = require('@alexbosworth/tap');
12
+
13
+ const requestSwapOut = require('./../../swaps/request_swap_out');
14
+ const respondToSwapOut = require('./../../swaps/respond_to_swap_out_request');
15
+
16
+ const capacity = 1e6;
17
+ const count = 50;
18
+ const interval = 100;
19
+ const maturity = 100;
20
+ const size = 2;
21
+ const taprootDerivationPath = `m/86'/0'/0'`;
22
+ const times = 3000;
23
+ const tokens = 1e5;
24
+
25
+ // Start an offchain swap but use the claim path to sweep
26
+ test(`Swap with claim path`, async ({end, equal, strictSame}) => {
27
+ const {kill, nodes} = await spawnLightningCluster({size});
28
+
29
+ const [{generate, id, lnd}, target] = nodes;
30
+
31
+ const {keys} = await getMasterPublicKeys({lnd});
32
+
33
+ // Exit early when taproot is not supported
34
+ if (!keys.find(n => n.derivation_path === taprootDerivationPath)) {
35
+ await kill({});
36
+
37
+ return end();
38
+ }
39
+
40
+ try {
41
+ await generate({count: maturity});
42
+
43
+ // Setup a channel between the nodes
44
+ {
45
+ // Make a channel
46
+ await asyncRetry({interval, times}, async () => {
47
+ await openChannel({
48
+ lnd,
49
+ local_tokens: capacity,
50
+ partner_public_key: target.id,
51
+ partner_socket: target.socket,
52
+ });
53
+ });
54
+
55
+ await asyncRetry({interval, times}, async () => {
56
+ const {channels} = await getChannels({lnd, is_actve: true});
57
+
58
+ if (!!channels.length) {
59
+ return channels;
60
+ }
61
+
62
+ await generate({});
63
+
64
+ await sendToChainAddress({
65
+ lnd,
66
+ address: (await createChainAddress({lnd: target.lnd})).address,
67
+ tokens: capacity,
68
+ });
69
+
70
+ const balance = await getChannelBalance({lnd});
71
+
72
+ if (!balance.channel_balance) {
73
+ throw new Error('WaitingForChannelBalance');
74
+ }
75
+
76
+ throw new Error('WaitingForChannelOpen');
77
+ });
78
+
79
+ await target.generate({count: maturity});
80
+ }
81
+
82
+ // Collect request messages
83
+ const messages = [];
84
+
85
+ // Make a swap out request
86
+ await requestSwapOut({
87
+ lnd,
88
+ ask: async (args, cbk) => {
89
+ if (args.name === 'tokens') {
90
+ return cbk({tokens: '10000'});
91
+ }
92
+
93
+ if (args.name === 'rate') {
94
+ return cbk({rate: '10'});
95
+ }
96
+
97
+ const swapRequest = messages.find(n => !!n.swap_request);
98
+
99
+ if (args.name === 'response') {
100
+ return await respondToSwapOut({
101
+ ask: (args, cbk) => {
102
+ if (args.name === 'rate') {
103
+ return cbk({rate: '100'});
104
+ }
105
+
106
+ if (args.name === 'req') {
107
+ return cbk({req: swapRequest.swap_request});
108
+ }
109
+
110
+ throw new Error('UnrecognizedQueryForResponse');
111
+ },
112
+ lnd: target.lnd,
113
+ logger: {
114
+ info: async message => {
115
+ if (!!message.response) {
116
+ return cbk({response: message.response});
117
+ }
118
+
119
+ // Transaction is funded, generate funding into a block
120
+ if (!!message.funding_transaction_id) {
121
+ return await target.generate({count});
122
+ }
123
+ },
124
+ },
125
+ });
126
+ }
127
+
128
+ if (args.name === 'ok') {
129
+ return cbk({ok: true});
130
+ }
131
+
132
+ throw new Error('UnrecognizedQueryForRequest');
133
+ },
134
+ is_uncooperative: true,
135
+ logger: {
136
+ info: async message => {
137
+ if (message.broadcasting_tx_to_resolve_swap) {
138
+ await generate({count});
139
+ }
140
+
141
+ return messages.push(message);
142
+ },
143
+ },
144
+ });
145
+ } catch (err) {
146
+ strictSame(err, null, 'Expected no failure');
147
+ }
148
+
149
+ await kill({});
150
+
151
+ return end();
152
+ });
@@ -0,0 +1,164 @@
1
+ const asyncRetry = require('async/retry');
2
+ const {createChainAddress} = require('ln-service');
3
+ const {createInvoice} = require('ln-service');
4
+ const {getChannelBalance} = require('ln-service');
5
+ const {getChannels} = require('ln-service');
6
+ const {getMasterPublicKeys} = require('ln-service');
7
+ const {openChannel} = require('ln-service');
8
+ const {pay} = require('ln-service');
9
+ const {sendToChainAddress} = require('ln-service');
10
+ const {spawnLightningCluster} = require('ln-docker-daemons');
11
+ const {test} = require('@alexbosworth/tap');
12
+
13
+ const requestSwapOut = require('./../../swaps/request_swap_out');
14
+ const respondToSwapOut = require('./../../swaps/respond_to_swap_out_request');
15
+
16
+ const capacity = 1e6;
17
+ const count = 50;
18
+ const interval = 100;
19
+ const maturity = 100;
20
+ const size = 2;
21
+ const taprootDerivationPath = `m/86'/0'/0'`;
22
+ const times = 3000;
23
+ const tokens = 1e5;
24
+
25
+ // Start an offchain swap but do not cooperate and eventually force a refund
26
+ test(`Timeout a swap`, async ({end, equal, strictSame}) => {
27
+ const {kill, nodes} = await spawnLightningCluster({size});
28
+
29
+ const [{generate, id, lnd}, target] = nodes;
30
+
31
+ const {keys} = await getMasterPublicKeys({lnd});
32
+
33
+ // Exit early when taproot is not supported
34
+ if (!keys.find(n => n.derivation_path === taprootDerivationPath)) {
35
+ await kill({});
36
+
37
+ return end();
38
+ }
39
+
40
+ try {
41
+ await generate({count: maturity});
42
+
43
+ // Setup a channel between the nodes
44
+ {
45
+ // Make a channel
46
+ await asyncRetry({interval, times}, async () => {
47
+ await openChannel({
48
+ lnd,
49
+ local_tokens: capacity,
50
+ partner_public_key: target.id,
51
+ partner_socket: target.socket,
52
+ });
53
+ });
54
+
55
+ await asyncRetry({interval, times}, async () => {
56
+ const {channels} = await getChannels({lnd, is_actve: true});
57
+
58
+ if (!!channels.length) {
59
+ return channels;
60
+ }
61
+
62
+ await generate({});
63
+
64
+ await sendToChainAddress({
65
+ lnd,
66
+ address: (await createChainAddress({lnd: target.lnd})).address,
67
+ tokens: capacity,
68
+ });
69
+
70
+ const balance = await getChannelBalance({lnd});
71
+
72
+ if (!balance.channel_balance) {
73
+ throw new Error('WaitingForChannelBalance');
74
+ }
75
+
76
+ throw new Error('WaitingForChannelOpen');
77
+ });
78
+
79
+ await target.generate({count: maturity});
80
+ }
81
+
82
+ // Collect request messages
83
+ const messages = [];
84
+
85
+ // Make a swap out request
86
+ await requestSwapOut({
87
+ lnd,
88
+ ask: async (args, cbk) => {
89
+ if (args.name === 'tokens') {
90
+ return cbk({tokens: '10000'});
91
+ }
92
+
93
+ if (args.name === 'rate') {
94
+ return cbk({rate: '10'});
95
+ }
96
+
97
+ const swapRequest = messages.find(n => !!n.swap_request);
98
+
99
+ if (args.name === 'response') {
100
+ try {
101
+ return await respondToSwapOut({
102
+ ask: (args, cbk) => {
103
+ if (args.name === 'rate') {
104
+ return cbk({rate: '100'});
105
+ }
106
+
107
+ if (args.name === 'req') {
108
+ return cbk({req: swapRequest.swap_request});
109
+ }
110
+
111
+ throw new Error('UnrecognizedQueryForResponse');
112
+ },
113
+ lnd: target.lnd,
114
+ logger: {
115
+ info: async message => {
116
+ // Push chain forward to timeout
117
+ if (!!message.blocks_until_timeout) {
118
+ return await target.generate({});
119
+ }
120
+
121
+ if (!!message.response) {
122
+ return cbk({response: message.response});
123
+ }
124
+
125
+ // Transaction is funded, generate funding into a block
126
+ if (!!message.funding_transaction_id) {
127
+ return await target.generate({count});
128
+ }
129
+ },
130
+ },
131
+ });
132
+ } catch (err) {
133
+ strictSame(err, [503, 'SwapFailedViaTimeout']);
134
+
135
+ return;
136
+ }
137
+ }
138
+
139
+ if (args.name === 'ok') {
140
+ return cbk({ok: true});
141
+ }
142
+
143
+ throw new Error('UnrecognizedQueryForRequest');
144
+ },
145
+ is_avoiding_broadcast: true,
146
+ is_uncooperative: true,
147
+ logger: {
148
+ info: async message => {
149
+ if (message.broadcasting_tx_to_resolve_swap) {
150
+ await generate({count});
151
+ }
152
+
153
+ return messages.push(message);
154
+ },
155
+ },
156
+ });
157
+ } catch (err) {
158
+ strictSame(err, null, 'Expected no failure');
159
+ }
160
+
161
+ await kill({});
162
+
163
+ return end();
164
+ });