ln-service 59.3.1 → 59.3.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 +11 -10
- package/test/integration/test_propose_channel.js +66 -63
- package/test/macros/index.js +0 -16
- package/test/signerrpc-integration/test_begin_group_signing_session.js +159 -97
- package/test/signerrpc-integration/test_sign_taproot.js +136 -68
- package/test/walletrpc-integration/test_fund_psbt.js +169 -100
- package/test/macros/btcsuite_type_daemon.js +0 -143
- package/test/macros/chain_send_transaction.js +0 -102
- package/test/macros/change_password.js +0 -441
- package/test/macros/connect_chain_node.js +0 -27
- package/test/macros/create_cluster.js +0 -426
- package/test/macros/generate_blocks.js +0 -141
- package/test/macros/get_raw_transaction.js +0 -78
- package/test/macros/mine_transaction.js +0 -87
- package/test/macros/rpc.js +0 -82
- package/test/macros/spawn_chain_daemon.js +0 -88
- package/test/macros/spawn_lnd.js +0 -558
|
@@ -2,13 +2,14 @@ const {equal} = require('node:assert').strict;
|
|
|
2
2
|
const test = require('node:test');
|
|
3
3
|
|
|
4
4
|
const asyncRetry = require('async/retry');
|
|
5
|
-
const {
|
|
5
|
+
const {componentsOfTransaction} = require('@alexbosworth/blockchain');
|
|
6
6
|
const {controlBlock} = require('p2tr');
|
|
7
7
|
const {createPsbt} = require('psbt');
|
|
8
|
+
const {decodeBech32Address} = require('@alexbosworth/blockchain');
|
|
8
9
|
const {decodePsbt} = require('psbt');
|
|
9
10
|
const {hashForTree} = require('p2tr');
|
|
10
|
-
const {
|
|
11
|
-
const {
|
|
11
|
+
const {idForTransaction} = require('@alexbosworth/blockchain');
|
|
12
|
+
const {p2wpkhOutputScript} = require('@alexbosworth/blockchain');
|
|
12
13
|
const {pointAdd} = require('tiny-secp256k1');
|
|
13
14
|
const {privateAdd} = require('tiny-secp256k1');
|
|
14
15
|
const {scriptElementsAsScript} = require('@alexbosworth/blockchain');
|
|
@@ -16,7 +17,8 @@ const {signHash} = require('p2tr');
|
|
|
16
17
|
const {signSchnorr} = require('tiny-secp256k1');
|
|
17
18
|
const {spawnLightningCluster} = require('ln-docker-daemons');
|
|
18
19
|
const tinysecp = require('tiny-secp256k1');
|
|
19
|
-
const {
|
|
20
|
+
const {transactionFromComponents} = require('@alexbosworth/blockchain');
|
|
21
|
+
const {v1HashToSign} = require('p2tr');
|
|
20
22
|
const {v1OutputScript} = require('p2tr');
|
|
21
23
|
|
|
22
24
|
const {broadcastChainTransaction} = require('./../../');
|
|
@@ -28,24 +30,29 @@ const {getUtxos} = require('./../../');
|
|
|
28
30
|
const {sendToChainAddress} = require('./../../');
|
|
29
31
|
const {signPsbt} = require('./../../');
|
|
30
32
|
|
|
33
|
+
const bufferAsHex = buffer => buffer.toString('hex');
|
|
31
34
|
const chainAddressRowType = 'chain_address';
|
|
32
35
|
const compile = elements => scriptElementsAsScript({elements}).script;
|
|
36
|
+
const componentsOfTx = tx => componentsOfTransaction({transaction: tx});
|
|
33
37
|
const confirmationCount = 6;
|
|
34
38
|
const count = 100;
|
|
35
39
|
const defaultInternalKey = '0350929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0';
|
|
40
|
+
const defaultLocktime = 0;
|
|
41
|
+
const defaultSequence = 0xffffffff;
|
|
42
|
+
const defaultTxVersion = 1;
|
|
36
43
|
const description = 'description';
|
|
44
|
+
const emptyScriptSig = '';
|
|
37
45
|
const {from} = Buffer;
|
|
38
|
-
const {fromBech32} = address;
|
|
39
|
-
const {fromHex} = Transaction;
|
|
40
|
-
const {fromOutputScript} = address;
|
|
41
46
|
const hexAsBuffer = hex => Buffer.from(hex, 'hex');
|
|
47
|
+
const idForTx = transaction => idForTransaction({transaction}).id;
|
|
42
48
|
const interval = retryCount => 10 * Math.pow(2, retryCount);
|
|
43
49
|
const OP_CHECKSIG = 172;
|
|
50
|
+
const p2wpkh = hash => p2wpkhOutputScript({hash}).script;
|
|
44
51
|
const regtestBech32AddressHrp = 'bcrt';
|
|
45
52
|
const smallTokens = 2e5;
|
|
46
53
|
const times = 20;
|
|
47
|
-
const {toOutputScript} = address;
|
|
48
54
|
const tokens = 1e6;
|
|
55
|
+
const transactionSighashDefault = 0;
|
|
49
56
|
const txIdHexByteLength = 64;
|
|
50
57
|
|
|
51
58
|
// Funding a transaction should result in a funded PSBT
|
|
@@ -107,11 +114,11 @@ test(`Fund PSBT`, async () => {
|
|
|
107
114
|
|
|
108
115
|
equal(output.tokens, tokens, 'Got expected tokens output');
|
|
109
116
|
|
|
110
|
-
const {
|
|
117
|
+
const {program, version} = decodeBech32Address({address});
|
|
111
118
|
|
|
112
119
|
const prefix = `${Buffer.from([version]).toString('hex')}14`;
|
|
113
120
|
|
|
114
|
-
const expectedOutput = `${prefix}${
|
|
121
|
+
const expectedOutput = `${prefix}${bufferAsHex(program)}`;
|
|
115
122
|
|
|
116
123
|
equal(output.output_script, expectedOutput, 'Got expected output script');
|
|
117
124
|
|
|
@@ -127,9 +134,9 @@ test(`Fund PSBT`, async () => {
|
|
|
127
134
|
try {
|
|
128
135
|
await generate({count});
|
|
129
136
|
|
|
130
|
-
const keyPair1 = ecp.makeRandom({
|
|
131
|
-
const keyPair2 = ecp.makeRandom({
|
|
132
|
-
const unusedKey = ecp.makeRandom({
|
|
137
|
+
const keyPair1 = ecp.makeRandom({});
|
|
138
|
+
const keyPair2 = ecp.makeRandom({});
|
|
139
|
+
const unusedKey = ecp.makeRandom({});
|
|
133
140
|
|
|
134
141
|
const witnessScript = compile([
|
|
135
142
|
from(unusedKey.publicKey).slice(1),
|
|
@@ -168,31 +175,42 @@ test(`Fund PSBT`, async () => {
|
|
|
168
175
|
// Send the tx to the chain
|
|
169
176
|
await broadcastChainTransaction({lnd, transaction: signed.transaction});
|
|
170
177
|
|
|
171
|
-
|
|
172
|
-
const tx = new Transaction();
|
|
178
|
+
const spends = componentsOfTx(signed.transaction).outputs;
|
|
173
179
|
|
|
174
180
|
// The new tx spends the Taproot output
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
181
|
+
const inputs = [{
|
|
182
|
+
id: idForTx(signed.transaction),
|
|
183
|
+
script: emptyScriptSig,
|
|
184
|
+
sequence: defaultSequence,
|
|
185
|
+
vout: spends.findIndex(n => n.tokens === tokens),
|
|
186
|
+
}];
|
|
179
187
|
|
|
180
188
|
// Make an output to pay back into the wallet
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
);
|
|
189
|
+
const chainAddress = await createChainAddress({lnd});
|
|
190
|
+
|
|
191
|
+
const {program} = decodeBech32Address({address: chainAddress.address});
|
|
185
192
|
|
|
186
193
|
// Add output to the pay back transaction
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
194
|
+
const outputs = [{
|
|
195
|
+
script: bufferAsHex(p2wpkh(program)),
|
|
196
|
+
tokens: smallTokens,
|
|
197
|
+
}];
|
|
198
|
+
|
|
199
|
+
// Make a new tx that will spend the output back into the wallet
|
|
200
|
+
const unsigned = transactionFromComponents({
|
|
201
|
+
inputs,
|
|
202
|
+
outputs,
|
|
203
|
+
locktime: defaultLocktime,
|
|
204
|
+
version: defaultTxVersion,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const [hashToSign] = inputs.map((input, i) => {
|
|
208
|
+
return v1HashToSign({
|
|
209
|
+
sighash: transactionSighashDefault,
|
|
210
|
+
spends: [{tokens, script: output.script}],
|
|
211
|
+
transaction: unsigned.transaction,
|
|
212
|
+
vin: i,
|
|
213
|
+
}).hash;
|
|
196
214
|
});
|
|
197
215
|
|
|
198
216
|
// Ready for private key combining
|
|
@@ -205,22 +223,33 @@ test(`Fund PSBT`, async () => {
|
|
|
205
223
|
hash,
|
|
206
224
|
private_key: Buffer.from(combinedKey).toString('hex'),
|
|
207
225
|
public_key: Buffer.from(combinedPoint).toString('hex'),
|
|
208
|
-
sign_hash: hashToSign
|
|
226
|
+
sign_hash: hashToSign,
|
|
209
227
|
});
|
|
210
228
|
|
|
211
|
-
const signature =
|
|
212
|
-
|
|
213
|
-
// Add the signature to the input
|
|
214
|
-
|
|
229
|
+
const {signature} = signedInput;
|
|
230
|
+
|
|
231
|
+
// Add the signature to the input and serialize the signed transaction
|
|
232
|
+
const {transaction} = transactionFromComponents({
|
|
233
|
+
outputs,
|
|
234
|
+
inputs: inputs.map(input => ({
|
|
235
|
+
id: input.id,
|
|
236
|
+
script: input.script,
|
|
237
|
+
sequence: input.sequence,
|
|
238
|
+
vout: input.vout,
|
|
239
|
+
witness: [signature],
|
|
240
|
+
})),
|
|
241
|
+
locktime: defaultLocktime,
|
|
242
|
+
version: defaultTxVersion,
|
|
243
|
+
});
|
|
215
244
|
|
|
216
|
-
await broadcastChainTransaction({lnd, transaction
|
|
245
|
+
await broadcastChainTransaction({lnd, transaction});
|
|
217
246
|
|
|
218
247
|
await asyncRetry({interval, times}, async () => {
|
|
219
248
|
await generate({});
|
|
220
249
|
|
|
221
250
|
const {utxos} = await getUtxos({lnd});
|
|
222
251
|
|
|
223
|
-
const utxo = utxos.find(n => n.transaction_id ===
|
|
252
|
+
const utxo = utxos.find(n => n.transaction_id === idForTx(transaction));
|
|
224
253
|
|
|
225
254
|
if (!utxo || !utxo.confirmation_count) {
|
|
226
255
|
throw new Error('ExpectedReceivedTaprootSpend');
|
|
@@ -234,7 +263,7 @@ test(`Fund PSBT`, async () => {
|
|
|
234
263
|
try {
|
|
235
264
|
await generate({count});
|
|
236
265
|
|
|
237
|
-
const keyPair = ecp.makeRandom({
|
|
266
|
+
const keyPair = ecp.makeRandom({});
|
|
238
267
|
|
|
239
268
|
const witnessScript = compile([
|
|
240
269
|
from(keyPair.publicKey.slice(1)),
|
|
@@ -264,35 +293,50 @@ test(`Fund PSBT`, async () => {
|
|
|
264
293
|
// Send the tx to the chain
|
|
265
294
|
await broadcastChainTransaction({lnd, transaction: signed.transaction});
|
|
266
295
|
|
|
267
|
-
|
|
268
|
-
const tx = new Transaction();
|
|
296
|
+
const spends = componentsOfTx(signed.transaction).outputs;
|
|
269
297
|
|
|
270
298
|
// The new tx spends the Taproot output
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
299
|
+
const inputs = [{
|
|
300
|
+
id: idForTx(signed.transaction),
|
|
301
|
+
script: emptyScriptSig,
|
|
302
|
+
sequence: defaultSequence,
|
|
303
|
+
vout: spends.findIndex(n => n.tokens === tokens),
|
|
304
|
+
}];
|
|
275
305
|
|
|
276
306
|
// Make an output to pay back into the wallet
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
);
|
|
307
|
+
const chainAddress = await createChainAddress({lnd});
|
|
308
|
+
|
|
309
|
+
const {program} = decodeBech32Address({address: chainAddress.address});
|
|
281
310
|
|
|
282
311
|
// Add output to the pay back transaction
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
312
|
+
const outputs = [{
|
|
313
|
+
script: bufferAsHex(p2wpkh(program)),
|
|
314
|
+
tokens: smallTokens,
|
|
315
|
+
}];
|
|
316
|
+
|
|
317
|
+
// Make a new tx that will spend the output back into the wallet
|
|
318
|
+
const unsigned = transactionFromComponents({
|
|
319
|
+
inputs,
|
|
320
|
+
outputs,
|
|
321
|
+
locktime: defaultLocktime,
|
|
322
|
+
version: defaultTxVersion,
|
|
293
323
|
});
|
|
294
324
|
|
|
295
|
-
const
|
|
325
|
+
const [hashToSign] = inputs.map((input, i) => {
|
|
326
|
+
return v1HashToSign({
|
|
327
|
+
leaf: {script: witnessScript},
|
|
328
|
+
sighash: transactionSighashDefault,
|
|
329
|
+
spends: [{tokens, script: output.script}],
|
|
330
|
+
transaction: unsigned.transaction,
|
|
331
|
+
vin: i,
|
|
332
|
+
}).hash;
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
const hashBuffer = hexAsBuffer(hashToSign);
|
|
336
|
+
|
|
337
|
+
const schnorrSig = signSchnorr(hashBuffer, from(keyPair.privateKey));
|
|
338
|
+
|
|
339
|
+
const signature = bufferAsHex(from(schnorrSig));
|
|
296
340
|
|
|
297
341
|
const {block} = controlBlock({
|
|
298
342
|
external_key: output.external_key,
|
|
@@ -300,23 +344,28 @@ test(`Fund PSBT`, async () => {
|
|
|
300
344
|
script_branches: branches,
|
|
301
345
|
});
|
|
302
346
|
|
|
303
|
-
// Add the signature to the input
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
347
|
+
// Add the signature to the input and serialize the signed transaction
|
|
348
|
+
const {transaction} = transactionFromComponents({
|
|
349
|
+
outputs,
|
|
350
|
+
inputs: inputs.map(input => ({
|
|
351
|
+
id: input.id,
|
|
352
|
+
script: input.script,
|
|
353
|
+
sequence: input.sequence,
|
|
354
|
+
vout: input.vout,
|
|
355
|
+
witness: [signature, witnessScript, block],
|
|
356
|
+
})),
|
|
357
|
+
locktime: defaultLocktime,
|
|
358
|
+
version: defaultTxVersion,
|
|
310
359
|
});
|
|
311
360
|
|
|
312
|
-
await broadcastChainTransaction({lnd, transaction
|
|
361
|
+
await broadcastChainTransaction({lnd, transaction});
|
|
313
362
|
|
|
314
363
|
await asyncRetry({interval, times}, async () => {
|
|
315
364
|
await generate({});
|
|
316
365
|
|
|
317
366
|
const {utxos} = await getUtxos({lnd});
|
|
318
367
|
|
|
319
|
-
const utxo = utxos.find(n => n.transaction_id ===
|
|
368
|
+
const utxo = utxos.find(n => n.transaction_id === idForTx(transaction));
|
|
320
369
|
|
|
321
370
|
if (!utxo || !utxo.confirmation_count) {
|
|
322
371
|
throw new Error('ExpectedReceivedTaprootSpend');
|
|
@@ -334,19 +383,17 @@ test(`Fund PSBT`, async () => {
|
|
|
334
383
|
try {
|
|
335
384
|
await generate({count});
|
|
336
385
|
|
|
337
|
-
const keyPair = ecp.makeRandom({
|
|
386
|
+
const keyPair = ecp.makeRandom({});
|
|
338
387
|
|
|
339
388
|
const output = v1OutputScript({
|
|
340
389
|
internal_key: from(keyPair.publicKey).toString('hex'),
|
|
341
390
|
});
|
|
342
391
|
|
|
343
|
-
const outputScript = hexAsBuffer(output.script);
|
|
344
|
-
|
|
345
392
|
const [utxo] = (await getUtxos({lnd})).utxos.reverse();
|
|
346
393
|
|
|
347
394
|
// Make a PSBT paying to the Taproot output
|
|
348
395
|
const {psbt} = createPsbt({
|
|
349
|
-
outputs: [{tokens, script:
|
|
396
|
+
outputs: [{tokens, script: output.script}],
|
|
350
397
|
utxos: [{id: utxo.transaction_id, vout: utxo.transaction_vout}],
|
|
351
398
|
});
|
|
352
399
|
|
|
@@ -359,52 +406,74 @@ test(`Fund PSBT`, async () => {
|
|
|
359
406
|
// Send the tx to the chain
|
|
360
407
|
await broadcastChainTransaction({lnd, transaction: signed.transaction});
|
|
361
408
|
|
|
362
|
-
|
|
363
|
-
const tx = new Transaction();
|
|
409
|
+
const spends = componentsOfTx(signed.transaction).outputs;
|
|
364
410
|
|
|
365
411
|
// The new tx spends the Taproot output
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
412
|
+
const inputs = [{
|
|
413
|
+
id: idForTx(signed.transaction),
|
|
414
|
+
script: emptyScriptSig,
|
|
415
|
+
sequence: defaultSequence,
|
|
416
|
+
vout: spends.findIndex(n => n.tokens === tokens),
|
|
417
|
+
}];
|
|
370
418
|
|
|
371
419
|
// Make an output to pay back into the wallet
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
);
|
|
420
|
+
const chainAddress = await createChainAddress({lnd});
|
|
421
|
+
|
|
422
|
+
const {program} = decodeBech32Address({address: chainAddress.address});
|
|
376
423
|
|
|
377
424
|
// Add output to the pay back transaction
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
425
|
+
const outputs = [{
|
|
426
|
+
script: bufferAsHex(p2wpkh(program)),
|
|
427
|
+
tokens: smallTokens,
|
|
428
|
+
}];
|
|
429
|
+
|
|
430
|
+
// Make a new tx that will spend the output back into the wallet
|
|
431
|
+
const unsigned = transactionFromComponents({
|
|
432
|
+
inputs,
|
|
433
|
+
outputs,
|
|
434
|
+
locktime: defaultLocktime,
|
|
435
|
+
version: defaultTxVersion,
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
const [hashToSign] = inputs.map((input, i) => {
|
|
439
|
+
return v1HashToSign({
|
|
440
|
+
sighash: transactionSighashDefault,
|
|
441
|
+
spends: [{tokens, script: output.script}],
|
|
442
|
+
transaction: unsigned.transaction,
|
|
443
|
+
vin: i,
|
|
444
|
+
}).hash;
|
|
387
445
|
});
|
|
388
446
|
|
|
389
447
|
const signedInput = signHash({
|
|
390
448
|
private_key: from(keyPair.privateKey).toString('hex'),
|
|
391
449
|
public_key: from(keyPair.publicKey).toString('hex'),
|
|
392
|
-
sign_hash: hashToSign
|
|
450
|
+
sign_hash: hashToSign,
|
|
393
451
|
});
|
|
394
452
|
|
|
395
|
-
const signature =
|
|
396
|
-
|
|
397
|
-
// Add the signature to the input
|
|
398
|
-
|
|
453
|
+
const {signature} = signedInput;
|
|
454
|
+
|
|
455
|
+
// Add the signature to the input and serialize the signed transaction
|
|
456
|
+
const {transaction} = transactionFromComponents({
|
|
457
|
+
outputs,
|
|
458
|
+
inputs: inputs.map(input => ({
|
|
459
|
+
id: input.id,
|
|
460
|
+
script: input.script,
|
|
461
|
+
sequence: input.sequence,
|
|
462
|
+
vout: input.vout,
|
|
463
|
+
witness: [signature],
|
|
464
|
+
})),
|
|
465
|
+
locktime: defaultLocktime,
|
|
466
|
+
version: defaultTxVersion,
|
|
467
|
+
});
|
|
399
468
|
|
|
400
|
-
await broadcastChainTransaction({lnd, transaction
|
|
469
|
+
await broadcastChainTransaction({lnd, transaction});
|
|
401
470
|
|
|
402
471
|
await asyncRetry({interval, times}, async () => {
|
|
403
472
|
await generate({});
|
|
404
473
|
|
|
405
474
|
const {utxos} = await getUtxos({lnd});
|
|
406
475
|
|
|
407
|
-
const utxo = utxos.find(n => n.transaction_id ===
|
|
476
|
+
const utxo = utxos.find(n => n.transaction_id === idForTx(transaction));
|
|
408
477
|
|
|
409
478
|
if (!utxo || !utxo.confirmation_count) {
|
|
410
479
|
throw new Error('ExpectedReceivedTaprootSpend');
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
const {join} = require('path');
|
|
2
|
-
const {spawn} = require('child_process');
|
|
3
|
-
|
|
4
|
-
const asyncAuto = require('async/auto');
|
|
5
|
-
const openPortFinder = require('portfinder');
|
|
6
|
-
const {networks} = require('bitcoinjs-lib');
|
|
7
|
-
const {payments} = require('bitcoinjs-lib');
|
|
8
|
-
const tinysecp = require('tiny-secp256k1');
|
|
9
|
-
|
|
10
|
-
const knownDaemons = ['btcd'];
|
|
11
|
-
const localhost = '127.0.0.1';
|
|
12
|
-
const notFoundIndex = -1;
|
|
13
|
-
const {p2pkh} = payments;
|
|
14
|
-
const rpcServerReady = /RPC.server.listening/;
|
|
15
|
-
const unableToStartServer = /Unable.to.start.server/;
|
|
16
|
-
|
|
17
|
-
/** Start a BTCSuite Type Daemon
|
|
18
|
-
|
|
19
|
-
{
|
|
20
|
-
daemon: <Daemon Name String>
|
|
21
|
-
dir: <Data Directory String>
|
|
22
|
-
[is_tls]: <Uses TLS Bool>
|
|
23
|
-
mining_public_key: <Mining Public Key Hex String>
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
@returns
|
|
27
|
-
<Daemon Object>
|
|
28
|
-
|
|
29
|
-
@returns via cbk
|
|
30
|
-
{
|
|
31
|
-
daemon: <Daemon Child Process Object>
|
|
32
|
-
listen_port: <Listen Port Number>
|
|
33
|
-
rpc_cert: <RPC Cert Path String>
|
|
34
|
-
rpc_port: <RPC Port Number>
|
|
35
|
-
}
|
|
36
|
-
*/
|
|
37
|
-
module.exports = (args, cbk) => {
|
|
38
|
-
return asyncAuto({
|
|
39
|
-
// Import ECPair library
|
|
40
|
-
ecp: async () => (await import('ecpair')).ECPairFactory(tinysecp),
|
|
41
|
-
|
|
42
|
-
// Check arguments
|
|
43
|
-
validate: cbk => {
|
|
44
|
-
if (knownDaemons.indexOf(args.daemon) === notFoundIndex) {
|
|
45
|
-
return cbk([400, 'ExpectedBtcsuiteDaemonName', args.daemon]);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (!args.dir) {
|
|
49
|
-
return cbk([400, 'ExpectedDirectoryForDaemon']);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (!args.mining_public_key) {
|
|
53
|
-
return cbk([400, 'ExpectedMiningPublicKeyForDaemon']);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return cbk();
|
|
57
|
-
},
|
|
58
|
-
|
|
59
|
-
// Get an open P2P listen port
|
|
60
|
-
listenPort: ['validate', ({}, cbk) => {
|
|
61
|
-
const port = 14567 + Math.round(Math.random() * 1000);
|
|
62
|
-
|
|
63
|
-
const stopPort = port + 1000;
|
|
64
|
-
|
|
65
|
-
return openPortFinder.getPort({port, stopPort}, (err, port) => {
|
|
66
|
-
if (!!err) {
|
|
67
|
-
return cbk([500, 'FailedToFindOpenPortForListenPort', err]);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return cbk(null, port);
|
|
71
|
-
});
|
|
72
|
-
}],
|
|
73
|
-
|
|
74
|
-
// Get an open RPC listen port
|
|
75
|
-
rpcPort: ['listenPort', ({}, cbk) => {
|
|
76
|
-
const port = 14567 + Math.round(Math.random() * 1000);
|
|
77
|
-
|
|
78
|
-
const stopPort = port + 1000;
|
|
79
|
-
|
|
80
|
-
return openPortFinder.getPort({port, stopPort}, (err, port) => {
|
|
81
|
-
if (!!err) {
|
|
82
|
-
return cbk([500, 'FailedToFindOpenPortForRpc', err]);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return cbk(null, port);
|
|
86
|
-
});
|
|
87
|
-
}],
|
|
88
|
-
|
|
89
|
-
// Spin up the chain daemon
|
|
90
|
-
spawnDaemon: [
|
|
91
|
-
'ecp',
|
|
92
|
-
'listenPort',
|
|
93
|
-
'rpcPort',
|
|
94
|
-
({ecp, listenPort, rpcPort}, cbk) =>
|
|
95
|
-
{
|
|
96
|
-
const miningKey = Buffer.from(args.mining_public_key, 'hex');
|
|
97
|
-
const network = networks.testnet;
|
|
98
|
-
|
|
99
|
-
const pubkey = ecp.fromPublicKey(miningKey, network).publicKey;
|
|
100
|
-
|
|
101
|
-
const daemon = spawn(args.daemon, [
|
|
102
|
-
'--datadir', args.dir,
|
|
103
|
-
'--debuglevel', 'trace',
|
|
104
|
-
'--listen', `${localhost}:${listenPort}`,
|
|
105
|
-
'--logdir', args.dir,
|
|
106
|
-
'--miningaddr', p2pkh({network, pubkey}).address,
|
|
107
|
-
(!args.is_tls ? '--notls' : null),
|
|
108
|
-
'--regtest',
|
|
109
|
-
'--relaynonstd',
|
|
110
|
-
'--rpccert', join(args.dir, 'rpc.cert'),
|
|
111
|
-
'--rpckey', join(args.dir, 'rpc.key'),
|
|
112
|
-
'--rpclisten', `${localhost}:${rpcPort}`,
|
|
113
|
-
'--rpcpass', '0k39BVOdg4uuS7qNCG2jbIXNpwU7d3Ft87PpHPPoCfk=',
|
|
114
|
-
'--rpcuser', 'bitcoinrpc',
|
|
115
|
-
'--txindex',
|
|
116
|
-
]);
|
|
117
|
-
|
|
118
|
-
daemon.stdout.on('data', data => {
|
|
119
|
-
if (unableToStartServer.test(`${data}`)) {
|
|
120
|
-
return cbk([503, 'SpawnDaemonFailure', `${data}`, args]);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (rpcServerReady.test(`${data}`)) {
|
|
124
|
-
return cbk(null, daemon);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return;
|
|
128
|
-
});
|
|
129
|
-
}],
|
|
130
|
-
},
|
|
131
|
-
(err, res) => {
|
|
132
|
-
if (!!err) {
|
|
133
|
-
return cbk(err);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return cbk(null, {
|
|
137
|
-
daemon: res.spawnDaemon,
|
|
138
|
-
listen_port: res.listenPort,
|
|
139
|
-
rpc_cert: join(args.dir, 'rpc.cert'),
|
|
140
|
-
rpc_port: res.rpcPort,
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
};
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
const {encode} = require('varuint-bitcoin');
|
|
2
|
-
|
|
3
|
-
const {address} = require('bitcoinjs-lib');
|
|
4
|
-
const {crypto} = require('bitcoinjs-lib');
|
|
5
|
-
const {networks} = require('bitcoinjs-lib');
|
|
6
|
-
const {payments} = require('bitcoinjs-lib');
|
|
7
|
-
const {script} = require('bitcoinjs-lib');
|
|
8
|
-
const {Transaction} = require('bitcoinjs-lib');
|
|
9
|
-
|
|
10
|
-
const scriptBufAsScript = require('./script_buffers_as_script');
|
|
11
|
-
|
|
12
|
-
const defaultNetwork = 'testnet';
|
|
13
|
-
const encodeSignature = script.signature.encode;
|
|
14
|
-
const hexBase = 16;
|
|
15
|
-
const {p2pkh} = payments;
|
|
16
|
-
const {SIGHASH_ALL} = Transaction;
|
|
17
|
-
const {toOutputScript} = address;
|
|
18
|
-
|
|
19
|
-
/** Create a transaction that sends coins to a destination chain address
|
|
20
|
-
|
|
21
|
-
{
|
|
22
|
-
destination: <Destination Address String>
|
|
23
|
-
ecp: <ECPair Object>
|
|
24
|
-
fee: <Fee Tokens To Remove From Spend Number>
|
|
25
|
-
private_key: <WIF Serialized Private Key String>
|
|
26
|
-
spend_transaction_id: <Transaction Id to Spend Hex String>
|
|
27
|
-
spend_vout: <Vout to Spend Number>
|
|
28
|
-
tokens: <Tokens to Spend Number>
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
@returns
|
|
32
|
-
{
|
|
33
|
-
transaction: <Transaction Hex Serialized String>
|
|
34
|
-
}
|
|
35
|
-
*/
|
|
36
|
-
module.exports = args => {
|
|
37
|
-
if (!args.destination) {
|
|
38
|
-
throw new Error('ExpectedDestinationAddressToSendTokensTo');
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (!args.ecp) {
|
|
42
|
-
throw new Error('ExpectedEcpairObjectToChainSendTransaction');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (!args.private_key) {
|
|
46
|
-
throw new Error('ExpectedPrivateKeyToAuthorizeSend');
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (!args.spend_transaction_id) {
|
|
50
|
-
throw new Error('ExpectedOutpointTxIdToSpend');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (args.spend_vout === undefined) {
|
|
54
|
-
throw new Error('ExpectedOutpointVoutToSpend');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (!args.tokens) {
|
|
58
|
-
throw new Error('ExpectedTokenCountToSend');
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const network = networks[defaultNetwork];
|
|
62
|
-
|
|
63
|
-
const keyPair = args.ecp.fromWIF(args.private_key, network);
|
|
64
|
-
const outputScript = toOutputScript(args.destination, network);
|
|
65
|
-
const tx = new Transaction();
|
|
66
|
-
|
|
67
|
-
const outpointHash = Buffer.from(args.spend_transaction_id, 'hex').reverse();
|
|
68
|
-
|
|
69
|
-
tx.addInput(outpointHash, args.spend_vout);
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
tx.addOutput(outputScript, (args.tokens - (args.fee || 0)));
|
|
73
|
-
} catch (err) {
|
|
74
|
-
throw new Error('ErrorAddingOutputToSendOnChainTransaction');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const sigHashAll = parseInt(SIGHASH_ALL, hexBase);
|
|
78
|
-
|
|
79
|
-
[keyPair].forEach((signingKey, vin) => {
|
|
80
|
-
const flag = sigHashAll;
|
|
81
|
-
const {publicKey} = signingKey;
|
|
82
|
-
|
|
83
|
-
const scriptPub = p2pkh({network, pubkey: publicKey}).output;
|
|
84
|
-
|
|
85
|
-
const sigHash = tx.hashForSignature(vin, scriptPub, flag);
|
|
86
|
-
|
|
87
|
-
const signature = signingKey.sign(sigHash);
|
|
88
|
-
|
|
89
|
-
const sig = encodeSignature(signature, flag);
|
|
90
|
-
|
|
91
|
-
const sigPush = Buffer.concat([encode(sig.length), sig]);
|
|
92
|
-
const pubKeyPush = Buffer.concat([encode(publicKey.length), publicKey]);
|
|
93
|
-
|
|
94
|
-
const scriptSig = Buffer.concat([sigPush, pubKeyPush]);
|
|
95
|
-
|
|
96
|
-
tx.setInputScript(vin, scriptSig);
|
|
97
|
-
|
|
98
|
-
return;
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
return {transaction: tx.toHex()};
|
|
102
|
-
};
|