@caravan/psbt 1.0.1 → 1.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/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-lint.log +26 -14
- package/.turbo/turbo-test.log +44 -44
- package/CHANGELOG.md +12 -0
- package/README.md +86 -16
- package/dist/index.d.mts +94 -12
- package/dist/index.d.ts +94 -12
- package/dist/index.js +214 -13
- package/dist/index.mjs +214 -13
- package/package.json +1 -1
- package/src/psbtv2/psbtv2.test.ts +347 -1
- package/src/psbtv2/psbtv2.ts +335 -22
- package/src/psbtv2/types.ts +6 -0
package/src/psbtv2/psbtv2.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
KeyType,
|
|
9
9
|
PsbtGlobalTxModifiableBits,
|
|
10
10
|
SighashType,
|
|
11
|
+
MapSelectorType,
|
|
11
12
|
} from "./types";
|
|
12
13
|
import {
|
|
13
14
|
bufferize,
|
|
@@ -44,7 +45,10 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
44
45
|
*/
|
|
45
46
|
|
|
46
47
|
get PSBT_GLOBAL_XPUB() {
|
|
47
|
-
return getNonUniqueKeyTypeValues(
|
|
48
|
+
return getNonUniqueKeyTypeValues(
|
|
49
|
+
this.globalMap,
|
|
50
|
+
KeyType.PSBT_GLOBAL_XPUB,
|
|
51
|
+
) as NonUniqueKeyTypeValue[];
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
get PSBT_GLOBAL_TX_VERSION() {
|
|
@@ -189,7 +193,7 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
189
193
|
return getNonUniqueKeyTypeValues(
|
|
190
194
|
this.globalMap,
|
|
191
195
|
KeyType.PSBT_GLOBAL_PROPRIETARY,
|
|
192
|
-
);
|
|
196
|
+
) as NonUniqueKeyTypeValue[];
|
|
193
197
|
}
|
|
194
198
|
|
|
195
199
|
/**
|
|
@@ -210,7 +214,7 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
210
214
|
);
|
|
211
215
|
}
|
|
212
216
|
|
|
213
|
-
get PSBT_IN_PARTIAL_SIG()
|
|
217
|
+
get PSBT_IN_PARTIAL_SIG() {
|
|
214
218
|
return getNonUniqueKeyTypeValues(
|
|
215
219
|
this.inputMaps,
|
|
216
220
|
KeyType.PSBT_IN_PARTIAL_SIG,
|
|
@@ -242,7 +246,7 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
242
246
|
return getNonUniqueKeyTypeValues(
|
|
243
247
|
this.inputMaps,
|
|
244
248
|
KeyType.PSBT_IN_BIP32_DERIVATION,
|
|
245
|
-
);
|
|
249
|
+
) as NonUniqueKeyTypeValue[][];
|
|
246
250
|
}
|
|
247
251
|
|
|
248
252
|
get PSBT_IN_FINAL_SCRIPTSIG() {
|
|
@@ -267,19 +271,31 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
267
271
|
}
|
|
268
272
|
|
|
269
273
|
get PSBT_IN_RIPEMD160() {
|
|
270
|
-
return getNonUniqueKeyTypeValues(
|
|
274
|
+
return getNonUniqueKeyTypeValues(
|
|
275
|
+
this.inputMaps,
|
|
276
|
+
KeyType.PSBT_IN_RIPEMD160,
|
|
277
|
+
) as NonUniqueKeyTypeValue[][];
|
|
271
278
|
}
|
|
272
279
|
|
|
273
280
|
get PSBT_IN_SHA256() {
|
|
274
|
-
return getNonUniqueKeyTypeValues(
|
|
281
|
+
return getNonUniqueKeyTypeValues(
|
|
282
|
+
this.inputMaps,
|
|
283
|
+
KeyType.PSBT_IN_SHA256,
|
|
284
|
+
) as NonUniqueKeyTypeValue[][];
|
|
275
285
|
}
|
|
276
286
|
|
|
277
287
|
get PSBT_IN_HASH160() {
|
|
278
|
-
return getNonUniqueKeyTypeValues(
|
|
288
|
+
return getNonUniqueKeyTypeValues(
|
|
289
|
+
this.inputMaps,
|
|
290
|
+
KeyType.PSBT_IN_HASH160,
|
|
291
|
+
) as NonUniqueKeyTypeValue[][];
|
|
279
292
|
}
|
|
280
293
|
|
|
281
294
|
get PSBT_IN_HASH256() {
|
|
282
|
-
return getNonUniqueKeyTypeValues(
|
|
295
|
+
return getNonUniqueKeyTypeValues(
|
|
296
|
+
this.inputMaps,
|
|
297
|
+
KeyType.PSBT_IN_HASH256,
|
|
298
|
+
) as NonUniqueKeyTypeValue[][];
|
|
283
299
|
}
|
|
284
300
|
|
|
285
301
|
get PSBT_IN_PREVIOUS_TXID() {
|
|
@@ -338,21 +354,21 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
338
354
|
return getNonUniqueKeyTypeValues(
|
|
339
355
|
this.inputMaps,
|
|
340
356
|
KeyType.PSBT_IN_TAP_SCRIPT_SIG,
|
|
341
|
-
);
|
|
357
|
+
) as NonUniqueKeyTypeValue[][];
|
|
342
358
|
}
|
|
343
359
|
|
|
344
360
|
get PSBT_IN_TAP_LEAF_SCRIPT() {
|
|
345
361
|
return getNonUniqueKeyTypeValues(
|
|
346
362
|
this.inputMaps,
|
|
347
363
|
KeyType.PSBT_IN_TAP_LEAF_SCRIPT,
|
|
348
|
-
);
|
|
364
|
+
) as NonUniqueKeyTypeValue[][];
|
|
349
365
|
}
|
|
350
366
|
|
|
351
367
|
get PSBT_IN_TAP_BIP32_DERIVATION() {
|
|
352
368
|
return getNonUniqueKeyTypeValues(
|
|
353
369
|
this.inputMaps,
|
|
354
370
|
KeyType.PSBT_IN_TAP_BIP32_DERIVATION,
|
|
355
|
-
);
|
|
371
|
+
) as NonUniqueKeyTypeValue[][];
|
|
356
372
|
}
|
|
357
373
|
|
|
358
374
|
get PSBT_IN_TAP_INTERNAL_KEY() {
|
|
@@ -455,6 +471,191 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
455
471
|
);
|
|
456
472
|
}
|
|
457
473
|
|
|
474
|
+
/**
|
|
475
|
+
* Operator Role Validation Getters
|
|
476
|
+
*/
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Returns true if the PsbtV2 is ready for an operator taking the Constructor
|
|
480
|
+
* role.
|
|
481
|
+
*
|
|
482
|
+
* This check assumes that the Creator used this class's constructor method to
|
|
483
|
+
* initialize the PsbtV2 without passing a psbt (constructor defaults were
|
|
484
|
+
* set).
|
|
485
|
+
*/
|
|
486
|
+
get isReadyForConstructor() {
|
|
487
|
+
// The Creator role (likely via the class constructor) must ensure at least
|
|
488
|
+
// the following value has been initialized. The psbt cannot be passed to
|
|
489
|
+
// the Constructor until it is set.
|
|
490
|
+
if (this.PSBT_GLOBAL_FALLBACK_LOCKTIME === null) {
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// At least inputs or outputs must still be modifiable.
|
|
495
|
+
if (
|
|
496
|
+
!this.isModifiable([PsbtGlobalTxModifiableBits.INPUTS]) &&
|
|
497
|
+
!this.isModifiable([PsbtGlobalTxModifiableBits.OUTPUTS])
|
|
498
|
+
) {
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return true;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Returns true if the PsbtV2 is ready for an operator taking the Updater
|
|
507
|
+
* role.
|
|
508
|
+
*
|
|
509
|
+
* Before signatures are added, but after an input is added, a PsbtV2 is
|
|
510
|
+
* likely to be ready for Constructor, ready for Updater, and ready for Signer
|
|
511
|
+
* simultaneously.
|
|
512
|
+
*
|
|
513
|
+
* According to BIP370, the Updater can modify the sequence number, but it is
|
|
514
|
+
* unclear if the Updater retains permissions provided in psbtv0 (BIP174). It
|
|
515
|
+
* is likely not the case that the Updater has the same permissions as
|
|
516
|
+
* previously because it seems to now be the realm of the Constructor to add
|
|
517
|
+
* inputs and outputs.
|
|
518
|
+
*/
|
|
519
|
+
get isReadyForUpdater() {
|
|
520
|
+
// In psbtv2, the Updater can set the sequence number, but an input must
|
|
521
|
+
// exist for this to be set.
|
|
522
|
+
if (this.PSBT_GLOBAL_INPUT_COUNT === 0) {
|
|
523
|
+
return false;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Inputs must still be modifiable
|
|
527
|
+
if (!this.isModifiable([PsbtGlobalTxModifiableBits.INPUTS])) {
|
|
528
|
+
return false;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
return true;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Returns true if the PsbtV2 is ready for an operator taking the Signer role.
|
|
536
|
+
*/
|
|
537
|
+
get isReadyForSigner() {
|
|
538
|
+
// An input must exist before it can be signed.
|
|
539
|
+
if (this.PSBT_GLOBAL_INPUT_COUNT === 0) {
|
|
540
|
+
return false;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// TODO: Maybe it makes sense to more granularly check if the psbt is fully
|
|
544
|
+
// signed or has minimum signatures. Until then, just check that sigs have
|
|
545
|
+
// not been finalized.
|
|
546
|
+
if (this.isReadyForTransactionExtractor) {
|
|
547
|
+
return false;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
return true;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Returns true if the PsbtV2 is ready for an operator taking the Combiner
|
|
555
|
+
* role.
|
|
556
|
+
*/
|
|
557
|
+
get isReadyForCombiner() {
|
|
558
|
+
// The combiner can potentially provide everything that's missing when
|
|
559
|
+
// merging another psbt. If it's at least ready for updates from the
|
|
560
|
+
// following roles, then it's ready for a Combiner.
|
|
561
|
+
return (
|
|
562
|
+
this.isReadyForConstructor ||
|
|
563
|
+
this.isReadyForUpdater ||
|
|
564
|
+
this.isReadyForSigner
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Unimplemented. Returns false.
|
|
570
|
+
*/
|
|
571
|
+
get isReadyForInputFinalizer() {
|
|
572
|
+
// Checks to see if the psbt contains everything needed to finalize inputs.
|
|
573
|
+
// This can become quite complicated considering multisig and taproot.
|
|
574
|
+
console.warn(
|
|
575
|
+
"PsbtV2.isReadyForInputFinalizer has been called, however, this getter is unimplemented and shouldn't be used.",
|
|
576
|
+
);
|
|
577
|
+
return false;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Returns true if the PsbtV2 is ready for an operator taking the Transaction
|
|
582
|
+
* Extractor role.
|
|
583
|
+
*
|
|
584
|
+
* If all the inputs have been finalized, then the psbt is ready for the
|
|
585
|
+
* Transaction Extractor. According to BIP 174, it's the responsibility of the
|
|
586
|
+
* Input Finalizer to add scriptSigs or scriptWitnesses and then remove other
|
|
587
|
+
* details besides the UTXO. This getter checks that the Input Finalizer has
|
|
588
|
+
* finished its job.
|
|
589
|
+
*/
|
|
590
|
+
get isReadyForTransactionExtractor() {
|
|
591
|
+
// Iterate over all inputs
|
|
592
|
+
|
|
593
|
+
for (let i = 0; i < this.PSBT_GLOBAL_INPUT_COUNT; i++) {
|
|
594
|
+
// Check for finalized script
|
|
595
|
+
if (
|
|
596
|
+
!this.PSBT_IN_FINAL_SCRIPTSIG[i] &&
|
|
597
|
+
!this.PSBT_IN_FINAL_SCRIPTWITNESS[i]
|
|
598
|
+
) {
|
|
599
|
+
return false;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Check that the corresponding UTXO is still available
|
|
603
|
+
if (
|
|
604
|
+
(this.PSBT_IN_FINAL_SCRIPTSIG[i] &&
|
|
605
|
+
!this.PSBT_IN_NON_WITNESS_UTXO[i]) ||
|
|
606
|
+
(this.PSBT_IN_FINAL_SCRIPTWITNESS[i] && !this.PSBT_IN_WITNESS_UTXO[i])
|
|
607
|
+
) {
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Check that Input Finalizer removed other values from the input.
|
|
612
|
+
//
|
|
613
|
+
// Test vectors from BIP 370 indicate that a missing PSBT_IN_OUTPUT_INDEX
|
|
614
|
+
// or PSBT_IN_PREVIOUS_TXID should be an invalid psbt, so the getters for
|
|
615
|
+
// these keys will throw unless the values are set. However, the BIP also
|
|
616
|
+
// requires that the Input Finalizer removes all other values from the
|
|
617
|
+
// input map except for the finalized scripts and UTXOs. Since removal of
|
|
618
|
+
// the above mentioned keys will result in an invalid psbt, it's decided
|
|
619
|
+
// here that it's safe to ignore the fact that those keys have not been
|
|
620
|
+
// removed.
|
|
621
|
+
if (
|
|
622
|
+
// Strings
|
|
623
|
+
this.PSBT_IN_REDEEM_SCRIPT[i] ||
|
|
624
|
+
this.PSBT_IN_WITNESS_SCRIPT[i] ||
|
|
625
|
+
this.PSBT_IN_POR_COMMITMENT[i] ||
|
|
626
|
+
this.PSBT_IN_TAP_KEY_SIG[i] ||
|
|
627
|
+
this.PSBT_IN_TAP_INTERNAL_KEY[i] ||
|
|
628
|
+
this.PSBT_IN_TAP_MERKLE_ROOT[i] ||
|
|
629
|
+
// Numbers
|
|
630
|
+
this.PSBT_IN_SIGHASH_TYPE[i] !== null ||
|
|
631
|
+
this.PSBT_IN_SEQUENCE[i] !== null ||
|
|
632
|
+
this.PSBT_IN_REQUIRED_TIME_LOCKTIME[i] !== null ||
|
|
633
|
+
this.PSBT_IN_REQUIRED_HEIGHT_LOCKTIME[i] !== null ||
|
|
634
|
+
// Arrays of non-unique keytype values
|
|
635
|
+
this.PSBT_IN_PARTIAL_SIG[i].filter((el) => el !== null).length > 0 ||
|
|
636
|
+
this.PSBT_IN_BIP32_DERIVATION[i].filter((el) => el !== null).length >
|
|
637
|
+
0 ||
|
|
638
|
+
this.PSBT_IN_RIPEMD160[i].filter((el) => el !== null).length > 0 ||
|
|
639
|
+
this.PSBT_IN_SHA256[i].filter((el) => el !== null).length > 0 ||
|
|
640
|
+
this.PSBT_IN_HASH160[i].filter((el) => el !== null).length > 0 ||
|
|
641
|
+
this.PSBT_IN_HASH256[i].filter((el) => el !== null).length > 0 ||
|
|
642
|
+
this.PSBT_IN_TAP_SCRIPT_SIG[i].filter((el) => el !== null).length > 0 ||
|
|
643
|
+
this.PSBT_IN_TAP_LEAF_SCRIPT[i].filter((el) => el !== null).length >
|
|
644
|
+
0 ||
|
|
645
|
+
this.PSBT_IN_TAP_BIP32_DERIVATION[i].filter((el) => el !== null)
|
|
646
|
+
.length > 0
|
|
647
|
+
) {
|
|
648
|
+
return false;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// This input has been finalized. Continue checking the next one.
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// All inputs have been finalized, so this psbt is ready for transaction
|
|
655
|
+
// extraction.
|
|
656
|
+
return true;
|
|
657
|
+
}
|
|
658
|
+
|
|
458
659
|
/**
|
|
459
660
|
* Other Getters/Setters
|
|
460
661
|
*/
|
|
@@ -530,7 +731,15 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
530
731
|
this.PSBT_GLOBAL_TX_VERSION = 2;
|
|
531
732
|
this.PSBT_GLOBAL_INPUT_COUNT = 0;
|
|
532
733
|
this.PSBT_GLOBAL_OUTPUT_COUNT = 0;
|
|
734
|
+
|
|
735
|
+
// TODO: Right now these values are setting a default. How can it be made to
|
|
736
|
+
// accept values on the constructor method? The Creator role should be
|
|
737
|
+
// allowed to configure these.
|
|
533
738
|
this.PSBT_GLOBAL_FALLBACK_LOCKTIME = 0;
|
|
739
|
+
this.PSBT_GLOBAL_TX_MODIFIABLE = [
|
|
740
|
+
PsbtGlobalTxModifiableBits.INPUTS,
|
|
741
|
+
PsbtGlobalTxModifiableBits.OUTPUTS,
|
|
742
|
+
];
|
|
534
743
|
}
|
|
535
744
|
|
|
536
745
|
/**
|
|
@@ -591,6 +800,11 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
591
800
|
* defined for PsbtV2.
|
|
592
801
|
*/
|
|
593
802
|
public dangerouslySetGlobalTxVersion1() {
|
|
803
|
+
if (!this.isReadyForConstructor) {
|
|
804
|
+
throw Error(
|
|
805
|
+
"The PsbtV2 is not ready for a Constructor. The PSBT_GLOBAL_TX_VERSION should not be forced to version 1.",
|
|
806
|
+
);
|
|
807
|
+
}
|
|
594
808
|
console.warn("Dangerously setting PsbtV2.PSBT_GLOBAL_TX_VERSION to 1!");
|
|
595
809
|
const bw = new BufferWriter();
|
|
596
810
|
bw.writeI32(1);
|
|
@@ -638,6 +852,17 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
638
852
|
// significant validation concerning this step detailed in the BIP0370
|
|
639
853
|
// Constructor role:
|
|
640
854
|
// https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki#constructor
|
|
855
|
+
//
|
|
856
|
+
// TODO: This method must properly handle the SIGHASH_SINGLE flag. If the
|
|
857
|
+
// `PSBT_GLOBAL_TX_MODIFIABLE` flag `SIGHASH_SINGLE` is present and a
|
|
858
|
+
// signature is present, then adding or removing inputs or outputs before a
|
|
859
|
+
// signature with sighash_single must happen atomically in pairs.
|
|
860
|
+
|
|
861
|
+
if (!this.isReadyForConstructor) {
|
|
862
|
+
throw Error(
|
|
863
|
+
"The PsbtV2 is not ready for a Constructor. Inputs cannot be added.",
|
|
864
|
+
);
|
|
865
|
+
}
|
|
641
866
|
if (!this.isModifiable([PsbtGlobalTxModifiableBits.INPUTS])) {
|
|
642
867
|
throw Error(
|
|
643
868
|
"PsbtV2.PSBT_GLOBAL_TX_MODIFIABLE inputs cannot be modified.",
|
|
@@ -704,6 +929,11 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
704
929
|
path: string;
|
|
705
930
|
}[];
|
|
706
931
|
}) {
|
|
932
|
+
if (!this.isReadyForConstructor) {
|
|
933
|
+
throw Error(
|
|
934
|
+
"The PsbtV2 is not ready for a Constructor. Outputs cannot be added.",
|
|
935
|
+
);
|
|
936
|
+
}
|
|
707
937
|
if (!this.isModifiable([PsbtGlobalTxModifiableBits.OUTPUTS])) {
|
|
708
938
|
throw Error(
|
|
709
939
|
"PsbtV2.PSBT_GLOBAL_TX_MODIFIABLE outputs cannot be modified.",
|
|
@@ -747,6 +977,11 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
747
977
|
* Removes an input-map from inputMaps.
|
|
748
978
|
*/
|
|
749
979
|
public deleteInput(index: number) {
|
|
980
|
+
if (!this.isReadyForConstructor) {
|
|
981
|
+
throw Error(
|
|
982
|
+
"The PsbtV2 is not ready for a Constructor. Inputs cannot be removed.",
|
|
983
|
+
);
|
|
984
|
+
}
|
|
750
985
|
if (!this.isModifiable([PsbtGlobalTxModifiableBits.INPUTS])) {
|
|
751
986
|
throw Error(
|
|
752
987
|
"PsbtV2.PSBT_GLOBAL_TX_MODIFIABLE inputs cannot be modified.",
|
|
@@ -761,6 +996,11 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
761
996
|
* Removes an output-map from outputMaps.
|
|
762
997
|
*/
|
|
763
998
|
public deleteOutput(index: number) {
|
|
999
|
+
if (!this.isReadyForConstructor) {
|
|
1000
|
+
throw Error(
|
|
1001
|
+
"The PsbtV2 is not ready for a Constructor. Outputs cannot be removed.",
|
|
1002
|
+
);
|
|
1003
|
+
}
|
|
764
1004
|
if (!this.isModifiable([PsbtGlobalTxModifiableBits.OUTPUTS])) {
|
|
765
1005
|
throw Error(
|
|
766
1006
|
"PsbtV2.PSBT_GLOBAL_TX_MODIFIABLE outputs cannot be modified.",
|
|
@@ -782,7 +1022,7 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
782
1022
|
}
|
|
783
1023
|
|
|
784
1024
|
/**
|
|
785
|
-
* Checks that provided flags are present in PSBT_GLOBAL_TX_MODIFIABLE.
|
|
1025
|
+
* Checks that all provided flags are present in PSBT_GLOBAL_TX_MODIFIABLE.
|
|
786
1026
|
*/
|
|
787
1027
|
private isModifiable(flags: PsbtGlobalTxModifiableBits[]) {
|
|
788
1028
|
for (const flag of flags) {
|
|
@@ -808,6 +1048,11 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
808
1048
|
* https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#signer
|
|
809
1049
|
*/
|
|
810
1050
|
public addPartialSig(inputIndex: number, pubkey: Buffer, sig: Buffer) {
|
|
1051
|
+
if (!this.isReadyForSigner) {
|
|
1052
|
+
throw Error(
|
|
1053
|
+
"The PsbtV2 is not ready for a Signer. Partial sigs cannot be added.",
|
|
1054
|
+
);
|
|
1055
|
+
}
|
|
811
1056
|
if (!this.inputMaps[inputIndex]) {
|
|
812
1057
|
throw Error(`PsbtV2 has no input at ${inputIndex}`);
|
|
813
1058
|
}
|
|
@@ -845,6 +1090,8 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
845
1090
|
* the pubkey exists.
|
|
846
1091
|
*/
|
|
847
1092
|
public removePartialSig(inputIndex: number, pubkey?: Buffer) {
|
|
1093
|
+
// TODO: What role is allowed to remove a partial sig? Perform that
|
|
1094
|
+
// role-check validation here.
|
|
848
1095
|
const input = this.inputMaps[inputIndex];
|
|
849
1096
|
|
|
850
1097
|
if (!input) {
|
|
@@ -874,6 +1121,72 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
874
1121
|
}
|
|
875
1122
|
}
|
|
876
1123
|
|
|
1124
|
+
/**
|
|
1125
|
+
* Sets values on the proprietary keytype for a global, input, or output map.
|
|
1126
|
+
* BIP 174 allows for proprietary values to be set on all maps with the
|
|
1127
|
+
* keytype `0xFC`. This method sets byte data to key values defined by the
|
|
1128
|
+
* args.
|
|
1129
|
+
*
|
|
1130
|
+
* Args:
|
|
1131
|
+
* - `mapSelector` selects which map to set the proprietary value. If this
|
|
1132
|
+
* value is not `"global"`, then a tuple must be provided with `"inputs"` or
|
|
1133
|
+
* `"outputs"` as the first element and the index `number` on the second
|
|
1134
|
+
* element representing which input or output map to set the value to. An
|
|
1135
|
+
* example looks like `["inputs", 0]`. If the map name doesn't match, the
|
|
1136
|
+
* values will be set to the global map. If the index is missing on
|
|
1137
|
+
* `"inputs"` or `"outputs"`, then it will throw.
|
|
1138
|
+
* - `identifier` should be the bytes identifier for the set of proprietary
|
|
1139
|
+
* keytypes.
|
|
1140
|
+
* - `subkeyType` accepts bytes proprietary keytype.
|
|
1141
|
+
* - `subkeyData` accepts bytes proprietary keydata.
|
|
1142
|
+
* - `valueData` accepts bytes which will be written as the proprietary value.
|
|
1143
|
+
*
|
|
1144
|
+
* From the provided args, a key with the following format will be generated:
|
|
1145
|
+
* `0xFC<compact uint identifier length><bytes identifier><bytes
|
|
1146
|
+
* subtype><bytes subkeydata>`
|
|
1147
|
+
*/
|
|
1148
|
+
public setProprietaryValue(
|
|
1149
|
+
mapSelector: MapSelectorType,
|
|
1150
|
+
identifier: Buffer,
|
|
1151
|
+
subkeyType: Buffer,
|
|
1152
|
+
subkeyData: Buffer,
|
|
1153
|
+
valueData: Buffer,
|
|
1154
|
+
) {
|
|
1155
|
+
if (
|
|
1156
|
+
(mapSelector[0] === "inputs" || mapSelector[0] === "outputs") &&
|
|
1157
|
+
typeof mapSelector[1] !== "number"
|
|
1158
|
+
) {
|
|
1159
|
+
throw Error(
|
|
1160
|
+
"Must specify an index when setting proprietary values to inputs or outputs.",
|
|
1161
|
+
);
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
let classMap: Map<string, Buffer> = this.globalMap,
|
|
1165
|
+
keyType = KeyType.PSBT_GLOBAL_PROPRIETARY;
|
|
1166
|
+
if (mapSelector[0] === "inputs") {
|
|
1167
|
+
classMap = this.inputMaps[mapSelector[1]];
|
|
1168
|
+
keyType = KeyType.PSBT_IN_PROPRIETARY;
|
|
1169
|
+
} else if (mapSelector[0] === "outputs") {
|
|
1170
|
+
classMap = this.outputMaps[mapSelector[1]];
|
|
1171
|
+
keyType = KeyType.PSBT_OUT_PROPRIETARY;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
if (!classMap) {
|
|
1175
|
+
throw Error("Map does not exist at that index.");
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
const bw = new BufferWriter();
|
|
1179
|
+
bw.writeBytes(Buffer.from(keyType, "hex"));
|
|
1180
|
+
bw.writeU8(identifier.length);
|
|
1181
|
+
bw.writeBytes(identifier);
|
|
1182
|
+
bw.writeBytes(subkeyType);
|
|
1183
|
+
bw.writeBytes(subkeyData);
|
|
1184
|
+
const key = bw.render().toString("hex");
|
|
1185
|
+
bw.writeBytes(valueData);
|
|
1186
|
+
const value = bw.render();
|
|
1187
|
+
classMap.set(key, value);
|
|
1188
|
+
}
|
|
1189
|
+
|
|
877
1190
|
/**
|
|
878
1191
|
* Ensures the PSBT is in the proper state when adding a partial sig keypair.
|
|
879
1192
|
* https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki#signer
|
|
@@ -909,7 +1222,11 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
909
1222
|
}
|
|
910
1223
|
|
|
911
1224
|
/**
|
|
912
|
-
* Attempts to return a PsbtV2 by converting from a PsbtV0 string or Buffer
|
|
1225
|
+
* Attempts to return a PsbtV2 by converting from a PsbtV0 string or Buffer.
|
|
1226
|
+
*
|
|
1227
|
+
* This method first starts with a fresh PsbtV2 having just been created. It
|
|
1228
|
+
* then takes the PsbtV2 through its operator saga through the Signer role. In
|
|
1229
|
+
* this sense validation for each operator role will be performed.
|
|
913
1230
|
*/
|
|
914
1231
|
static FromV0(psbt: string | Buffer, allowTxnVersion1 = false): PsbtV2 {
|
|
915
1232
|
const psbtv0Buf = bufferize(psbt);
|
|
@@ -918,11 +1235,6 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
918
1235
|
|
|
919
1236
|
// Creator Role
|
|
920
1237
|
const psbtv2 = new PsbtV2();
|
|
921
|
-
// Set it fully modifiable so that we can add the v0 inputs and outputs.
|
|
922
|
-
psbtv2.PSBT_GLOBAL_TX_MODIFIABLE = [
|
|
923
|
-
PsbtGlobalTxModifiableBits.INPUTS,
|
|
924
|
-
PsbtGlobalTxModifiableBits.OUTPUTS,
|
|
925
|
-
];
|
|
926
1238
|
const txVersion = psbtv0.data.getTransaction().readInt32LE(0);
|
|
927
1239
|
if (txVersion === 1 && allowTxnVersion1) {
|
|
928
1240
|
psbtv2.dangerouslySetGlobalTxVersion1();
|
|
@@ -932,7 +1244,7 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
932
1244
|
.readInt32LE(0);
|
|
933
1245
|
}
|
|
934
1246
|
|
|
935
|
-
//
|
|
1247
|
+
// Constructor Role
|
|
936
1248
|
for (const globalXpub of psbtv0GlobalMap.globalXpub ?? []) {
|
|
937
1249
|
psbtv2.addGlobalXpub(
|
|
938
1250
|
globalXpub.extendedPubkey,
|
|
@@ -941,8 +1253,7 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
941
1253
|
);
|
|
942
1254
|
}
|
|
943
1255
|
|
|
944
|
-
|
|
945
|
-
let txInputs: any = [];
|
|
1256
|
+
const txInputs: any = [];
|
|
946
1257
|
for (const [index, txInput] of psbtv0.txInputs.entries()) {
|
|
947
1258
|
txInputs[index] = txInput;
|
|
948
1259
|
}
|
|
@@ -964,7 +1275,7 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
964
1275
|
});
|
|
965
1276
|
}
|
|
966
1277
|
|
|
967
|
-
|
|
1278
|
+
const txOutputs: any = [];
|
|
968
1279
|
for (const [index, txOutput] of psbtv0.txOutputs.entries()) {
|
|
969
1280
|
txOutputs[index] = txOutput;
|
|
970
1281
|
}
|
|
@@ -980,6 +1291,8 @@ export class PsbtV2 extends PsbtV2Maps {
|
|
|
980
1291
|
});
|
|
981
1292
|
}
|
|
982
1293
|
|
|
1294
|
+
// Signer Role
|
|
1295
|
+
|
|
983
1296
|
// Finally, add partialSigs to inputs. This has to be performed last since
|
|
984
1297
|
// it may change PSBT_GLOBAL_TX_MODIFIABLE preventing inputs or outputs from
|
|
985
1298
|
// being added.
|
package/src/psbtv2/types.ts
CHANGED