paid-services 3.15.1 → 3.15.4

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.1
3
+ ## Version 3.15.4
4
4
 
5
5
  - `manageSwap`: Add method to execute submarine swaps on testnet
6
6
 
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "ecpair": "2.0.1",
17
17
  "goldengate": "11.2.1",
18
18
  "invoices": "2.0.6",
19
- "ln-service": "53.11.0",
19
+ "ln-service": "53.15.0",
20
20
  "ln-sync": "3.12.0",
21
21
  "psbt": "2.0.1",
22
22
  "p2tr": "1.3.1",
@@ -25,7 +25,7 @@
25
25
  "description": "Lightning Paid Services library",
26
26
  "devDependencies": {
27
27
  "@alexbosworth/tap": "15.0.11",
28
- "ln-docker-daemons": "2.2.7",
28
+ "ln-docker-daemons": "2.2.9",
29
29
  "mock-lnd": "1.4.1",
30
30
  "secp256k1": "4.0.3"
31
31
  },
@@ -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.1"
50
+ "version": "3.15.4"
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,8 @@ 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'`;
12
+ const minConfirmations = {btc: 2, btcregtest: 3, btctestnet: 1};
10
13
  const recoverRespondAction = 'recover-response';
11
14
  const requestAction = 'request';
12
15
  const respondAction = 'respond';
@@ -46,13 +49,25 @@ module.exports = ({ask, lnd, logger, request}, cbk) => {
46
49
  return cbk();
47
50
  },
48
51
 
52
+ // Look for a p2tr master public key to detect p2tr support
53
+ getMasterPublicKeys: ['validate', ({}, cbk) => {
54
+ return getMasterPublicKeys({lnd}, cbk);
55
+ }],
56
+
49
57
  // Get the network to make sure this is on testnet
50
58
  getNetwork: ['validate', ({}, cbk) => getNetwork({lnd}, cbk)],
51
59
 
60
+ // Determine if taproot is supported or not
61
+ hasTr: ['getMasterPublicKeys', ({getMasterPublicKeys}, cbk) => {
62
+ const {keys} = getMasterPublicKeys;
63
+
64
+ return cbk(null, !!keys.find(n => n.derivation_path === bip86Path));
65
+ }],
66
+
52
67
  // Select a swap action
53
- selectAction: ['getNetwork', ({getNetwork}, cbk) => {
68
+ selectAction: ['getNetwork', 'hasTr', ({getNetwork, hasTr}, cbk) => {
54
69
  // Check to make sure the network is supported
55
- if (getNetwork.network !== allowedNetwork) {
70
+ if (!hasTr && getNetwork.network !== allowedNetwork) {
56
71
  return cbk([501, 'CurrentlyUnsupportedChainForSwap']);
57
72
  }
58
73
 
@@ -79,30 +94,53 @@ module.exports = ({ask, lnd, logger, request}, cbk) => {
79
94
  }],
80
95
 
81
96
  // Make swap request
82
- makeRequest: ['selectAction', ({selectAction}, cbk) => {
97
+ makeRequest: ['hasTr', 'selectAction', ({hasTr, selectAction}, cbk) => {
83
98
  if (selectAction !== requestAction) {
84
99
  return cbk();
85
100
  }
86
101
 
87
- return requestSwapOut({ask, lnd, logger, request}, cbk);
102
+ return requestSwapOut({
103
+ ask,
104
+ lnd,
105
+ logger,
106
+ min_confirmations: minConfirmations[getNetwork.network],
107
+ request: !hasTr ? request : undefined,
108
+ },
109
+ cbk);
88
110
  }],
89
111
 
90
112
  // Respond to swap request
91
- makeResponse: ['selectAction', ({selectAction}, cbk) => {
113
+ makeResponse: ['hasTr', 'selectAction', ({hasTr, selectAction}, cbk) => {
92
114
  if (selectAction !== respondAction) {
93
115
  return cbk();
94
116
  }
95
117
 
96
- return respondToSwapOutRequest({ask, lnd, logger, request}, cbk);
118
+ return respondToSwapOutRequest({
119
+ ask,
120
+ lnd,
121
+ logger,
122
+ request: !hasTr ? request : undefined,
123
+ },
124
+ cbk);
97
125
  }],
98
126
 
99
127
  // Recover responding to swap request
100
- recoverResponse: ['selectAction', ({selectAction}, cbk) => {
128
+ recoverResponse: [
129
+ 'hasTr',
130
+ 'selectAction',
131
+ ({hasTr, selectAction}, cbk) =>
132
+ {
101
133
  if (selectAction !== recoverRespondAction) {
102
134
  return cbk();
103
135
  }
104
136
 
105
- return recoverResponseToSwapOut({ask, lnd, logger, request}, cbk);
137
+ return recoverResponseToSwapOut({
138
+ ask,
139
+ lnd,
140
+ logger,
141
+ request: !hasTr ? request : undefined,
142
+ },
143
+ cbk);
106
144
  }],
107
145
 
108
146
  // 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,30 @@ 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
+ [min_confirmations]: <Minimum Confirmations to Wait Number>
30
+ [request]: <Request Function>
28
31
  }
29
32
 
30
33
  @returns via cbk or Promise
31
34
  */
32
- module.exports = ({ask, lnd, logger, request}, cbk) => {
35
+ module.exports = (args, cbk) => {
33
36
  return new Promise((resolve, reject) => {
34
37
  return asyncAuto({
35
38
  // Check arguments
36
39
  validate: cbk => {
37
- if (!ask) {
40
+ if (!args.ask) {
38
41
  return cbk([400, 'ExpectedAskFunctionToRequestSwapOut']);
39
42
  }
40
43
 
41
- if (!lnd) {
44
+ if (!args.lnd) {
42
45
  return cbk([400, 'ExpectedAuthenticatedLndToRequestSwapOut']);
43
46
  }
44
47
 
45
- if (!logger) {
48
+ if (!args.logger) {
46
49
  return cbk([400, 'ExpectedWinstonLoggerToRequestSwapOut']);
47
50
  }
48
51
 
@@ -51,7 +54,7 @@ module.exports = ({ask, lnd, logger, request}, cbk) => {
51
54
 
52
55
  // Ask for amount
53
56
  askForTokens: ['validate', ({}, cbk) => {
54
- return ask({
57
+ return args.ask({
55
58
  default: defaultSwapAmount,
56
59
  message: 'Amount to swap?',
57
60
  name: 'tokens',
@@ -71,11 +74,11 @@ module.exports = ({ask, lnd, logger, request}, cbk) => {
71
74
  }],
72
75
 
73
76
  // Get the network name
74
- getNetwork: ['validate', ({}, cbk) => getNetwork({lnd}, cbk)],
77
+ getNetwork: ['validate', ({}, cbk) => getNetwork({lnd: args.lnd}, cbk)],
75
78
 
76
79
  // Ask for routing fee rate
77
80
  askForRate: ['askForTokens', ({}, cbk) => {
78
- return ask({
81
+ return args.ask({
79
82
  default: defaultFeeRate,
80
83
  message: 'Max routing fee rate for swap funds in parts per million?',
81
84
  name: 'rate',
@@ -97,8 +100,8 @@ module.exports = ({ask, lnd, logger, request}, cbk) => {
97
100
  // Make a swap request
98
101
  makeRequest: ['askForTokens', ({askForTokens}, cbk) => {
99
102
  return makeRequest({
100
- lnd,
101
- is_external_solo_key: true,
103
+ is_external_solo_key: !!args.request,
104
+ lnd: args.lnd,
102
105
  tokens: askForTokens,
103
106
  },
104
107
  cbk);
@@ -111,9 +114,9 @@ module.exports = ({ask, lnd, logger, request}, cbk) => {
111
114
  'makeRequest',
112
115
  ({getNetwork, makeRequest}, cbk) =>
113
116
  {
114
- logger.info({swap_request: makeRequest.request});
117
+ args.logger.info({swap_request: makeRequest.request});
115
118
 
116
- return ask({
119
+ return args.ask({
117
120
  message: 'Response to swap request?',
118
121
  name: 'response',
119
122
  validate: response => {
@@ -152,7 +155,7 @@ module.exports = ({ask, lnd, logger, request}, cbk) => {
152
155
  const fee = tokens - askForTokens;
153
156
  const pricing = `Execution cost ${deposit}, plus liquidity fee ${fee}`;
154
157
 
155
- return ask({
158
+ return args.ask({
156
159
  default: true,
157
160
  message: `Start swap ${timeout}? ${pricing}?`,
158
161
  name: 'ok',
@@ -176,15 +179,18 @@ module.exports = ({ask, lnd, logger, request}, cbk) => {
176
179
 
177
180
  const emitter = new EventEmitter();
178
181
 
179
- emitter.on('update', update => logger.info(update));
182
+ emitter.on('update', update => args.logger.info(update));
180
183
 
181
184
  return completeOffToOnSwap({
182
185
  emitter,
183
- lnd,
184
- request,
186
+ is_avoiding_broadcast: args.is_avoiding_broadcast,
187
+ is_uncooperative: args.is_uncooperative,
188
+ lnd: args.lnd,
185
189
  max_fee_deposit: defaultMaxFeeForDeposit,
186
190
  max_fee_funding: askForTokens * askForRate / rateDenominator,
191
+ min_confirmations: args.min_confirmations,
187
192
  recovery: makeRequest.recovery,
193
+ request: args.request,
188
194
  response: getResponse,
189
195
  },
190
196
  cbk);
@@ -23,7 +23,7 @@ const rateDenominator = 1e6;
23
23
  ask: <Ask Function>
24
24
  lnd: <Authenticated LND API Object>
25
25
  logger: <Winston Logger Object>
26
- request: <Request Function>
26
+ [request]: <Request Function>
27
27
  }
28
28
 
29
29
  @returns via cbk or Promise
@@ -45,10 +45,6 @@ module.exports = ({ask, lnd, logger, request}, cbk) => {
45
45
  return cbk([400, 'ExpectedLoggerToRespondToSwapOutRequest']);
46
46
  }
47
47
 
48
- if (!request) {
49
- return cbk([400, 'ExpectedRequestFunctionToRespondToSwapOutReq']);
50
- }
51
-
52
48
  return cbk();
53
49
  },
54
50
 
@@ -115,7 +111,7 @@ module.exports = ({ask, lnd, logger, request}, cbk) => {
115
111
  lnd,
116
112
  delta: defaultCltvDelta,
117
113
  deposit: ceil(getRate.tokens_per_vbyte * estimatedVirtualSize),
118
- is_external_solo_key: true,
114
+ is_external_solo_key: !!request,
119
115
  price: floor(tokens * askForRate / rateDenominator),
120
116
  request: askForRequest,
121
117
  },
@@ -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
26
+ test(`Start offchain 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
+ 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
+ logger: {
135
+ info: async message => {
136
+ if (message.broadcasting_tx_to_resolve_swap) {
137
+ await generate({count});
138
+ }
139
+
140
+ return messages.push(message);
141
+ },
142
+ },
143
+ min_confirmations: 3,
144
+ });
145
+ } catch (err) {
146
+ strictSame(err, null, 'Expected no failure');
147
+ }
148
+
149
+ await kill({});
150
+
151
+ return end();
152
+ });
@@ -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
+ });