ln-service 54.1.2 → 54.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Versions
2
2
 
3
+ ## 54.2.0
4
+
5
+ - `openChannel`, `openChannels`: Add `base_fee_mtokens`, `fee_rate` to set
6
+ initial routing fee rate.
7
+
3
8
  ## 54.1.2
4
9
 
5
10
  - `subscribeToRpcRequests`: Fix support for LND 0.15.1
package/README.md CHANGED
@@ -3571,9 +3571,14 @@ If give_tokens is set, it is a gift and it does not alter the capacity
3571
3571
 
3572
3572
  Requires `offchain:write`, `onchain:write`, `peers:write` permissions
3573
3573
 
3574
+ `base_fee_mtokens` is not supported on LND 0.15.2 and below
3575
+ `fee_rate` is not supported on LND 0.15.2 and below
3576
+
3574
3577
  {
3578
+ [base_fee_mtokens]: <Routing Base Fee Millitokens Charged String>
3575
3579
  [chain_fee_tokens_per_vbyte]: <Chain Fee Tokens Per VByte Number>
3576
3580
  [cooperative_close_address]: <Restrict Cooperative Close To Address String>
3581
+ [fee_rate]: <Routing Fee Rate In Millitokens Per Million Number>
3577
3582
  [give_tokens]: <Tokens to Gift To Partner Number> // Defaults to zero
3578
3583
  [is_private]: <Channel is Private Bool> // Defaults to false
3579
3584
  lnd: <Authenticated LND API Object>
@@ -3619,10 +3624,15 @@ after the funding step.
3619
3624
  `--protocol.option-scid-alias` and `--protocol.zero-conf` set on both sides
3620
3625
  as well as a channel open request listener to accept the trusted funding.
3621
3626
 
3627
+ `base_fee_mtokens` is not supported on LND 0.15.2 and below
3628
+ `fee_rate` is not supported on LND 0.15.2 and below
3629
+
3622
3630
  {
3623
3631
  channels: [{
3632
+ [base_fee_mtokens]: <Routing Base Fee Millitokens Charged String>
3624
3633
  capacity: <Channel Capacity Tokens Number>
3625
3634
  [cooperative_close_address]: <Restrict Coop Close To Address String>
3635
+ [fee_rate]: <Routing Fee Rate In Millitokens Per Million Number>
3626
3636
  [give_tokens]: <Tokens to Gift To Partner Number> // Defaults to zero
3627
3637
  [is_private]: <Channel is Private Bool> // Defaults to false
3628
3638
  [is_trusted_funding]: <Peer Should Avoid Waiting For Confirmation Bool>
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "cors": "2.8.5",
12
12
  "express": "4.18.1",
13
13
  "invoices": "2.2.0",
14
- "lightning": "6.1.2",
14
+ "lightning": "6.2.0",
15
15
  "macaroon": "3.0.4",
16
16
  "morgan": "1.10.0",
17
17
  "ws": "8.9.0"
@@ -28,7 +28,7 @@
28
28
  "bn.js": "5.2.1",
29
29
  "bs58check": "2.1.2",
30
30
  "ecpair": "2.1.0",
31
- "ln-docker-daemons": "3.0.1",
31
+ "ln-docker-daemons": "3.1.0",
32
32
  "p2tr": "1.3.2",
33
33
  "portfinder": "1.0.32",
34
34
  "psbt": "2.7.1",
@@ -71,5 +71,5 @@
71
71
  "integration-test-0.12.0": "DOCKER_LND_VERSION=v0.12.0-beta npm run test",
72
72
  "test": "echo $DOCKER_LND_VERSION && tap -j 2 --branches=1 --functions=1 --lines=1 --statements=1 -t 200 test/autopilotrpc-integration/*.js test/chainrpc-integration/*.js test/integration/*.js test/invoicesrpc-integration/*.js test/peersrpc-integration/*.js test/routerrpc-integration/*.js test/signerrpc-integration/*.js test/tower_clientrpc-integration/*.js test/tower_serverrpc-integration/*.js test/walletrpc-integration/*.js"
73
73
  },
74
- "version": "54.1.2"
74
+ "version": "54.2.0"
75
75
  }
@@ -14,7 +14,7 @@ const {subscribeToBlocks} = require('./../../');
14
14
  const {waitForTermination} = require('./../macros');
15
15
 
16
16
  const confirmationCount = 6;
17
- const interval = 1;
17
+ const interval = 10;
18
18
  const race = promises => Promise.race(promises);
19
19
  const times = 4000;
20
20
 
@@ -1,19 +1,15 @@
1
+ const {spawnLightningCluster} = require('ln-docker-daemons');
1
2
  const {test} = require('@alexbosworth/tap');
2
3
 
3
- const {authenticatedLndGrpc} = require('./../../');
4
4
  const {createChainAddress} = require('./../../');
5
5
  const {getWalletVersion} = require('./../../');
6
6
  const {grantAccess} = require('./../../');
7
- const {spawnLnd} = require('./../macros');
8
- const {waitForTermination} = require('./../macros');
9
7
 
10
8
  const format = 'np2wpkh';
11
9
 
12
10
  // Granting access should result in access granted
13
11
  test(`Get access credentials`, async ({end, equal, rejects, strictSame}) => {
14
- const spawned = await spawnLnd({});
15
-
16
- const {lnd, kill} = spawned;
12
+ const [{lnd, kill, rpc}] = (await spawnLightningCluster({})).nodes;
17
13
 
18
14
  await grantAccess({lnd, is_ok_to_create_chain_addresses: true});
19
15
 
@@ -27,17 +23,11 @@ test(`Get access credentials`, async ({end, equal, rejects, strictSame}) => {
27
23
 
28
24
  strictSame(makeChainAddresses.permissions, permissions, 'Got permissions');
29
25
 
30
- const canPay = authenticatedLndGrpc({
31
- cert: spawned.lnd_cert,
26
+ const canPay = rpc({
32
27
  macaroon: (await grantAccess({lnd, is_ok_to_pay: true})).macaroon,
33
- socket: `localhost:${spawned.rpc_port}`,
34
28
  });
35
29
 
36
- const makeAddress = authenticatedLndGrpc({
37
- cert: spawned.lnd_cert,
38
- macaroon: makeChainAddresses.macaroon,
39
- socket: `localhost:${spawned.rpc_port}`,
40
- });
30
+ const makeAddress = rpc({macaroon: makeChainAddresses.macaroon});
41
31
 
42
32
  await rejects(
43
33
  grantAccess({lnd: makeAddress.lnd, is_ok_to_create_chain_addresses: true}),
@@ -64,10 +54,8 @@ test(`Get access credentials`, async ({end, equal, rejects, strictSame}) => {
64
54
  methods: ['createChainAddress'],
65
55
  });
66
56
 
67
- const authenticatedToCreateAddress = authenticatedLndGrpc({
68
- cert: spawned.lnd_cert,
57
+ const authenticatedToCreateAddress = rpc({
69
58
  macaroon: createChainAddressCredential.macaroon,
70
- socket: `localhost:${spawned.rpc_port}`,
71
59
  });
72
60
 
73
61
  const created = await createChainAddress({
@@ -78,9 +66,7 @@ test(`Get access credentials`, async ({end, equal, rejects, strictSame}) => {
78
66
  equal(!!created, true, 'Can make address with URI credential');
79
67
  }
80
68
 
81
- kill();
82
-
83
- await waitForTermination({lnd});
69
+ await kill({});
84
70
 
85
71
  return end();
86
72
  });
@@ -5,12 +5,17 @@ const {test} = require('@alexbosworth/tap');
5
5
  const {addPeer} = require('./../../');
6
6
  const {createChainAddress} = require('./../../');
7
7
  const {getChainBalance} = require('./../../');
8
+ const {getChannel} = require('./../../');
9
+ const {getChannels} = require('./../../');
8
10
  const {openChannel} = require('./../../');
9
11
 
12
+ const baseFee = '1337';
10
13
  const channelCapacityTokens = 1e6;
11
14
  const count = 100;
15
+ const defaultBaseFee = '1000';
12
16
  const defaultFee = 1e3;
13
17
  const defaultVout = 0;
18
+ const feeRate = 420;
14
19
  const giftTokens = 1000;
15
20
  const interval = 250;
16
21
  const size = 2;
@@ -21,7 +26,7 @@ const txIdHexLength = 32 * 2;
21
26
  test(`Open channel`, async ({end, equal}) => {
22
27
  const {kill, nodes} = await spawnLightningCluster({size});
23
28
 
24
- const [{generate, lnd}, target] = nodes;
29
+ const [{generate, id, lnd}, target] = nodes;
25
30
 
26
31
  const {address} = await createChainAddress({lnd});
27
32
 
@@ -32,8 +37,10 @@ test(`Open channel`, async ({end, equal}) => {
32
37
 
33
38
  return await openChannel({
34
39
  lnd,
40
+ base_fee_mtokens: baseFee,
35
41
  chain_fee_tokens_per_vbyte: defaultFee,
36
42
  cooperative_close_address: address,
43
+ fee_rate: feeRate,
37
44
  give_tokens: giftTokens,
38
45
  local_tokens: channelCapacityTokens,
39
46
  partner_public_key: target.id,
@@ -44,6 +51,30 @@ test(`Open channel`, async ({end, equal}) => {
44
51
  equal(channelOpen.transaction_id.length, txIdHexLength, 'Channel tx id');
45
52
  equal(channelOpen.transaction_vout, defaultVout, 'Channel tx output index');
46
53
 
54
+ await asyncRetry({interval, times}, async () => {
55
+ await generate({});
56
+
57
+ const {channels} = await getChannels({lnd});
58
+
59
+ const [channel] = channels;
60
+
61
+ if (!channel) {
62
+ throw new Error('ExpectedChannelOpened');
63
+ }
64
+
65
+ const {policies} = await getChannel({lnd, id: channel.id});
66
+
67
+ const policy = policies.find(n => n.public_key === id);
68
+
69
+ // LND 0.15.2 and below do not support setting fees on open
70
+ if (policy.base_fee_mtokens === defaultBaseFee) {
71
+ return;
72
+ }
73
+
74
+ equal(policy.base_fee_mtokens, baseFee, 'Base fee is set');
75
+ equal(policy.fee_rate, feeRate, 'Fee rate is set');
76
+ });
77
+
47
78
  await kill({});
48
79
 
49
80
  return end();
@@ -13,15 +13,19 @@ const {delay} = require('./../macros');
13
13
  const {fundPendingChannels} = require('./../../');
14
14
  const {getChainBalance} = require('./../../');
15
15
  const {getChainTransactions} = require('./../../');
16
+ const {getChannel} = require('./../../');
16
17
  const {getChannels} = require('./../../');
17
18
  const {getHeight} = require('./../../');
18
19
  const {getPeers} = require('./../../');
19
20
  const {openChannels} = require('./../../');
20
21
  const {sendToChainAddresses} = require('./../../');
21
22
 
23
+ const baseFeeMtokens = '1234';
22
24
  const capacity = 1e6;
23
25
  const count = 10;
26
+ const defaultBaseFee = '1000';
24
27
  const interval = 10;
28
+ const feeRate = 56;
25
29
  const maturity = 100;
26
30
  const race = promises => Promise.race(promises);
27
31
  const size = 3;
@@ -61,7 +65,9 @@ test(`Open channels`, async ({end, equal}) => {
61
65
 
62
66
  const channels = [target, remote].map(({id}) => ({
63
67
  capacity,
68
+ base_fee_mtokens: baseFeeMtokens,
64
69
  cooperative_close_address: address,
70
+ fee_rate: feeRate,
65
71
  partner_public_key: id,
66
72
  }));
67
73
 
@@ -129,6 +135,18 @@ test(`Open channels`, async ({end, equal}) => {
129
135
 
130
136
  equal(channel.cooperative_close_address, address, 'Channel close addr');
131
137
 
138
+ const {policies} = await getChannel({lnd, id: channel.id});
139
+
140
+ const policy = policies.find(n => !!n.cltv_delta);
141
+
142
+ // LND 0.15.2 and below do not support setting fees on open
143
+ if (policy.base_fee_mtokens === defaultBaseFee) {
144
+ return;
145
+ }
146
+
147
+ equal(policy.base_fee_mtokens, baseFeeMtokens, 'Base fee is set');
148
+ equal(policy.fee_rate, feeRate, 'Fee rate is set');
149
+
132
150
  return;
133
151
  });
134
152
  } catch (err) {
@@ -0,0 +1,196 @@
1
+ const asyncRetry = require('async/retry');
2
+ const {getPortPromise: getPort} = require('portfinder');
3
+ const {setupChannel} = require('ln-docker-daemons');
4
+ const {spawnLightningDocker} = require('ln-docker-daemons');
5
+ const {test} = require('@alexbosworth/tap');
6
+
7
+ const {addPeer} = require('./../../');
8
+ const {authenticatedLndGrpc} = require('./../../');
9
+ const {createChainAddress} = require('./../../');
10
+ const {getBackup} = require('./../../');
11
+ const {getIdentity} = require('./../../');
12
+ const {getPendingChannels} = require('./../../');
13
+ const {getUtxos} = require('./../../');
14
+ const {recoverFundsFromChannel} = require('./../../');
15
+
16
+ const confirmationCount = 20;
17
+ const generateAddress = '2N8hwP1WmJrFF5QWABn38y63uYLhnJYJYTF';
18
+ const giftTokens = 1e5;
19
+ const interval = 10;
20
+ const makeAddress = ({lnd}) => createChainAddress({lnd});
21
+ const maturity = 100;
22
+ const seed = 'about rabbit ozone hope jaguar quit scare twenty punch crisp consider clutch ring frost universe okay execute shrug drink notice abandon wine denial retreat';
23
+ const times = 3000;
24
+
25
+ // Using a channel backup should recover funds
26
+ test(`Recover funds from channel`, async ({end, equal}) => {
27
+ const control = await spawnLightningDocker({
28
+ seed,
29
+ chain_p2p_port: await getPort({port: 8000, stopPort: 9000}),
30
+ chain_rpc_port: await getPort({port: 9001, stopPort: 10000}),
31
+ chain_zmq_block_port: await getPort({port: 10001, stopPort: 11000}),
32
+ chain_zmq_tx_port: await getPort({port: 11001, stopPort: 12000}),
33
+ generate_address: generateAddress,
34
+ lightning_p2p_port: await getPort({port: 12001, stopPort: 13000}),
35
+ lightning_rpc_port: await getPort({port: 13001, stopPort: 14000}),
36
+ });
37
+
38
+ const target = await spawnLightningDocker({
39
+ chain_p2p_port: await getPort({port: 8000, stopPort: 9000}),
40
+ chain_rpc_port: await getPort({port: 9001, stopPort: 10000}),
41
+ chain_zmq_block_port: await getPort({port: 10001, stopPort: 11000}),
42
+ chain_zmq_tx_port: await getPort({port: 11001, stopPort: 12000}),
43
+ generate_address: generateAddress,
44
+ lightning_p2p_port: await getPort({port: 12001, stopPort: 13000}),
45
+ lightning_rpc_port: await getPort({port: 13001, stopPort: 14000}),
46
+ });
47
+
48
+ await control.add_chain_peer({socket: target.chain_socket});
49
+ await target.add_chain_peer({socket: control.chain_socket});
50
+
51
+ const controlLnd = authenticatedLndGrpc({
52
+ cert: control.cert,
53
+ macaroon: control.macaroon,
54
+ socket: control.socket,
55
+ });
56
+
57
+ const id = (await getIdentity({lnd: controlLnd.lnd})).public_key;
58
+
59
+ const targetLnd = authenticatedLndGrpc({
60
+ cert: target.cert,
61
+ macaroon: target.macaroon,
62
+ socket: target.socket,
63
+ });
64
+
65
+ const targetId = (await getIdentity({lnd: targetLnd.lnd})).public_key;
66
+
67
+ const channelOpen = await setupChannel({
68
+ generate: ({address, count}) => {
69
+ return new Promise(async (resolve, reject) => {
70
+ const {lnd} = targetLnd;
71
+
72
+ await target.generate({
73
+ count,
74
+ address: (await makeAddress({lnd})).address,
75
+ });
76
+
77
+ if (!count || count < maturity) {
78
+ return resolve();
79
+ }
80
+
81
+ await asyncRetry({interval, times}, async () => {
82
+ const [utxo] = (await getUtxos({lnd})).utxos;
83
+
84
+ if (!utxo) {
85
+ throw new Error('ExpectedUtxoInUtxos');
86
+ }
87
+ });
88
+
89
+ return resolve();
90
+ });
91
+ },
92
+ give_tokens: giftTokens,
93
+ lnd: targetLnd.lnd,
94
+ to: {id, socket: control.ln_socket},
95
+ });
96
+
97
+ const {backup} = await getBackup({
98
+ lnd: controlLnd.lnd,
99
+ transaction_id: channelOpen.transaction_id,
100
+ transaction_vout: channelOpen.transaction_vout,
101
+ });
102
+
103
+ await control.kill({});
104
+
105
+ const clone = await spawnLightningDocker({
106
+ seed,
107
+ chain_p2p_port: await getPort({port: 8000, stopPort: 9000}),
108
+ chain_rpc_port: await getPort({port: 9001, stopPort: 10000}),
109
+ chain_zmq_block_port: await getPort({port: 10001, stopPort: 11000}),
110
+ chain_zmq_tx_port: await getPort({port: 11001, stopPort: 12000}),
111
+ generate_address: generateAddress,
112
+ lightning_p2p_port: await getPort({port: 12001, stopPort: 13000}),
113
+ lightning_rpc_port: await getPort({port: 13001, stopPort: 14000}),
114
+ });
115
+
116
+ await clone.add_chain_peer({socket: target.chain_socket});
117
+
118
+ await target.add_chain_peer({socket: clone.chain_socket});
119
+
120
+ const cloneLnd = authenticatedLndGrpc({
121
+ cert: clone.cert,
122
+ macaroon: clone.macaroon,
123
+ socket: clone.socket,
124
+ });
125
+
126
+ await addPeer({
127
+ lnd: cloneLnd.lnd,
128
+ public_key: targetId,
129
+ socket: target.ln_socket,
130
+ });
131
+
132
+ await asyncRetry({interval, times}, async () => {
133
+ await recoverFundsFromChannel({backup, lnd: cloneLnd.lnd});
134
+ });
135
+
136
+ await clone.generate({
137
+ address: generateAddress,
138
+ count: confirmationCount,
139
+ });
140
+
141
+ await target.generate({
142
+ address: generateAddress,
143
+ count: confirmationCount,
144
+ });
145
+
146
+ // Target should force close the channel
147
+ {
148
+ const {lnd} = targetLnd;
149
+
150
+ await asyncRetry({interval, times}, async () => {
151
+ await addPeer({
152
+ lnd: cloneLnd.lnd,
153
+ public_key: targetId,
154
+ socket: target.ln_socket,
155
+ });
156
+
157
+ const [chan] = (await getPendingChannels({lnd})).pending_channels;
158
+
159
+ if (!chan) {
160
+ throw new Error('ExpectedChannelClosing');
161
+ }
162
+ });
163
+ }
164
+
165
+ // Make sure that the clone is getting the recovered funds
166
+ {
167
+ const {lnd} = cloneLnd;
168
+
169
+ await asyncRetry({interval, times}, async () => {
170
+ const [chan] = (await getPendingChannels({lnd})).pending_channels;
171
+
172
+ await target.generate({address: generateAddress});
173
+
174
+ if (!chan.local_balance) {
175
+ throw new Error('ExpectedChannelClosingBalance');
176
+ }
177
+ });
178
+
179
+ const [chan] = (await getPendingChannels({lnd})).pending_channels;
180
+
181
+ equal(!!chan.close_transaction_id, true, 'Close transaction id found');
182
+ equal(chan.is_active, false, 'Chan no longer active');
183
+ equal(chan.is_closing, true, 'Channel is closing');
184
+ equal(chan.is_opening, false, 'Channel closing');
185
+ equal(chan.local_balance, giftTokens, 'Funds are being restored');
186
+ equal(chan.partner_public_key, targetId, 'Peer key');
187
+ equal(chan.transaction_id, channelOpen.transaction_id, 'Chan tx id');
188
+ equal(chan.transaction_vout, channelOpen.transaction_vout, 'Chan tx vout');
189
+ }
190
+
191
+ await clone.kill({});
192
+
193
+ await target.kill({});
194
+
195
+ return end();
196
+ });
@@ -0,0 +1,198 @@
1
+ const asyncRetry = require('async/retry');
2
+ const {getPortPromise: getPort} = require('portfinder');
3
+ const {setupChannel} = require('ln-docker-daemons');
4
+ const {spawnLightningDocker} = require('ln-docker-daemons');
5
+ const {test} = require('@alexbosworth/tap');
6
+
7
+ const {addPeer} = require('./../../');
8
+ const {authenticatedLndGrpc} = require('./../../');
9
+ const {createChainAddress} = require('./../../');
10
+ const {getBackups} = require('./../../');
11
+ const {getIdentity} = require('./../../');
12
+ const {getPendingChannels} = require('./../../');
13
+ const {getUtxos} = require('./../../');
14
+ const {recoverFundsFromChannels} = require('./../../');
15
+
16
+ const confirmationCount = 20;
17
+ const generateAddress = '2N8hwP1WmJrFF5QWABn38y63uYLhnJYJYTF';
18
+ const giftTokens = 1e5;
19
+ const interval = 10;
20
+ const makeAddress = ({lnd}) => createChainAddress({lnd});
21
+ const maturity = 100;
22
+ const seed = 'about rabbit ozone hope jaguar quit scare twenty punch crisp consider clutch ring frost universe okay execute shrug drink notice abandon wine denial retreat';
23
+ const times = 3000;
24
+
25
+ // Using a channels backup should recover funds
26
+ test(`Recover funds from channels`, async ({end, equal}) => {
27
+ const control = await asyncRetry({interval, times}, async () => {
28
+ return await spawnLightningDocker({
29
+ seed,
30
+ chain_p2p_port: await getPort({port: 8000, stopPort: 9000}),
31
+ chain_rpc_port: await getPort({port: 9001, stopPort: 10000}),
32
+ chain_zmq_block_port: await getPort({port: 10001, stopPort: 11000}),
33
+ chain_zmq_tx_port: await getPort({port: 11001, stopPort: 12000}),
34
+ generate_address: generateAddress,
35
+ lightning_p2p_port: await getPort({port: 12001, stopPort: 13000}),
36
+ lightning_rpc_port: await getPort({port: 13001, stopPort: 14000}),
37
+ });
38
+ });
39
+
40
+ const target = await asyncRetry({interval, times}, async () => {
41
+ return await spawnLightningDocker({
42
+ chain_p2p_port: await getPort({port: 8000, stopPort: 9000}),
43
+ chain_rpc_port: await getPort({port: 9001, stopPort: 10000}),
44
+ chain_zmq_block_port: await getPort({port: 10001, stopPort: 11000}),
45
+ chain_zmq_tx_port: await getPort({port: 11001, stopPort: 12000}),
46
+ generate_address: generateAddress,
47
+ lightning_p2p_port: await getPort({port: 12001, stopPort: 13000}),
48
+ lightning_rpc_port: await getPort({port: 13001, stopPort: 14000}),
49
+ });
50
+ });
51
+
52
+ await control.add_chain_peer({socket: target.chain_socket});
53
+ await target.add_chain_peer({socket: control.chain_socket});
54
+
55
+ const controlLnd = authenticatedLndGrpc({
56
+ cert: control.cert,
57
+ macaroon: control.macaroon,
58
+ socket: control.socket,
59
+ });
60
+
61
+ const id = (await getIdentity({lnd: controlLnd.lnd})).public_key;
62
+
63
+ const targetLnd = authenticatedLndGrpc({
64
+ cert: target.cert,
65
+ macaroon: target.macaroon,
66
+ socket: target.socket,
67
+ });
68
+
69
+ const targetId = (await getIdentity({lnd: targetLnd.lnd})).public_key;
70
+
71
+ const channelOpen = await setupChannel({
72
+ generate: ({address, count}) => {
73
+ return new Promise(async (resolve, reject) => {
74
+ const {lnd} = targetLnd;
75
+
76
+ await target.generate({
77
+ count,
78
+ address: (await makeAddress({lnd})).address,
79
+ });
80
+
81
+ if (!count || count < maturity) {
82
+ return resolve();
83
+ }
84
+
85
+ await asyncRetry({interval, times}, async () => {
86
+ const [utxo] = (await getUtxos({lnd})).utxos;
87
+
88
+ if (!utxo) {
89
+ throw new Error('ExpectedUtxoInUtxos');
90
+ }
91
+ });
92
+
93
+ return resolve();
94
+ });
95
+ },
96
+ give_tokens: giftTokens,
97
+ lnd: targetLnd.lnd,
98
+ to: {id, socket: control.ln_socket},
99
+ });
100
+
101
+ const {backup} = await getBackups({lnd: controlLnd.lnd});
102
+
103
+ await control.kill({});
104
+
105
+ const clone = await asyncRetry({interval, times}, async () => {
106
+ return await spawnLightningDocker({
107
+ seed,
108
+ chain_p2p_port: await getPort({port: 8000, stopPort: 9000}),
109
+ chain_rpc_port: await getPort({port: 9001, stopPort: 10000}),
110
+ chain_zmq_block_port: await getPort({port: 10001, stopPort: 11000}),
111
+ chain_zmq_tx_port: await getPort({port: 11001, stopPort: 12000}),
112
+ generate_address: generateAddress,
113
+ lightning_p2p_port: await getPort({port: 12001, stopPort: 13000}),
114
+ lightning_rpc_port: await getPort({port: 13001, stopPort: 14000}),
115
+ });
116
+ });
117
+
118
+ await clone.add_chain_peer({socket: target.chain_socket});
119
+
120
+ await target.add_chain_peer({socket: clone.chain_socket});
121
+
122
+ const cloneLnd = authenticatedLndGrpc({
123
+ cert: clone.cert,
124
+ macaroon: clone.macaroon,
125
+ socket: clone.socket,
126
+ });
127
+
128
+ await addPeer({
129
+ lnd: cloneLnd.lnd,
130
+ public_key: targetId,
131
+ socket: target.ln_socket,
132
+ });
133
+
134
+ await asyncRetry({interval, times}, async () => {
135
+ await recoverFundsFromChannels({backup, lnd: cloneLnd.lnd});
136
+ });
137
+
138
+ await clone.generate({
139
+ address: generateAddress,
140
+ count: confirmationCount,
141
+ });
142
+
143
+ await target.generate({
144
+ address: generateAddress,
145
+ count: confirmationCount,
146
+ });
147
+
148
+ // Target should force close the channel
149
+ {
150
+ const {lnd} = targetLnd;
151
+
152
+ await asyncRetry({interval, times}, async () => {
153
+ await addPeer({
154
+ lnd: cloneLnd.lnd,
155
+ public_key: targetId,
156
+ socket: target.ln_socket,
157
+ });
158
+
159
+ const [chan] = (await getPendingChannels({lnd})).pending_channels;
160
+
161
+ if (!chan) {
162
+ throw new Error('ExpectedChannelClosing');
163
+ }
164
+ });
165
+ }
166
+
167
+ // Make sure that the clone is getting the recovered funds
168
+ {
169
+ const {lnd} = cloneLnd;
170
+
171
+ await asyncRetry({interval, times}, async () => {
172
+ const [chan] = (await getPendingChannels({lnd})).pending_channels;
173
+
174
+ await target.generate({address: generateAddress});
175
+
176
+ if (!chan.local_balance) {
177
+ throw new Error('ExpectedChannelClosingBalance');
178
+ }
179
+ });
180
+
181
+ const [chan] = (await getPendingChannels({lnd})).pending_channels;
182
+
183
+ equal(!!chan.close_transaction_id, true, 'Close transaction id found');
184
+ equal(chan.is_active, false, 'Chan no longer active');
185
+ equal(chan.is_closing, true, 'Channel is closing');
186
+ equal(chan.is_opening, false, 'Channel closing');
187
+ equal(chan.local_balance, giftTokens, 'Funds are being restored');
188
+ equal(chan.partner_public_key, targetId, 'Peer key');
189
+ equal(chan.transaction_id, channelOpen.transaction_id, 'Chan tx id');
190
+ equal(chan.transaction_vout, channelOpen.transaction_vout, 'Chan tx vout');
191
+ }
192
+
193
+ await clone.kill({});
194
+
195
+ await target.kill({});
196
+
197
+ return end();
198
+ });
@@ -39,7 +39,7 @@ const readMacaroonFileName = 'readonly.macaroon';
39
39
  const retryCreateSeedCount = 500;
40
40
  const startPortRange = 7593;
41
41
  const startWalletTimeoutMs = 4500;
42
- const times = 100;
42
+ const times = 200;
43
43
 
44
44
  /** Run a change password test
45
45
 
@@ -1,57 +1,60 @@
1
+ const asyncRetry = require('async/retry');
2
+ const {spawnLightningCluster} = require('ln-docker-daemons');
1
3
  const {test} = require('@alexbosworth/tap');
2
- const tinysecp = require('tiny-secp256k1');
3
4
 
4
5
  const {broadcastChainTransaction} = require('./../../');
5
- const {chainSendTransaction} = require('./../macros');
6
6
  const {createChainAddress} = require('./../../');
7
- const {delay} = require('./../macros');
8
- const {generateBlocks} = require('./../macros');
7
+ const {fundPsbt} = require('./../../');
9
8
  const {getChainTransactions} = require('./../../');
10
- const {spawnLnd} = require('./../macros');
11
- const {waitForTermination} = require('./../macros');
9
+ const {signPsbt} = require('./../../');
12
10
 
13
11
  const count = 100;
14
- const defaultVout = 0;
15
- const fee = 1e3;
16
- const format = 'np2wpkh';
12
+ const description = 'description';
13
+ const interval = 10;
14
+ const times = 2000;
17
15
  const tokens = 1e8;
18
16
 
19
17
  // Test sending a chain transaction to Bitcoin network peers
20
- test(`Send chain transaction`, async ({end, equal}) => {
21
- const node = await spawnLnd({});
18
+ test(`Broadcast chain transaction`, async ({end, equal}) => {
19
+ const [{generate, kill, lnd}] = (await spawnLightningCluster({})).nodes;
22
20
 
23
- const {lnd} = node;
21
+ try {
22
+ await generate({count});
24
23
 
25
- // Generate some funds
26
- const {blocks} = await node.generate({count});
24
+ const {address} = await createChainAddress({lnd});
27
25
 
28
- const [block] = blocks;
26
+ const {psbt} = await fundPsbt({lnd, outputs: [{address, tokens}]});
29
27
 
30
- const [coinbaseTransactionId] = block.transaction_ids;
28
+ const {transaction} = await signPsbt({lnd, psbt});
31
29
 
32
- const {transaction} = chainSendTransaction({
33
- fee,
34
- tokens,
35
- destination: (await createChainAddress({format, lnd})).address,
36
- ecp: (await import('ecpair')).ECPairFactory(tinysecp),
37
- private_key: node.mining_key,
38
- spend_transaction_id: coinbaseTransactionId,
39
- spend_vout: defaultVout,
40
- });
30
+ const {id} = await broadcastChainTransaction({
31
+ description,
32
+ lnd,
33
+ transaction,
34
+ });
41
35
 
42
- const {id} = await broadcastChainTransaction({transaction, lnd: node.lnd});
36
+ await asyncRetry({interval, times}, async () => {
37
+ const {transactions} = await getChainTransactions({lnd});
43
38
 
44
- await delay(5000);
39
+ await generate({});
45
40
 
46
- const {transactions} = await getChainTransactions({lnd});
41
+ const tx = transactions.find(n => n.id === id);
47
42
 
48
- const [tx] = transactions;
43
+ if (!tx) {
44
+ throw new Error('ExpectedTransactionBroadcast');
45
+ }
49
46
 
50
- equal(id, tx.id, 'Transaction is found in broadcast');
47
+ if (!tx.is_confirmed) {
48
+ throw new Error('ExpectedTransactionConfirmed');
49
+ }
51
50
 
52
- node.kill();
51
+ equal(tx.description, description, 'Description is set');
52
+ });
53
+ } catch (err) {
54
+ equal(err, null, 'Expected no error');
55
+ }
53
56
 
54
- await waitForTermination({lnd});
57
+ await kill({});
55
58
 
56
59
  return end();
57
60
  });
@@ -1,54 +1,43 @@
1
+ const {spawnLightningCluster} = require('ln-docker-daemons');
1
2
  const {test} = require('@alexbosworth/tap');
2
- const tinysecp = require('tiny-secp256k1');
3
3
 
4
4
  const {broadcastChainTransaction} = require('./../../');
5
- const {chainSendTransaction} = require('./../macros');
6
5
  const {createChainAddress} = require('./../../');
7
- const {generateBlocks} = require('./../macros');
6
+ const {fundPsbt} = require('./../../');
7
+ const {getUtxos} = require('./../../');
8
8
  const {requestChainFeeIncrease} = require('./../../');
9
- const {spawnLnd} = require('./../macros');
10
- const {waitForTermination} = require('./../macros');
9
+ const {signPsbt} = require('./../../');
11
10
 
12
11
  const count = 100;
13
- const defaultVout = 0;
14
- const fee = 1e3;
15
- const format = 'np2wpkh';
16
12
  const tokens = 1e8;
17
13
 
18
- // Test requesting an increase in chain fees
14
+ // Test requesting a chain fee increase
19
15
  test(`Request chain fee increase`, async ({end, equal}) => {
20
- const node = await spawnLnd({});
16
+ const [{generate, kill, lnd}] = (await spawnLightningCluster({})).nodes;
21
17
 
22
- const {lnd} = node;
18
+ try {
19
+ await generate({count});
23
20
 
24
- // Generate some funds
25
- const {blocks} = await node.generate({count});
21
+ const {address} = await createChainAddress({lnd});
26
22
 
27
- const [block] = blocks;
23
+ const {psbt} = await fundPsbt({lnd, outputs: [{address, tokens}]});
28
24
 
29
- const [coinbaseTransactionId] = block.transaction_ids;
25
+ const {transaction} = await signPsbt({lnd, psbt});
30
26
 
31
- const {transaction} = chainSendTransaction({
32
- fee,
33
- tokens,
34
- destination: (await createChainAddress({format, lnd})).address,
35
- ecp: (await import('ecpair')).ECPairFactory(tinysecp),
36
- private_key: node.mining_key,
37
- spend_transaction_id: coinbaseTransactionId,
38
- spend_vout: defaultVout,
39
- });
27
+ await broadcastChainTransaction({lnd, transaction});
40
28
 
41
- const {id} = await broadcastChainTransaction({transaction, lnd: node.lnd});
29
+ const bump = (await getUtxos({lnd})).utxos.find(n => n.tokens === tokens);
42
30
 
43
- await requestChainFeeIncrease({
44
- lnd,
45
- transaction_id: id,
46
- transaction_vout: Number(),
47
- });
31
+ await requestChainFeeIncrease({
32
+ lnd,
33
+ transaction_id: bump.transaction_id,
34
+ transaction_vout: bump.transaction_vout,
35
+ });
36
+ } catch (err) {
37
+ equal(err, null, 'Expected no error');
38
+ }
48
39
 
49
- node.kill();
50
-
51
- await waitForTermination({lnd});
40
+ await kill({});
52
41
 
53
42
  return end();
54
43
  });
@@ -1,85 +0,0 @@
1
- const {test} = require('@alexbosworth/tap');
2
-
3
- const {createCluster} = require('./../macros');
4
- const {delay} = require('./../macros');
5
- const {getBackup} = require('./../../');
6
- const {getPendingChannels} = require('./../../');
7
- const {recoverFundsFromChannel} = require('./../../');
8
- const {setupChannel} = require('./../macros');
9
- const {spawnLnd} = require('./../macros');
10
- const {stopDaemon} = require('./../../');
11
- const {waitForPendingChannel} = require('./../macros');
12
- const {waitForTermination} = require('./../macros');
13
-
14
- const channelCapacityTokens = 1e6;
15
- const confirmationCount = 20;
16
- const defaultFee = 1e3;
17
- const giftTokens = 1e5;
18
- const seed = 'abandon tank dose ripple foil subway close flock laptop cabbage primary silent plastic unhappy west weird panda plastic brave prefer diesel glad jazz isolate';
19
-
20
- // Using a channel backup should recover funds
21
- test(`Recover funds from channel`, async ({end, equal}) => {
22
- const clone = await spawnLnd({seed});
23
-
24
- const cluster = await createCluster({
25
- is_remote_skipped: true,
26
- nodes: [clone],
27
- });
28
-
29
- const {lnd} = cluster.control;
30
-
31
- const channelOpen = await setupChannel({
32
- generate: cluster.generate,
33
- generator: cluster.target,
34
- give: giftTokens,
35
- lnd: cluster.target.lnd,
36
- to: cluster.control,
37
- });
38
-
39
- const {backup} = await getBackup({
40
- lnd,
41
- transaction_id: channelOpen.transaction_id,
42
- transaction_vout: channelOpen.transaction_vout,
43
- });
44
-
45
- await stopDaemon({lnd});
46
-
47
- cluster.control.kill();
48
-
49
- await recoverFundsFromChannel({backup, lnd: clone.lnd});
50
-
51
- await delay(3000);
52
-
53
- await cluster.generate({count: confirmationCount, node: clone});
54
-
55
- await delay(3000);
56
-
57
- await cluster.generate({count: confirmationCount, node: cluster.target});
58
-
59
- await delay(3000);
60
-
61
- await waitForPendingChannel({
62
- id: channelOpen.transaction_id,
63
- is_closing: true,
64
- lnd: clone.lnd,
65
- });
66
-
67
- const [chan] = (await getPendingChannels({lnd: clone.lnd})).pending_channels;
68
-
69
- equal(!!chan.close_transaction_id, true, 'Close transaction id found');
70
- equal(chan.is_active, false, 'Chan no longer active');
71
- equal(chan.is_closing, true, 'Channel is closing');
72
- equal(chan.is_opening, false, 'Channel closing');
73
- equal(chan.local_balance, giftTokens, 'Funds are being restored');
74
- equal(chan.partner_public_key, cluster.target_node_public_key, 'Peer key');
75
- equal(chan.transaction_id, channelOpen.transaction_id, 'Chan tx id');
76
- equal(chan.transaction_vout, channelOpen.transaction_vout, 'Chan tx vout');
77
-
78
- clone.kill();
79
-
80
- await waitForTermination({lnd: clone.lnd});
81
-
82
- await cluster.kill({});
83
-
84
- return end();
85
- });
@@ -1,112 +0,0 @@
1
- const asyncRetry = require('async/retry');
2
- const {test} = require('@alexbosworth/tap');
3
-
4
- const {createCluster} = require('./../macros');
5
- const {delay} = require('./../macros');
6
- const {getBackups} = require('./../../');
7
- const {getPendingChannels} = require('./../../');
8
- const {recoverFundsFromChannels} = require('./../../');
9
- const {spawnLnd} = require('./../macros');
10
- const {setupChannel} = require('./../macros');
11
- const {stopDaemon} = require('./../../');
12
- const {waitForPendingChannel} = require('./../macros');
13
- const {waitForTermination} = require('./../macros');
14
-
15
- const channelCapacityTokens = 1e6;
16
- const confirmationCount = 20;
17
- const defaultFee = 1e3;
18
- const giftTokens = 1e5;
19
- const seed = 'abandon tank dose ripple foil subway close flock laptop cabbage primary silent plastic unhappy west weird panda plastic brave prefer diesel glad jazz isolate';
20
-
21
- // Using the channels backup should recover funds
22
- test(`Recover funds with backup`, async ({end, equal}) => {
23
- const clone = await spawnLnd({seed});
24
-
25
- const cluster = await createCluster({
26
- is_remote_skipped: true,
27
- nodes: [clone],
28
- });
29
-
30
- let channelOpen;
31
- const {lnd} = cluster.control;
32
-
33
- try {
34
- channelOpen = await setupChannel({
35
- generate: cluster.generate,
36
- generator: cluster.target,
37
- give: giftTokens,
38
- lnd: cluster.target.lnd,
39
- to: cluster.control,
40
- });
41
- } catch (err) {
42
- equal(err, null, 'Expected no error setting up a channel');
43
-
44
- clone.kill();
45
-
46
- await waitForTermination({lnd: clone.lnd});
47
-
48
- await cluster.kill({});
49
-
50
- return end();
51
- }
52
-
53
- await delay(3000);
54
-
55
- const {backup} = await getBackups({lnd});
56
-
57
- await delay(3000);
58
-
59
- await stopDaemon({lnd});
60
-
61
- try {
62
- const recover = await recoverFundsFromChannels({backup, lnd: clone.lnd});
63
-
64
- equal(recover, undefined, 'Recover does not return anything');
65
- } catch (err) {
66
- equal(err, null, 'An error is not expected');
67
- }
68
-
69
- await cluster.generate({count: confirmationCount, node: clone});
70
-
71
- await waitForPendingChannel({
72
- id: channelOpen.transaction_id,
73
- is_recovering: true,
74
- lnd: clone.lnd,
75
- });
76
-
77
- await delay(3000);
78
-
79
- await cluster.generate({count: confirmationCount, node: clone});
80
-
81
- await delay(3000);
82
-
83
- await cluster.generate({count: confirmationCount, node: cluster.target});
84
-
85
- const [chan] = (await getPendingChannels({lnd: clone.lnd})).pending_channels;
86
-
87
- equal(!!chan.close_transaction_id, true, 'Close transaction id found');
88
- equal(chan.is_active, false, 'Chan no longer active');
89
- equal(chan.is_closing, true, 'Channel is closing');
90
- equal(chan.is_opening, false, 'Channel closing');
91
- equal(chan.local_balance, giftTokens, 'Funds are being restored');
92
- equal(chan.partner_public_key, cluster.target_node_public_key, 'Peer key');
93
- equal(chan.transaction_id, channelOpen.transaction_id, 'Chan tx id');
94
- equal(chan.transaction_vout, channelOpen.transaction_vout, 'Chan tx vout');
95
-
96
- const interval = retryCount => 50 * Math.pow(2, retryCount);
97
- const times = 10;
98
-
99
- await delay(3000);
100
-
101
- clone.kill();
102
-
103
- clone.kill();
104
-
105
- clone.kill();
106
-
107
- await waitForTermination({lnd: clone.lnd});
108
-
109
- await cluster.kill({});
110
-
111
- return end();
112
- });