ln-service 53.9.2 → 53.10.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 +6 -1
- package/README.md +12 -3
- package/package.json +5 -5
- package/test/chainrpc-integration/test_subscribe_to_blocks.js +0 -1
- package/test/chainrpc-integration/test_subscribe_to_chain_address.js +0 -1
- package/test/integration/test_create_chain_address.js +9 -0
- package/test/integration/test_get_pending_force.js +1 -1
- package/test/walletrpc-integration/test_fund_psbt.js +203 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -514,12 +514,19 @@ Create a new receive address.
|
|
|
514
514
|
|
|
515
515
|
Requires `address:write` permission
|
|
516
516
|
|
|
517
|
+
LND 0.14.3 and below do not support p2tr addresses
|
|
518
|
+
|
|
517
519
|
{
|
|
518
|
-
[format]: <Receive Address Type String> // "np2wpkh" || "p2wpkh"
|
|
520
|
+
[format]: <Receive Address Type String> // "np2wpkh" || "p2tr" || "p2wpkh"
|
|
519
521
|
[is_unused]: <Get As-Yet Unused Address Bool>
|
|
520
522
|
lnd: <Authenticated LND API Object>
|
|
521
523
|
}
|
|
522
524
|
|
|
525
|
+
@returns via cbk or Promise
|
|
526
|
+
{
|
|
527
|
+
address: <Chain Address String>
|
|
528
|
+
}
|
|
529
|
+
|
|
523
530
|
Example:
|
|
524
531
|
|
|
525
532
|
```node
|
|
@@ -3530,7 +3537,7 @@ Requires `onchain:write` permission
|
|
|
3530
3537
|
|
|
3531
3538
|
Requires LND built with `walletrpc` tag
|
|
3532
3539
|
|
|
3533
|
-
This method is not supported in LND 0.14.
|
|
3540
|
+
This method is not supported in LND 0.14.3 and below
|
|
3534
3541
|
|
|
3535
3542
|
{
|
|
3536
3543
|
lnd: <Authenticated LND API Object>
|
|
@@ -3685,7 +3692,7 @@ Requires `offchain:write` permission
|
|
|
3685
3692
|
[outgoing_channels]: [<Pay Out of Outgoing Channel Ids String>]
|
|
3686
3693
|
[pathfinding_timeout]: <Time to Spend Finding a Route Milliseconds Number>
|
|
3687
3694
|
[payment]: <Payment Identifier Hex String>
|
|
3688
|
-
routes: [[{
|
|
3695
|
+
[routes]: [[{
|
|
3689
3696
|
[base_fee_mtokens]: <Base Routing Fee In Millitokens String>
|
|
3690
3697
|
[channel]: <Standard Format Channel Id String>
|
|
3691
3698
|
[cltv_delta]: <CLTV Blocks Delta Number>
|
|
@@ -5433,6 +5440,8 @@ const {secret} = await once(sub, 'confirmed');
|
|
|
5433
5440
|
|
|
5434
5441
|
Subscribe to successful outgoing payments
|
|
5435
5442
|
|
|
5443
|
+
Payments may be omitted if LND does not finalize the payment record
|
|
5444
|
+
|
|
5436
5445
|
Requires `offchain:read` permission
|
|
5437
5446
|
|
|
5438
5447
|
Note: Method not supported on LND 0.13.4 and below
|
package/package.json
CHANGED
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
"cors": "2.8.5",
|
|
12
12
|
"express": "4.17.3",
|
|
13
13
|
"invoices": "2.0.4",
|
|
14
|
-
"lightning": "5.
|
|
14
|
+
"lightning": "5.9.0",
|
|
15
15
|
"macaroon": "3.0.4",
|
|
16
16
|
"morgan": "1.10.0",
|
|
17
17
|
"ws": "8.5.0"
|
|
18
18
|
},
|
|
19
19
|
"description": "Interaction helper for your Lightning Network daemon",
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@alexbosworth/tap": "15.0.
|
|
21
|
+
"@alexbosworth/tap": "15.0.11",
|
|
22
22
|
"@alexbosworth/node-fetch": "2.6.2",
|
|
23
23
|
"async": "3.2.3",
|
|
24
24
|
"asyncjs-util": "1.2.8",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"bn.js": "5.2.0",
|
|
29
29
|
"bs58check": "2.1.2",
|
|
30
30
|
"ecpair": "2.0.1",
|
|
31
|
-
"ln-docker-daemons": "2.2.
|
|
32
|
-
"p2tr": "1.
|
|
31
|
+
"ln-docker-daemons": "2.2.6",
|
|
32
|
+
"p2tr": "1.3.0",
|
|
33
33
|
"portfinder": "1.0.28",
|
|
34
34
|
"psbt": "2.0.0",
|
|
35
35
|
"rimraf": "3.0.2",
|
|
@@ -69,5 +69,5 @@
|
|
|
69
69
|
"integration-test-0.12.0": "DOCKER_LND_VERSION=v0.12.0-beta npm run test",
|
|
70
70
|
"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/routerrpc-integration/*.js test/signerrpc-integration/*.js test/tower_clientrpc-integration/*.js test/tower_serverrpc-integration/*.js test/walletrpc-integration/*.js"
|
|
71
71
|
},
|
|
72
|
-
"version": "53.
|
|
72
|
+
"version": "53.10.0"
|
|
73
73
|
}
|
|
@@ -10,7 +10,6 @@ const {delay} = require('./../macros');
|
|
|
10
10
|
const {generateBlocks} = require('./../macros');
|
|
11
11
|
const {getHeight} = require('./../../');
|
|
12
12
|
const {getChainBalance} = require('./../../');
|
|
13
|
-
const {spawnLnd} = require('./../macros');
|
|
14
13
|
const {subscribeToBlocks} = require('./../../');
|
|
15
14
|
const {waitForTermination} = require('./../macros');
|
|
16
15
|
|
|
@@ -11,7 +11,6 @@ const {getChainTransactions} = require('./../../');
|
|
|
11
11
|
const {getHeight} = require('./../../');
|
|
12
12
|
const {mineTransaction} = require('./../macros');
|
|
13
13
|
const {sendToChainAddress} = require('./../../');
|
|
14
|
-
const {spawnLnd} = require('./../macros');
|
|
15
14
|
const {subscribeToChainAddress} = require('./../../');
|
|
16
15
|
const {waitForTermination} = require('./../macros');
|
|
17
16
|
|
|
@@ -7,6 +7,7 @@ const {createChainAddress} = require('./../../');
|
|
|
7
7
|
const formats = ['np2wpkh', 'p2wpkh'];
|
|
8
8
|
const p2shAddressVersion = 196;
|
|
9
9
|
const pkHashByteLength = 20;
|
|
10
|
+
const prefixForV1 = 'bcrt1p';
|
|
10
11
|
const regtestBech32AddressHrp = 'bcrt';
|
|
11
12
|
|
|
12
13
|
// Creating addresses should result in addresses
|
|
@@ -34,6 +35,14 @@ test(`Create address results in address creation`, async ({end, equal}) => {
|
|
|
34
35
|
equal(np2wpkh.address, unusedNp2wpkh.address, 'Nested is reused');
|
|
35
36
|
equal(p2wpkh.address, unusedP2wpkh.address, 'Native is reused');
|
|
36
37
|
|
|
38
|
+
try {
|
|
39
|
+
const {address} = await createChainAddress({lnd, format: 'p2tr'});
|
|
40
|
+
|
|
41
|
+
equal(address.startsWith(prefixForV1), true, 'A taproot address is made');
|
|
42
|
+
} catch (err) {
|
|
43
|
+
// LND 0.14.3 and below do not support TR addresses
|
|
44
|
+
}
|
|
45
|
+
|
|
37
46
|
await kill({});
|
|
38
47
|
|
|
39
48
|
return end();
|
|
@@ -178,7 +178,7 @@ test(`Get pending channels`, async ({end, equal}) => {
|
|
|
178
178
|
equal(forceClose.received, 0, 'No receive amount');
|
|
179
179
|
equal(forceClose.recovered_tokens, undefined, 'No recovered amount');
|
|
180
180
|
|
|
181
|
-
// LND 0.14.
|
|
181
|
+
// LND 0.14.3 and below do not support remote balance info
|
|
182
182
|
if (!!forceClose.remote_balance) {
|
|
183
183
|
equal(forceClose.remote_balance, giftTokens, 'Got gift remote balance');
|
|
184
184
|
} else {
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
const asyncRetry = require('async/retry');
|
|
2
2
|
const {address} = require('bitcoinjs-lib');
|
|
3
|
+
const {controlBlock} = require('p2tr');
|
|
3
4
|
const {createPsbt} = require('psbt');
|
|
4
5
|
const {decodePsbt} = require('psbt');
|
|
6
|
+
const {hashForTree} = require('p2tr');
|
|
7
|
+
const {leafHash} = require('p2tr');
|
|
5
8
|
const {networks} = require('bitcoinjs-lib');
|
|
9
|
+
const {pointAdd} = require('tiny-secp256k1');
|
|
10
|
+
const {privateAdd} = require('tiny-secp256k1');
|
|
11
|
+
const {script} = require('bitcoinjs-lib');
|
|
6
12
|
const {signHash} = require('p2tr');
|
|
13
|
+
const {signSchnorr} = require('tiny-secp256k1');
|
|
7
14
|
const {spawnLightningCluster} = require('ln-docker-daemons');
|
|
8
15
|
const {test} = require('@alexbosworth/tap');
|
|
9
16
|
const tinysecp = require('tiny-secp256k1');
|
|
@@ -20,14 +27,18 @@ const {sendToChainAddress} = require('./../../');
|
|
|
20
27
|
const {signPsbt} = require('./../../');
|
|
21
28
|
|
|
22
29
|
const chainAddressRowType = 'chain_address';
|
|
30
|
+
const {compile} = script;
|
|
23
31
|
const confirmationCount = 6;
|
|
24
32
|
const count = 100;
|
|
33
|
+
const defaultInternalKey = '0350929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0';
|
|
25
34
|
const description = 'description';
|
|
35
|
+
const {from} = Buffer;
|
|
26
36
|
const {fromBech32} = address;
|
|
27
37
|
const {fromHex} = Transaction;
|
|
28
38
|
const {fromOutputScript} = address;
|
|
29
39
|
const hexAsBuffer = hex => Buffer.from(hex, 'hex');
|
|
30
40
|
const interval = retryCount => 10 * Math.pow(2, retryCount);
|
|
41
|
+
const OP_CHECKSIG = 172;
|
|
31
42
|
const regtestBech32AddressHrp = 'bcrt';
|
|
32
43
|
const smallTokens = 2e5;
|
|
33
44
|
const times = 20;
|
|
@@ -108,7 +119,198 @@ test(`Fund PSBT`, async ({end, equal}) => {
|
|
|
108
119
|
equal(!!decodedInput.witness_utxo.script_pub, true, 'PSBT input address');
|
|
109
120
|
equal(decodedInput.witness_utxo.tokens, 5000000000, 'PSBT has input tokens');
|
|
110
121
|
|
|
111
|
-
// A Taproot
|
|
122
|
+
// A Taproot script can be funded and spent with internal key + script hash
|
|
123
|
+
try {
|
|
124
|
+
await generate({count});
|
|
125
|
+
|
|
126
|
+
const keyPair1 = ecp.makeRandom({network: networks.regtest});
|
|
127
|
+
const keyPair2 = ecp.makeRandom({network: networks.regtest});
|
|
128
|
+
const unusedKey = ecp.makeRandom({network: networks.regtest});
|
|
129
|
+
|
|
130
|
+
const witnessScript = compile([unusedKey.publicKey.slice(1), OP_CHECKSIG]);
|
|
131
|
+
|
|
132
|
+
const branches = [{script: witnessScript.toString('hex')}];
|
|
133
|
+
|
|
134
|
+
const {hash} = hashForTree({branches});
|
|
135
|
+
|
|
136
|
+
// Create a combined key using public key material
|
|
137
|
+
const combinedPoint = pointAdd(keyPair1.publicKey, keyPair2.publicKey);
|
|
138
|
+
|
|
139
|
+
const output = v1OutputScript({
|
|
140
|
+
hash,
|
|
141
|
+
internal_key: Buffer.from(combinedPoint).toString('hex'),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const [utxo] = (await getUtxos({lnd})).utxos.reverse();
|
|
145
|
+
|
|
146
|
+
// Make a PSBT paying to the Taproot output
|
|
147
|
+
const {psbt} = createPsbt({
|
|
148
|
+
outputs: [{tokens, script: output.script}],
|
|
149
|
+
utxos: [{id: utxo.transaction_id, vout: utxo.transaction_vout}],
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Sign the PSBT
|
|
153
|
+
const signed = await signPsbt({
|
|
154
|
+
lnd,
|
|
155
|
+
psbt: (await fundPsbt({lnd, psbt})).psbt,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Send the tx to the chain
|
|
159
|
+
await broadcastChainTransaction({lnd, transaction: signed.transaction});
|
|
160
|
+
|
|
161
|
+
// Make a new tx that will spend the output back into the wallet
|
|
162
|
+
const tx = new Transaction();
|
|
163
|
+
|
|
164
|
+
// The new tx spends the Taproot output
|
|
165
|
+
tx.addInput(
|
|
166
|
+
fromHex(signed.transaction).getHash(),
|
|
167
|
+
fromHex(signed.transaction).outs.findIndex(n => n.value === tokens)
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
// Make an output to pay back into the wallet
|
|
171
|
+
const chainOutput = toOutputScript(
|
|
172
|
+
(await createChainAddress({lnd})).address,
|
|
173
|
+
networks.regtest
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
// Add output to the pay back transaction
|
|
177
|
+
tx.addOutput(chainOutput, smallTokens);
|
|
178
|
+
|
|
179
|
+
const [hashToSign] = tx.ins.map((input, i) => {
|
|
180
|
+
return tx.hashForWitnessV1(
|
|
181
|
+
i,
|
|
182
|
+
[hexAsBuffer(output.script)],
|
|
183
|
+
[tokens],
|
|
184
|
+
Transaction.SIGHASH_DEFAULT,
|
|
185
|
+
);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Ready for private key combining
|
|
189
|
+
const combinedKey = privateAdd(keyPair1.privateKey, keyPair2.privateKey);
|
|
190
|
+
|
|
191
|
+
const signedInput = signHash({
|
|
192
|
+
hash,
|
|
193
|
+
private_key: Buffer.from(combinedKey).toString('hex'),
|
|
194
|
+
public_key: Buffer.from(combinedPoint).toString('hex'),
|
|
195
|
+
sign_hash: hashToSign.toString('hex'),
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const signature = hexAsBuffer(signedInput.signature);
|
|
199
|
+
|
|
200
|
+
// Add the signature to the input
|
|
201
|
+
tx.ins.forEach((input, i) => tx.setWitness(i, [signature]));
|
|
202
|
+
|
|
203
|
+
await broadcastChainTransaction({lnd, transaction: tx.toHex()});
|
|
204
|
+
|
|
205
|
+
await asyncRetry({interval, times}, async () => {
|
|
206
|
+
await generate({});
|
|
207
|
+
|
|
208
|
+
const {utxos} = await getUtxos({lnd});
|
|
209
|
+
|
|
210
|
+
const utxo = utxos.find(n => n.transaction_id === tx.getId());
|
|
211
|
+
|
|
212
|
+
if (!utxo || !utxo.confirmation_count) {
|
|
213
|
+
throw new Error('ExpectedReceivedTaprootSpend');
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
} catch (err) {
|
|
217
|
+
equal(err, null, 'Expected no error');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// A Taproot script output should be funded and spent with script
|
|
221
|
+
try {
|
|
222
|
+
await generate({count});
|
|
223
|
+
|
|
224
|
+
const keyPair = ecp.makeRandom({network: networks.regtest});
|
|
225
|
+
|
|
226
|
+
const witnessScript = compile([keyPair.publicKey.slice(1), OP_CHECKSIG]);
|
|
227
|
+
|
|
228
|
+
const branches = [{script: witnessScript.toString('hex')}];
|
|
229
|
+
|
|
230
|
+
const {hash} = hashForTree({branches});
|
|
231
|
+
|
|
232
|
+
const output = v1OutputScript({hash, internal_key: defaultInternalKey});
|
|
233
|
+
|
|
234
|
+
const [utxo] = (await getUtxos({lnd})).utxos.reverse();
|
|
235
|
+
|
|
236
|
+
// Make a PSBT paying to the Taproot output
|
|
237
|
+
const {psbt} = createPsbt({
|
|
238
|
+
outputs: [{tokens, script: output.script}],
|
|
239
|
+
utxos: [{id: utxo.transaction_id, vout: utxo.transaction_vout}],
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Sign the PSBT
|
|
243
|
+
const signed = await signPsbt({
|
|
244
|
+
lnd,
|
|
245
|
+
psbt: (await fundPsbt({lnd, psbt})).psbt,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Send the tx to the chain
|
|
249
|
+
await broadcastChainTransaction({lnd, transaction: signed.transaction});
|
|
250
|
+
|
|
251
|
+
// Make a new tx that will spend the output back into the wallet
|
|
252
|
+
const tx = new Transaction();
|
|
253
|
+
|
|
254
|
+
// The new tx spends the Taproot output
|
|
255
|
+
tx.addInput(
|
|
256
|
+
fromHex(signed.transaction).getHash(),
|
|
257
|
+
fromHex(signed.transaction).outs.findIndex(n => n.value === tokens)
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
// Make an output to pay back into the wallet
|
|
261
|
+
const chainOutput = toOutputScript(
|
|
262
|
+
(await createChainAddress({lnd})).address,
|
|
263
|
+
networks.regtest
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
// Add output to the pay back transaction
|
|
267
|
+
tx.addOutput(chainOutput, smallTokens);
|
|
268
|
+
|
|
269
|
+
const [hashToSign] = tx.ins.map((input, i) => {
|
|
270
|
+
return tx.hashForWitnessV1(
|
|
271
|
+
i,
|
|
272
|
+
[hexAsBuffer(output.script)],
|
|
273
|
+
[tokens],
|
|
274
|
+
Transaction.SIGHASH_DEFAULT,
|
|
275
|
+
hexAsBuffer(leafHash({script: witnessScript.toString('hex')}).hash),
|
|
276
|
+
);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
const signature = from(signSchnorr(hashToSign, keyPair.privateKey));
|
|
280
|
+
|
|
281
|
+
const {block} = controlBlock({
|
|
282
|
+
external_key: output.external_key,
|
|
283
|
+
leaf_script: witnessScript.toString('hex'),
|
|
284
|
+
script_branches: branches,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// Add the signature to the input
|
|
288
|
+
tx.ins.forEach((input, i) => {
|
|
289
|
+
return tx.setWitness(i, [
|
|
290
|
+
signature,
|
|
291
|
+
witnessScript,
|
|
292
|
+
hexAsBuffer(block),
|
|
293
|
+
]);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
await broadcastChainTransaction({lnd, transaction: tx.toHex()});
|
|
297
|
+
|
|
298
|
+
await asyncRetry({interval, times}, async () => {
|
|
299
|
+
await generate({});
|
|
300
|
+
|
|
301
|
+
const {utxos} = await getUtxos({lnd});
|
|
302
|
+
|
|
303
|
+
const utxo = utxos.find(n => n.transaction_id === tx.getId());
|
|
304
|
+
|
|
305
|
+
if (!utxo || !utxo.confirmation_count) {
|
|
306
|
+
throw new Error('ExpectedReceivedTaprootSpend');
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
} catch (err) {
|
|
310
|
+
equal(err, null, 'Expected no error');
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// A Taproot output should be funded for a regular key spend
|
|
112
314
|
try {
|
|
113
315
|
await generate({count});
|
|
114
316
|
|