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 +1 -1
- package/package.json +1 -1
- package/swaps/complete_on_to_off_swap.js +2 -3
- package/swaps/manage_swap.js +45 -9
- package/swaps/recover_response_to_swap_out.js +1 -5
- package/swaps/request_swap_out.js +20 -16
- package/swaps/respond_to_swap_out_request.js +1 -1
- package/test/integration/test_request_swap_out.js +1 -1
- package/test/integration/test_swap_with_claim_path.js +152 -0
- package/test/integration/test_swap_with_refund_path.js +164 -0
package/CHANGELOG.md
CHANGED
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.
|
|
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
|
-
|
|
683
|
+
() => {});
|
|
685
684
|
});
|
|
686
685
|
|
|
687
686
|
subTimeout.on('error', err => done(err));
|
package/swaps/manage_swap.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const asyncAuto = require('async/auto');
|
|
2
|
-
const {
|
|
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({
|
|
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({
|
|
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: [
|
|
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({
|
|
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 = (
|
|
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
|
-
|
|
101
|
-
|
|
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
|
-
|
|
184
|
-
|
|
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:
|
|
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 =
|
|
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
|
+
});
|