@bitcoinerlab/descriptors 2.0.3 → 2.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/README.md +2 -2
- package/dist/descriptors.d.ts +86 -2
- package/dist/descriptors.js +292 -9
- package/dist/psbt.d.ts +2 -1
- package/dist/psbt.js +17 -28
- package/package.json +8 -5
package/README.md
CHANGED
|
@@ -84,10 +84,10 @@ To call `updatePsbtAsInput()`, use the following syntax:
|
|
|
84
84
|
```javascript
|
|
85
85
|
import { Psbt } from 'bitcoinjs-lib';
|
|
86
86
|
const psbt = new Psbt();
|
|
87
|
-
const inputFinalizer = output.updatePsbtAsInput({ psbt, txHex, vout });
|
|
87
|
+
const inputFinalizer = output.updatePsbtAsInput({ psbt, txHex, vout, rbf });
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
-
Here, `psbt` refers to an instance of the [bitcoinjs-lib Psbt class](https://github.com/bitcoinjs/bitcoinjs-lib). The parameter `txHex` denotes a hex string that serializes the previous transaction containing this output. Meanwhile, `vout` is an integer that marks the position of the output within that transaction.
|
|
90
|
+
Here, `psbt` refers to an instance of the [bitcoinjs-lib Psbt class](https://github.com/bitcoinjs/bitcoinjs-lib). The parameter `txHex` denotes a hex string that serializes the previous transaction containing this output. Meanwhile, `vout` is an integer that marks the position of the output within that transaction. Finally, `rbf` is an optional parameter (defaulting to `true`) used to indicate whether the transaction uses Replace-By-Fee (RBF). When RBF is enabled, transactions can be replaced while they are in the mempool with others that have higher fees. Note that RBF is enabled for the entire transaction if at least one input signals it. Also, note that transactions using relative time locks inherently opt into RBF due to the `nSequence` range used.
|
|
91
91
|
|
|
92
92
|
The method returns the `inputFinalizer()` function. This finalizer function completes a PSBT input by adding the unlocking script (`scriptWitness` or `scriptSig`) that satisfies the previous output's spending conditions. Bear in mind that both `scriptSig` and `scriptWitness` incorporate signatures. As such, you should complete all necessary signing operations before calling `inputFinalizer()`. Detailed [explanations on the `inputFinalizer` method](#signers-and-finalizers-finalize-psbt-input) can be found in the Signers and Finalizers section.
|
|
93
93
|
|
package/dist/descriptors.d.ts
CHANGED
|
@@ -115,8 +115,40 @@ export declare function DescriptorsFactory(ecc: TinySecp256k1Interface): {
|
|
|
115
115
|
getNetwork(): Network;
|
|
116
116
|
/**
|
|
117
117
|
* Whether this `Output` is Segwit.
|
|
118
|
+
*
|
|
119
|
+
* *NOTE:* When the descriptor in an input is `addr(address)`, it is assumed
|
|
120
|
+
* that any `addr(SH_TYPE_ADDRESS)` is in fact a Segwit `SH_WPKH`
|
|
121
|
+
* (Script Hash-Witness Public Key Hash).
|
|
122
|
+
* For inputs using arbitrary scripts (not standard addresses),
|
|
123
|
+
* use a descriptor in the format `sh(MINISCRIPT)`.
|
|
124
|
+
*
|
|
118
125
|
*/
|
|
119
126
|
isSegwit(): boolean | undefined;
|
|
127
|
+
/**
|
|
128
|
+
* Returns the tuple: `{ isPKH: boolean; isWPKH: boolean; isSH: boolean; }`
|
|
129
|
+
* for this Output.
|
|
130
|
+
*/
|
|
131
|
+
guessOutput(): {
|
|
132
|
+
isPKH: boolean;
|
|
133
|
+
isWPKH: boolean;
|
|
134
|
+
isSH: boolean;
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Computes the Weight Unit contributions of this Output as if it were the
|
|
138
|
+
* input in a tx.
|
|
139
|
+
*
|
|
140
|
+
* *NOTE:* When the descriptor in an input is `addr(address)`, it is assumed
|
|
141
|
+
* that any `addr(SH_TYPE_ADDRESS)` is in fact a Segwit `SH_WPKH`
|
|
142
|
+
* (Script Hash-Witness Public Key Hash).
|
|
143
|
+
* For inputs using arbitrary scripts (not standard addresses),
|
|
144
|
+
* use a descriptor in the format `sh(MINISCRIPT)`.
|
|
145
|
+
*/
|
|
146
|
+
inputWeight(isSegwitTx: boolean, signatures: PartialSig[] | 'DANGEROUSLY_USE_FAKE_SIGNATURES'): number;
|
|
147
|
+
/**
|
|
148
|
+
* Computes the Weight Unit contributions of this Output as if it were the
|
|
149
|
+
* output in a tx.
|
|
150
|
+
*/
|
|
151
|
+
outputWeight(): number;
|
|
120
152
|
/** @deprecated - Use updatePsbtAsInput instead
|
|
121
153
|
* @hidden
|
|
122
154
|
*/
|
|
@@ -126,6 +158,7 @@ export declare function DescriptorsFactory(ecc: TinySecp256k1Interface): {
|
|
|
126
158
|
txId?: string;
|
|
127
159
|
value?: number;
|
|
128
160
|
vout: number;
|
|
161
|
+
rbf?: boolean;
|
|
129
162
|
}): number;
|
|
130
163
|
/**
|
|
131
164
|
* Sets this output as an input of the provided `psbt` and updates the
|
|
@@ -144,6 +177,14 @@ export declare function DescriptorsFactory(ecc: TinySecp256k1Interface): {
|
|
|
144
177
|
*
|
|
145
178
|
* When unsure, always use `txHex`, and skip `txId` and `value` for safety.
|
|
146
179
|
*
|
|
180
|
+
* Use `rbf` to mark whether this tx can be replaced with another with
|
|
181
|
+
* higher fee while being in the mempool. Note that a tx will automatically
|
|
182
|
+
* be marked as replacable if a single input requests it.
|
|
183
|
+
* Note that any transaction using a relative timelock (nSequence < 0x80000000)
|
|
184
|
+
* also falls within the RBF range (nSequence < 0xFFFFFFFE), making it
|
|
185
|
+
* inherently replaceable. So don't set `rbf` to false if this is tx uses
|
|
186
|
+
* relative time locks.
|
|
187
|
+
*
|
|
147
188
|
* @returns A finalizer function to be used after signing the `psbt`.
|
|
148
189
|
* This function ensures that this input is properly finalized.
|
|
149
190
|
* The finalizer has this signature:
|
|
@@ -151,12 +192,13 @@ export declare function DescriptorsFactory(ecc: TinySecp256k1Interface): {
|
|
|
151
192
|
* `( { psbt, validate = true } : { psbt: Psbt; validate: boolean | undefined } ) => void`
|
|
152
193
|
*
|
|
153
194
|
*/
|
|
154
|
-
updatePsbtAsInput({ psbt, txHex, txId, value, vout }: {
|
|
195
|
+
updatePsbtAsInput({ psbt, txHex, txId, value, vout, rbf }: {
|
|
155
196
|
psbt: Psbt;
|
|
156
197
|
txHex?: string;
|
|
157
198
|
txId?: string;
|
|
158
199
|
value?: number;
|
|
159
200
|
vout: number;
|
|
201
|
+
rbf?: boolean;
|
|
160
202
|
}): ({ psbt, validate }: {
|
|
161
203
|
psbt: Psbt;
|
|
162
204
|
/** Runs further test on the validity of the signatures.
|
|
@@ -365,8 +407,40 @@ export declare function DescriptorsFactory(ecc: TinySecp256k1Interface): {
|
|
|
365
407
|
getNetwork(): Network;
|
|
366
408
|
/**
|
|
367
409
|
* Whether this `Output` is Segwit.
|
|
410
|
+
*
|
|
411
|
+
* *NOTE:* When the descriptor in an input is `addr(address)`, it is assumed
|
|
412
|
+
* that any `addr(SH_TYPE_ADDRESS)` is in fact a Segwit `SH_WPKH`
|
|
413
|
+
* (Script Hash-Witness Public Key Hash).
|
|
414
|
+
* For inputs using arbitrary scripts (not standard addresses),
|
|
415
|
+
* use a descriptor in the format `sh(MINISCRIPT)`.
|
|
416
|
+
*
|
|
368
417
|
*/
|
|
369
418
|
isSegwit(): boolean | undefined;
|
|
419
|
+
/**
|
|
420
|
+
* Returns the tuple: `{ isPKH: boolean; isWPKH: boolean; isSH: boolean; }`
|
|
421
|
+
* for this Output.
|
|
422
|
+
*/
|
|
423
|
+
guessOutput(): {
|
|
424
|
+
isPKH: boolean;
|
|
425
|
+
isWPKH: boolean;
|
|
426
|
+
isSH: boolean;
|
|
427
|
+
};
|
|
428
|
+
/**
|
|
429
|
+
* Computes the Weight Unit contributions of this Output as if it were the
|
|
430
|
+
* input in a tx.
|
|
431
|
+
*
|
|
432
|
+
* *NOTE:* When the descriptor in an input is `addr(address)`, it is assumed
|
|
433
|
+
* that any `addr(SH_TYPE_ADDRESS)` is in fact a Segwit `SH_WPKH`
|
|
434
|
+
* (Script Hash-Witness Public Key Hash).
|
|
435
|
+
* For inputs using arbitrary scripts (not standard addresses),
|
|
436
|
+
* use a descriptor in the format `sh(MINISCRIPT)`.
|
|
437
|
+
*/
|
|
438
|
+
inputWeight(isSegwitTx: boolean, signatures: PartialSig[] | 'DANGEROUSLY_USE_FAKE_SIGNATURES'): number;
|
|
439
|
+
/**
|
|
440
|
+
* Computes the Weight Unit contributions of this Output as if it were the
|
|
441
|
+
* output in a tx.
|
|
442
|
+
*/
|
|
443
|
+
outputWeight(): number;
|
|
370
444
|
/** @deprecated - Use updatePsbtAsInput instead
|
|
371
445
|
* @hidden
|
|
372
446
|
*/
|
|
@@ -376,6 +450,7 @@ export declare function DescriptorsFactory(ecc: TinySecp256k1Interface): {
|
|
|
376
450
|
txId?: string;
|
|
377
451
|
value?: number;
|
|
378
452
|
vout: number;
|
|
453
|
+
rbf?: boolean;
|
|
379
454
|
}): number;
|
|
380
455
|
/**
|
|
381
456
|
* Sets this output as an input of the provided `psbt` and updates the
|
|
@@ -394,6 +469,14 @@ export declare function DescriptorsFactory(ecc: TinySecp256k1Interface): {
|
|
|
394
469
|
*
|
|
395
470
|
* When unsure, always use `txHex`, and skip `txId` and `value` for safety.
|
|
396
471
|
*
|
|
472
|
+
* Use `rbf` to mark whether this tx can be replaced with another with
|
|
473
|
+
* higher fee while being in the mempool. Note that a tx will automatically
|
|
474
|
+
* be marked as replacable if a single input requests it.
|
|
475
|
+
* Note that any transaction using a relative timelock (nSequence < 0x80000000)
|
|
476
|
+
* also falls within the RBF range (nSequence < 0xFFFFFFFE), making it
|
|
477
|
+
* inherently replaceable. So don't set `rbf` to false if this is tx uses
|
|
478
|
+
* relative time locks.
|
|
479
|
+
*
|
|
397
480
|
* @returns A finalizer function to be used after signing the `psbt`.
|
|
398
481
|
* This function ensures that this input is properly finalized.
|
|
399
482
|
* The finalizer has this signature:
|
|
@@ -401,12 +484,13 @@ export declare function DescriptorsFactory(ecc: TinySecp256k1Interface): {
|
|
|
401
484
|
* `( { psbt, validate = true } : { psbt: Psbt; validate: boolean | undefined } ) => void`
|
|
402
485
|
*
|
|
403
486
|
*/
|
|
404
|
-
updatePsbtAsInput({ psbt, txHex, txId, value, vout }: {
|
|
487
|
+
updatePsbtAsInput({ psbt, txHex, txId, value, vout, rbf }: {
|
|
405
488
|
psbt: Psbt;
|
|
406
489
|
txHex?: string;
|
|
407
490
|
txId?: string;
|
|
408
491
|
value?: number;
|
|
409
492
|
vout: number;
|
|
493
|
+
rbf?: boolean;
|
|
410
494
|
}): ({ psbt, validate }: {
|
|
411
495
|
psbt: Psbt;
|
|
412
496
|
/** Runs further test on the validity of the signatures.
|
package/dist/descriptors.js
CHANGED
|
@@ -35,9 +35,14 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
35
35
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
36
36
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
37
37
|
};
|
|
38
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
39
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
|
+
};
|
|
38
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
42
|
exports.DescriptorsFactory = void 0;
|
|
43
|
+
const lodash_memoize_1 = __importDefault(require("lodash.memoize"));
|
|
40
44
|
const bitcoinjs_lib_1 = require("bitcoinjs-lib");
|
|
45
|
+
const varuint_bitcoin_1 = require("varuint-bitcoin");
|
|
41
46
|
const { p2sh, p2wpkh, p2pkh, p2pk, p2wsh, p2tr } = bitcoinjs_lib_1.payments;
|
|
42
47
|
const bip32_1 = require("bip32");
|
|
43
48
|
const ecpair_1 = require("ecpair");
|
|
@@ -57,6 +62,28 @@ function countNonPushOnlyOPs(script) {
|
|
|
57
62
|
throw new Error(`Error: cound not decompile ${script}`);
|
|
58
63
|
return decompile.filter(op => typeof op === 'number' && op > bitcoinjs_lib_1.script.OPS['OP_16']).length;
|
|
59
64
|
}
|
|
65
|
+
function vectorSize(someVector) {
|
|
66
|
+
const length = someVector.length;
|
|
67
|
+
return ((0, varuint_bitcoin_1.encodingLength)(length) +
|
|
68
|
+
someVector.reduce((sum, witness) => {
|
|
69
|
+
return sum + varSliceSize(witness);
|
|
70
|
+
}, 0));
|
|
71
|
+
}
|
|
72
|
+
function varSliceSize(someScript) {
|
|
73
|
+
const length = someScript.length;
|
|
74
|
+
return (0, varuint_bitcoin_1.encodingLength)(length) + length;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* This function will typically return 73; since it assumes a signature size of
|
|
78
|
+
* 72 bytes (this is the max size of a DER encoded signature) and it adds 1
|
|
79
|
+
* extra byte for encoding its length
|
|
80
|
+
*/
|
|
81
|
+
function signatureSize(signature) {
|
|
82
|
+
const length = signature === 'DANGEROUSLY_USE_FAKE_SIGNATURES'
|
|
83
|
+
? 72
|
|
84
|
+
: signature.signature.length;
|
|
85
|
+
return (0, varuint_bitcoin_1.encodingLength)(length) + length;
|
|
86
|
+
}
|
|
60
87
|
/*
|
|
61
88
|
* Returns a bare descriptor without checksum and particularized for a certain
|
|
62
89
|
* index (if desc was a range descriptor)
|
|
@@ -184,22 +211,28 @@ function DescriptorsFactory(ecc) {
|
|
|
184
211
|
}
|
|
185
212
|
try {
|
|
186
213
|
payment = p2pkh({ output, network });
|
|
214
|
+
isSegwit = false;
|
|
187
215
|
}
|
|
188
216
|
catch (e) { }
|
|
189
217
|
try {
|
|
190
218
|
payment = p2sh({ output, network });
|
|
219
|
+
// It assumes that an addr(SH_ADDRESS) is always a add(SH_WPKH) address
|
|
220
|
+
isSegwit = true;
|
|
191
221
|
}
|
|
192
222
|
catch (e) { }
|
|
193
223
|
try {
|
|
194
224
|
payment = p2wpkh({ output, network });
|
|
225
|
+
isSegwit = true;
|
|
195
226
|
}
|
|
196
227
|
catch (e) { }
|
|
197
228
|
try {
|
|
198
229
|
payment = p2wsh({ output, network });
|
|
230
|
+
isSegwit = true;
|
|
199
231
|
}
|
|
200
232
|
catch (e) { }
|
|
201
233
|
try {
|
|
202
234
|
payment = p2tr({ output, network });
|
|
235
|
+
isSegwit = true;
|
|
203
236
|
}
|
|
204
237
|
catch (e) { }
|
|
205
238
|
if (!payment) {
|
|
@@ -480,6 +513,25 @@ function DescriptorsFactory(ecc) {
|
|
|
480
513
|
__classPrivateFieldSet(this, _Output_signersPubKeys, [this.getScriptPubKey()], "f");
|
|
481
514
|
}
|
|
482
515
|
}
|
|
516
|
+
this.getSequence = (0, lodash_memoize_1.default)(this.getSequence);
|
|
517
|
+
this.getLockTime = (0, lodash_memoize_1.default)(this.getLockTime);
|
|
518
|
+
const getSignaturesKey = (signatures) => signatures === 'DANGEROUSLY_USE_FAKE_SIGNATURES'
|
|
519
|
+
? signatures
|
|
520
|
+
: signatures
|
|
521
|
+
.map(s => `${s.pubkey.toString('hex')}-${s.signature.toString('hex')}`)
|
|
522
|
+
.join('|');
|
|
523
|
+
this.getScriptSatisfaction = (0, lodash_memoize_1.default)(this.getScriptSatisfaction,
|
|
524
|
+
// resolver function:
|
|
525
|
+
getSignaturesKey);
|
|
526
|
+
this.guessOutput = (0, lodash_memoize_1.default)(this.guessOutput);
|
|
527
|
+
this.inputWeight = (0, lodash_memoize_1.default)(this.inputWeight,
|
|
528
|
+
// resolver function:
|
|
529
|
+
(isSegwitTx, signatures) => {
|
|
530
|
+
const segwitKey = isSegwitTx ? 'segwit' : 'non-segwit';
|
|
531
|
+
const signaturesKey = getSignaturesKey(signatures);
|
|
532
|
+
return `${segwitKey}-${signaturesKey}`;
|
|
533
|
+
});
|
|
534
|
+
this.outputWeight = (0, lodash_memoize_1.default)(this.outputWeight);
|
|
483
535
|
}
|
|
484
536
|
/**
|
|
485
537
|
* Creates and returns an instance of bitcoinjs-lib
|
|
@@ -607,10 +659,228 @@ function DescriptorsFactory(ecc) {
|
|
|
607
659
|
}
|
|
608
660
|
/**
|
|
609
661
|
* Whether this `Output` is Segwit.
|
|
662
|
+
*
|
|
663
|
+
* *NOTE:* When the descriptor in an input is `addr(address)`, it is assumed
|
|
664
|
+
* that any `addr(SH_TYPE_ADDRESS)` is in fact a Segwit `SH_WPKH`
|
|
665
|
+
* (Script Hash-Witness Public Key Hash).
|
|
666
|
+
* For inputs using arbitrary scripts (not standard addresses),
|
|
667
|
+
* use a descriptor in the format `sh(MINISCRIPT)`.
|
|
668
|
+
*
|
|
610
669
|
*/
|
|
611
670
|
isSegwit() {
|
|
612
671
|
return __classPrivateFieldGet(this, _Output_isSegwit, "f");
|
|
613
672
|
}
|
|
673
|
+
/**
|
|
674
|
+
* Returns the tuple: `{ isPKH: boolean; isWPKH: boolean; isSH: boolean; }`
|
|
675
|
+
* for this Output.
|
|
676
|
+
*/
|
|
677
|
+
guessOutput() {
|
|
678
|
+
function guessSH(output) {
|
|
679
|
+
try {
|
|
680
|
+
bitcoinjs_lib_1.payments.p2sh({ output });
|
|
681
|
+
return true;
|
|
682
|
+
}
|
|
683
|
+
catch (err) {
|
|
684
|
+
return false;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
function guessWPKH(output) {
|
|
688
|
+
try {
|
|
689
|
+
bitcoinjs_lib_1.payments.p2wpkh({ output });
|
|
690
|
+
return true;
|
|
691
|
+
}
|
|
692
|
+
catch (err) {
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
function guessPKH(output) {
|
|
697
|
+
try {
|
|
698
|
+
bitcoinjs_lib_1.payments.p2pkh({ output });
|
|
699
|
+
return true;
|
|
700
|
+
}
|
|
701
|
+
catch (err) {
|
|
702
|
+
return false;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
const isPKH = guessPKH(this.getScriptPubKey());
|
|
706
|
+
const isWPKH = guessWPKH(this.getScriptPubKey());
|
|
707
|
+
const isSH = guessSH(this.getScriptPubKey());
|
|
708
|
+
if ([isPKH, isWPKH, isSH].filter(Boolean).length > 1)
|
|
709
|
+
throw new Error('Cannot have multiple output types.');
|
|
710
|
+
return { isPKH, isWPKH, isSH };
|
|
711
|
+
}
|
|
712
|
+
// References for inputWeight & outputWeight:
|
|
713
|
+
// https://gist.github.com/junderw/b43af3253ea5865ed52cb51c200ac19c
|
|
714
|
+
// https://bitcoinops.org/en/tools/calc-size/
|
|
715
|
+
// Look for byteLength: https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/ts_src/transaction.ts
|
|
716
|
+
// https://github.com/bitcoinjs/coinselect/blob/master/utils.js
|
|
717
|
+
/**
|
|
718
|
+
* Computes the Weight Unit contributions of this Output as if it were the
|
|
719
|
+
* input in a tx.
|
|
720
|
+
*
|
|
721
|
+
* *NOTE:* When the descriptor in an input is `addr(address)`, it is assumed
|
|
722
|
+
* that any `addr(SH_TYPE_ADDRESS)` is in fact a Segwit `SH_WPKH`
|
|
723
|
+
* (Script Hash-Witness Public Key Hash).
|
|
724
|
+
* For inputs using arbitrary scripts (not standard addresses),
|
|
725
|
+
* use a descriptor in the format `sh(MINISCRIPT)`.
|
|
726
|
+
*/
|
|
727
|
+
inputWeight(
|
|
728
|
+
/**
|
|
729
|
+
* Indicates if the transaction is a Segwit transaction.
|
|
730
|
+
* If a transaction isSegwitTx, a single byte is then also required for
|
|
731
|
+
* non-witness inputs to encode the length of the empty witness stack:
|
|
732
|
+
* encodeLength(0) + 0 = 1
|
|
733
|
+
* Read more:
|
|
734
|
+
* https://gist.github.com/junderw/b43af3253ea5865ed52cb51c200ac19c?permalink_comment_id=4760512#gistcomment-4760512
|
|
735
|
+
*/
|
|
736
|
+
isSegwitTx,
|
|
737
|
+
/*
|
|
738
|
+
* Array of `PartialSig`. Each `PartialSig` includes
|
|
739
|
+
* a public key and its corresponding signature. This parameter
|
|
740
|
+
* enables the accurate calculation of signature sizes.
|
|
741
|
+
* Pass 'DANGEROUSLY_USE_FAKE_SIGNATURES' to assume 72 bytes in length.
|
|
742
|
+
* Mainly used for testing.
|
|
743
|
+
*/
|
|
744
|
+
signatures) {
|
|
745
|
+
if (this.isSegwit() && !isSegwitTx)
|
|
746
|
+
throw new Error(`a tx is segwit if at least one input is segwit`);
|
|
747
|
+
const errorMsg = 'Input type not implemented. Currently supported: pkh(KEY), wpkh(KEY), \
|
|
748
|
+
sh(wpkh(KEY)), sh(wsh(MINISCRIPT)), sh(MINISCRIPT), wsh(MINISCRIPT), \
|
|
749
|
+
addr(PKH_ADDRESS), addr(WPKH_ADDRESS), addr(SH_WPKH_ADDRESS).';
|
|
750
|
+
//expand any miniscript-based descriptor. If not miniscript-based, then it's
|
|
751
|
+
//an addr() descriptor. For those, we can only guess their type.
|
|
752
|
+
const expansion = this.expand().expandedExpression;
|
|
753
|
+
const { isPKH, isWPKH, isSH } = this.guessOutput();
|
|
754
|
+
if (!expansion && !isPKH && !isWPKH && !isSH)
|
|
755
|
+
throw new Error(errorMsg);
|
|
756
|
+
const firstSignature = signatures && typeof signatures[0] === 'object'
|
|
757
|
+
? signatures[0]
|
|
758
|
+
: 'DANGEROUSLY_USE_FAKE_SIGNATURES';
|
|
759
|
+
if (expansion ? expansion.startsWith('pkh(') : isPKH) {
|
|
760
|
+
return (
|
|
761
|
+
// Non-segwit: (txid:32) + (vout:4) + (sequence:4) + (script_len:1) + (sig:73) + (pubkey:34)
|
|
762
|
+
(32 + 4 + 4 + 1 + signatureSize(firstSignature) + 34) * 4 +
|
|
763
|
+
//Segwit:
|
|
764
|
+
(isSegwitTx ? 1 : 0));
|
|
765
|
+
}
|
|
766
|
+
else if (expansion ? expansion.startsWith('wpkh(') : isWPKH) {
|
|
767
|
+
if (!isSegwitTx)
|
|
768
|
+
throw new Error('Should be SegwitTx');
|
|
769
|
+
return (
|
|
770
|
+
// Non-segwit: (txid:32) + (vout:4) + (sequence:4) + (script_len:1)
|
|
771
|
+
41 * 4 +
|
|
772
|
+
// Segwit: (push_count:1) + (sig:73) + (pubkey:34)
|
|
773
|
+
(1 + signatureSize(firstSignature) + 34));
|
|
774
|
+
}
|
|
775
|
+
else if (expansion ? expansion.startsWith('sh(wpkh(') : isSH) {
|
|
776
|
+
if (!isSegwitTx)
|
|
777
|
+
throw new Error('Should be SegwitTx');
|
|
778
|
+
return (
|
|
779
|
+
// Non-segwit: (txid:32) + (vout:4) + (sequence:4) + (script_len:1) + (p2wpkh:23)
|
|
780
|
+
// -> p2wpkh_script: OP_0 OP_PUSH20 <public_key_hash>
|
|
781
|
+
// -> p2wpkh: (script_len:1) + (script:22)
|
|
782
|
+
64 * 4 +
|
|
783
|
+
// Segwit: (push_count:1) + (sig:73) + (pubkey:34)
|
|
784
|
+
(1 + signatureSize(firstSignature) + 34));
|
|
785
|
+
}
|
|
786
|
+
else if (expansion?.startsWith('sh(wsh(')) {
|
|
787
|
+
if (!isSegwitTx)
|
|
788
|
+
throw new Error('Should be SegwitTx');
|
|
789
|
+
const witnessScript = this.getWitnessScript();
|
|
790
|
+
if (!witnessScript)
|
|
791
|
+
throw new Error('sh(wsh) must provide witnessScript');
|
|
792
|
+
const payment = bitcoinjs_lib_1.payments.p2sh({
|
|
793
|
+
redeem: bitcoinjs_lib_1.payments.p2wsh({
|
|
794
|
+
redeem: {
|
|
795
|
+
input: this.getScriptSatisfaction(signatures || 'DANGEROUSLY_USE_FAKE_SIGNATURES'),
|
|
796
|
+
output: witnessScript
|
|
797
|
+
}
|
|
798
|
+
})
|
|
799
|
+
});
|
|
800
|
+
if (!payment || !payment.input || !payment.witness)
|
|
801
|
+
throw new Error('Could not create payment');
|
|
802
|
+
return (
|
|
803
|
+
//Non-segwit
|
|
804
|
+
4 * (40 + varSliceSize(payment.input)) +
|
|
805
|
+
//Segwit
|
|
806
|
+
vectorSize(payment.witness));
|
|
807
|
+
}
|
|
808
|
+
else if (expansion?.startsWith('sh(')) {
|
|
809
|
+
const redeemScript = this.getRedeemScript();
|
|
810
|
+
if (!redeemScript)
|
|
811
|
+
throw new Error('sh() must provide redeemScript');
|
|
812
|
+
const payment = bitcoinjs_lib_1.payments.p2sh({
|
|
813
|
+
redeem: {
|
|
814
|
+
input: this.getScriptSatisfaction(signatures || 'DANGEROUSLY_USE_FAKE_SIGNATURES'),
|
|
815
|
+
output: redeemScript
|
|
816
|
+
}
|
|
817
|
+
});
|
|
818
|
+
if (!payment || !payment.input)
|
|
819
|
+
throw new Error('Could not create payment');
|
|
820
|
+
if (payment.witness?.length)
|
|
821
|
+
throw new Error('A legacy p2sh payment should not cointain a witness');
|
|
822
|
+
return (
|
|
823
|
+
//Non-segwit
|
|
824
|
+
4 * (40 + varSliceSize(payment.input)) +
|
|
825
|
+
//Segwit:
|
|
826
|
+
(isSegwitTx ? 1 : 0));
|
|
827
|
+
}
|
|
828
|
+
else if (expansion?.startsWith('wsh(')) {
|
|
829
|
+
const witnessScript = this.getWitnessScript();
|
|
830
|
+
if (!witnessScript)
|
|
831
|
+
throw new Error('wsh must provide witnessScript');
|
|
832
|
+
const payment = bitcoinjs_lib_1.payments.p2wsh({
|
|
833
|
+
redeem: {
|
|
834
|
+
input: this.getScriptSatisfaction(signatures || 'DANGEROUSLY_USE_FAKE_SIGNATURES'),
|
|
835
|
+
output: witnessScript
|
|
836
|
+
}
|
|
837
|
+
});
|
|
838
|
+
if (!payment || !payment.input || !payment.witness)
|
|
839
|
+
throw new Error('Could not create payment');
|
|
840
|
+
return (
|
|
841
|
+
//Non-segwit
|
|
842
|
+
4 * (40 + varSliceSize(payment.input)) +
|
|
843
|
+
//Segwit
|
|
844
|
+
vectorSize(payment.witness));
|
|
845
|
+
}
|
|
846
|
+
else {
|
|
847
|
+
throw new Error(errorMsg);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* Computes the Weight Unit contributions of this Output as if it were the
|
|
852
|
+
* output in a tx.
|
|
853
|
+
*/
|
|
854
|
+
outputWeight() {
|
|
855
|
+
const errorMsg = 'Output type not implemented. Currently supported: pkh(KEY), wpkh(KEY), \
|
|
856
|
+
sh(ANYTHING), wsh(ANYTHING), addr(PKH_ADDRESS), addr(WPKH_ADDRESS), \
|
|
857
|
+
addr(SH_WPKH_ADDRESS)';
|
|
858
|
+
//expand any miniscript-based descriptor. If not miniscript-based, then it's
|
|
859
|
+
//an addr() descriptor. For those, we can only guess their type.
|
|
860
|
+
const expansion = this.expand().expandedExpression;
|
|
861
|
+
const { isPKH, isWPKH, isSH } = this.guessOutput();
|
|
862
|
+
if (!expansion && !isPKH && !isWPKH && !isSH)
|
|
863
|
+
throw new Error(errorMsg);
|
|
864
|
+
if (expansion ? expansion.startsWith('pkh(') : isPKH) {
|
|
865
|
+
// (p2pkh:26) + (amount:8)
|
|
866
|
+
return 34 * 4;
|
|
867
|
+
}
|
|
868
|
+
else if (expansion ? expansion.startsWith('wpkh(') : isWPKH) {
|
|
869
|
+
// (p2wpkh:23) + (amount:8)
|
|
870
|
+
return 31 * 4;
|
|
871
|
+
}
|
|
872
|
+
else if (expansion ? expansion.startsWith('sh(') : isSH) {
|
|
873
|
+
// (p2sh:24) + (amount:8)
|
|
874
|
+
return 32 * 4;
|
|
875
|
+
}
|
|
876
|
+
else if (expansion?.startsWith('wsh(')) {
|
|
877
|
+
// (p2wsh:35) + (amount:8)
|
|
878
|
+
return 43 * 4;
|
|
879
|
+
}
|
|
880
|
+
else {
|
|
881
|
+
throw new Error(errorMsg);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
614
884
|
/** @deprecated - Use updatePsbtAsInput instead
|
|
615
885
|
* @hidden
|
|
616
886
|
*/
|
|
@@ -635,6 +905,14 @@ function DescriptorsFactory(ecc) {
|
|
|
635
905
|
*
|
|
636
906
|
* When unsure, always use `txHex`, and skip `txId` and `value` for safety.
|
|
637
907
|
*
|
|
908
|
+
* Use `rbf` to mark whether this tx can be replaced with another with
|
|
909
|
+
* higher fee while being in the mempool. Note that a tx will automatically
|
|
910
|
+
* be marked as replacable if a single input requests it.
|
|
911
|
+
* Note that any transaction using a relative timelock (nSequence < 0x80000000)
|
|
912
|
+
* also falls within the RBF range (nSequence < 0xFFFFFFFE), making it
|
|
913
|
+
* inherently replaceable. So don't set `rbf` to false if this is tx uses
|
|
914
|
+
* relative time locks.
|
|
915
|
+
*
|
|
638
916
|
* @returns A finalizer function to be used after signing the `psbt`.
|
|
639
917
|
* This function ensures that this input is properly finalized.
|
|
640
918
|
* The finalizer has this signature:
|
|
@@ -642,8 +920,8 @@ function DescriptorsFactory(ecc) {
|
|
|
642
920
|
* `( { psbt, validate = true } : { psbt: Psbt; validate: boolean | undefined } ) => void`
|
|
643
921
|
*
|
|
644
922
|
*/
|
|
645
|
-
updatePsbtAsInput({ psbt, txHex, txId, value, vout //vector output index
|
|
646
|
-
|
|
923
|
+
updatePsbtAsInput({ psbt, txHex, txId, value, vout, //vector output index
|
|
924
|
+
rbf = true }) {
|
|
647
925
|
if (txHex === undefined) {
|
|
648
926
|
console.warn(`Warning: missing txHex may allow fee attacks`);
|
|
649
927
|
}
|
|
@@ -664,7 +942,8 @@ function DescriptorsFactory(ecc) {
|
|
|
664
942
|
scriptPubKey: this.getScriptPubKey(),
|
|
665
943
|
isSegwit,
|
|
666
944
|
witnessScript: this.getWitnessScript(),
|
|
667
|
-
redeemScript: this.getRedeemScript()
|
|
945
|
+
redeemScript: this.getRedeemScript(),
|
|
946
|
+
rbf
|
|
668
947
|
});
|
|
669
948
|
const finalizer = ({ psbt, validate = true }) => this.finalizePsbtInput({ index, psbt, validate });
|
|
670
949
|
return finalizer;
|
|
@@ -802,16 +1081,20 @@ function DescriptorsFactory(ecc) {
|
|
|
802
1081
|
scriptPubKey = out.script;
|
|
803
1082
|
}
|
|
804
1083
|
const locktime = this.getLockTime() || 0;
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
sequence
|
|
1084
|
+
const sequence = this.getSequence();
|
|
1085
|
+
//We don't know whether the user opted for RBF or not. So check that
|
|
1086
|
+
//at least one of the 2 sequences matches.
|
|
1087
|
+
const sequenceNoRBF = sequence !== undefined
|
|
1088
|
+
? sequence
|
|
1089
|
+
: locktime === 0
|
|
1090
|
+
? 0xffffffff
|
|
1091
|
+
: 0xfffffffe;
|
|
1092
|
+
const sequenceRBF = sequence !== undefined ? sequence : 0xfffffffd;
|
|
810
1093
|
const eqBuffers = (buf1, buf2) => buf1 instanceof Buffer && buf2 instanceof Buffer
|
|
811
1094
|
? Buffer.compare(buf1, buf2) === 0
|
|
812
1095
|
: buf1 === buf2;
|
|
813
1096
|
if (Buffer.compare(scriptPubKey, this.getScriptPubKey()) !== 0 ||
|
|
814
|
-
|
|
1097
|
+
(sequenceRBF !== inputSequence && sequenceNoRBF !== inputSequence) ||
|
|
815
1098
|
locktime !== psbt.locktime ||
|
|
816
1099
|
!eqBuffers(this.getWitnessScript(), input.witnessScript) ||
|
|
817
1100
|
!eqBuffers(this.getRedeemScript(), input.redeemScript)) {
|
package/dist/psbt.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export declare function finalScriptsFuncFactory(scriptSatisfaction: Buffer, netw
|
|
|
21
21
|
/**
|
|
22
22
|
* Important: Read comments on descriptor.updatePsbt regarding not passing txHex
|
|
23
23
|
*/
|
|
24
|
-
export declare function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysInfo, scriptPubKey, isSegwit, witnessScript, redeemScript }: {
|
|
24
|
+
export declare function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysInfo, scriptPubKey, isSegwit, witnessScript, redeemScript, rbf }: {
|
|
25
25
|
psbt: Psbt;
|
|
26
26
|
vout: number;
|
|
27
27
|
txHex?: string;
|
|
@@ -34,5 +34,6 @@ export declare function updatePsbt({ psbt, vout, txHex, txId, value, sequence, l
|
|
|
34
34
|
isSegwit: boolean;
|
|
35
35
|
witnessScript: Buffer | undefined;
|
|
36
36
|
redeemScript: Buffer | undefined;
|
|
37
|
+
rbf: boolean;
|
|
37
38
|
}): number;
|
|
38
39
|
export {};
|
package/dist/psbt.js
CHANGED
|
@@ -1,33 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// Copyright (c) 2023 Jose-Luis Landabaso - https://bitcoinerlab.com
|
|
3
3
|
// Distributed under the MIT software license
|
|
4
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
5
|
-
if (k2 === undefined) k2 = k;
|
|
6
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
7
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
8
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
9
|
-
}
|
|
10
|
-
Object.defineProperty(o, k2, desc);
|
|
11
|
-
}) : (function(o, m, k, k2) {
|
|
12
|
-
if (k2 === undefined) k2 = k;
|
|
13
|
-
o[k2] = m[k];
|
|
14
|
-
}));
|
|
15
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
16
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
17
|
-
}) : function(o, v) {
|
|
18
|
-
o["default"] = v;
|
|
19
|
-
});
|
|
20
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
21
|
-
if (mod && mod.__esModule) return mod;
|
|
22
|
-
var result = {};
|
|
23
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
24
|
-
__setModuleDefault(result, mod);
|
|
25
|
-
return result;
|
|
26
|
-
};
|
|
27
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
5
|
exports.updatePsbt = exports.finalScriptsFuncFactory = void 0;
|
|
29
6
|
const bitcoinjs_lib_1 = require("bitcoinjs-lib");
|
|
30
|
-
const
|
|
7
|
+
const varuint_bitcoin_1 = require("varuint-bitcoin");
|
|
31
8
|
function reverseBuffer(buffer) {
|
|
32
9
|
if (buffer.length < 1)
|
|
33
10
|
return buffer;
|
|
@@ -48,9 +25,9 @@ function witnessStackToScriptWitness(witness) {
|
|
|
48
25
|
}
|
|
49
26
|
function writeVarInt(i) {
|
|
50
27
|
const currentLen = buffer.length;
|
|
51
|
-
const varintLen =
|
|
28
|
+
const varintLen = (0, varuint_bitcoin_1.encodingLength)(i);
|
|
52
29
|
buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
|
|
53
|
-
|
|
30
|
+
(0, varuint_bitcoin_1.encode)(i, buffer, currentLen);
|
|
54
31
|
}
|
|
55
32
|
function writeVarSlice(slice) {
|
|
56
33
|
writeVarInt(slice.length);
|
|
@@ -109,8 +86,10 @@ exports.finalScriptsFuncFactory = finalScriptsFuncFactory;
|
|
|
109
86
|
/**
|
|
110
87
|
* Important: Read comments on descriptor.updatePsbt regarding not passing txHex
|
|
111
88
|
*/
|
|
112
|
-
function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysInfo, scriptPubKey, isSegwit, witnessScript, redeemScript }) {
|
|
89
|
+
function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysInfo, scriptPubKey, isSegwit, witnessScript, redeemScript, rbf }) {
|
|
113
90
|
//Some data-sanity checks:
|
|
91
|
+
if (sequence !== undefined && rbf && sequence > 0xfffffffd)
|
|
92
|
+
throw new Error(`Error: incompatible sequence and rbf settings`);
|
|
114
93
|
if (!isSegwit && txHex === undefined)
|
|
115
94
|
throw new Error(`Error: txHex is mandatory for Non-Segwit inputs`);
|
|
116
95
|
if (isSegwit &&
|
|
@@ -152,13 +131,23 @@ function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysIn
|
|
|
152
131
|
// this input's sequence < 0xffffffff
|
|
153
132
|
if (sequence === undefined) {
|
|
154
133
|
//NOTE: if sequence is undefined, bitcoinjs-lib uses 0xffffffff as default
|
|
155
|
-
sequence = 0xfffffffe;
|
|
134
|
+
sequence = rbf ? 0xfffffffd : 0xfffffffe;
|
|
156
135
|
}
|
|
157
136
|
else if (sequence > 0xfffffffe) {
|
|
158
137
|
throw new Error(`Error: incompatible sequence: ${sequence} and locktime: ${locktime}`);
|
|
159
138
|
}
|
|
139
|
+
if (sequence === undefined && rbf)
|
|
140
|
+
sequence = 0xfffffffd;
|
|
160
141
|
psbt.setLocktime(locktime);
|
|
161
142
|
}
|
|
143
|
+
else {
|
|
144
|
+
if (sequence === undefined) {
|
|
145
|
+
if (rbf)
|
|
146
|
+
sequence = 0xfffffffd;
|
|
147
|
+
else
|
|
148
|
+
sequence = 0xffffffff;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
162
151
|
const input = {
|
|
163
152
|
hash: reverseBuffer(Buffer.from(txId, 'hex')),
|
|
164
153
|
index: vout
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@bitcoinerlab/descriptors",
|
|
3
3
|
"description": "This library parses and creates Bitcoin Miniscript Descriptors and generates Partially Signed Bitcoin Transactions (PSBTs). It provides PSBT finalizers and signers for single-signature, BIP32 and Hardware Wallets.",
|
|
4
4
|
"homepage": "https://github.com/bitcoinerlab/descriptors",
|
|
5
|
-
"version": "2.0
|
|
5
|
+
"version": "2.2.0",
|
|
6
6
|
"author": "Jose-Luis Landabaso",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"repository": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"docs": "typedoc --options ./node_modules/@bitcoinerlab/configs/typedoc.json",
|
|
33
33
|
"build:src": "tsc --project ./node_modules/@bitcoinerlab/configs/tsconfig.src.json",
|
|
34
34
|
"build:fixtures": "node test/tools/generateBitcoinCoreFixtures.js -i test/fixtures/descriptor_tests.cpp | npx prettier --parser typescript > test/fixtures/bitcoinCore.ts",
|
|
35
|
-
"build:test": "npm run build:fixtures && tsc --project ./node_modules/@bitcoinerlab/configs/tsconfig.test.json",
|
|
35
|
+
"build:test": "npm run build:fixtures && tsc --project ./node_modules/@bitcoinerlab/configs/tsconfig.test.json --resolveJsonModule",
|
|
36
36
|
"build": "npm run build:src && npm run build:test",
|
|
37
37
|
"lint": "eslint --ignore-path .gitignore --ext .ts src/ test/",
|
|
38
38
|
"ensureTester": "./node_modules/@bitcoinerlab/configs/scripts/ensureTester.sh",
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@bitcoinerlab/configs": "github:bitcoinerlab/configs",
|
|
60
60
|
"@ledgerhq/hw-transport-node-hid": "^6.27.12",
|
|
61
|
+
"@types/lodash.memoize": "^4.1.9",
|
|
61
62
|
"bip39": "^3.0.4",
|
|
62
63
|
"bip65": "^1.0.3",
|
|
63
64
|
"bip68": "^1.0.4",
|
|
@@ -66,10 +67,12 @@
|
|
|
66
67
|
"yargs": "^17.7.2"
|
|
67
68
|
},
|
|
68
69
|
"dependencies": {
|
|
69
|
-
"@bitcoinerlab/miniscript": "^1.
|
|
70
|
-
"@bitcoinerlab/secp256k1": "^1.
|
|
70
|
+
"@bitcoinerlab/miniscript": "^1.4.0",
|
|
71
|
+
"@bitcoinerlab/secp256k1": "^1.1.1",
|
|
71
72
|
"bip32": "^4.0.0",
|
|
72
73
|
"bitcoinjs-lib": "^6.1.3",
|
|
73
|
-
"ecpair": "^2.1.0"
|
|
74
|
+
"ecpair": "^2.1.0",
|
|
75
|
+
"lodash.memoize": "^4.1.2",
|
|
76
|
+
"varuint-bitcoin": "^1.1.2"
|
|
74
77
|
}
|
|
75
78
|
}
|