1
1
'use strict' ;
2
2
Object . defineProperty ( exports , '__esModule' , { value : true } ) ;
3
3
exports . Psbt = void 0 ;
4
+ const ecc = require ( 'tiny-secp256k1' ) ; // TODO: extract
5
+ const ecpair_1 = require ( 'ecpair' ) ;
4
6
const bip174_1 = require ( 'bip174' ) ;
5
7
const varuint = require ( 'bip174/src/lib/converter/varint' ) ;
6
8
const utils_1 = require ( 'bip174/src/lib/utils' ) ;
@@ -11,6 +13,7 @@ const networks_1 = require('./networks');
11
13
const payments = require ( './payments' ) ;
12
14
const bscript = require ( './script' ) ;
13
15
const transaction_1 = require ( './transaction' ) ;
16
+ const taprootutils_1 = require ( './payments/taprootutils' ) ;
14
17
/**
15
18
* These are the default arguments for a Psbt instance.
16
19
*/
@@ -103,6 +106,39 @@ class Psbt {
103
106
checkTxForDupeIns ( psbt . __CACHE . __TX , psbt . __CACHE ) ;
104
107
return psbt ;
105
108
}
109
+ /**
110
+ * Helper method for converting a normal Signer into a Taproot Signer.
111
+ * Note that this helper method requires the Private Key of the Signer to be present.
112
+ * Steps:
113
+ * - if the Y coordinate of the Signer Public Key is odd then negate the Private Key
114
+ * - tweak the private key with the provided hash (should be empty for key-path spending)
115
+ * @param signer - a taproot signer object, the Private Key must be present
116
+ * @param opts - tweak options
117
+ * @returns a Signer having the Private and Public keys tweaked
118
+ */
119
+ static tweakSigner ( signer , opts = { } ) {
120
+ let privateKey = signer . privateKey ;
121
+ if ( ! privateKey ) {
122
+ throw new Error ( 'Private key is required for tweaking signer!' ) ;
123
+ }
124
+ if ( signer . publicKey [ 0 ] === 3 ) {
125
+ privateKey = ecc . privateNegate ( privateKey ) ;
126
+ }
127
+ const tweakedPrivateKey = ecc . privateAdd (
128
+ privateKey ,
129
+ ( 0 , taprootutils_1 . tapTweakHash ) (
130
+ signer . publicKey . slice ( 1 , 33 ) ,
131
+ opts . tweakHash ,
132
+ ) ,
133
+ ) ;
134
+ if ( ! tweakedPrivateKey ) {
135
+ throw new Error ( 'Invalid tweaked private key!' ) ;
136
+ }
137
+ const ECPair = ( 0 , ecpair_1 . ECPairFactory ) ( ecc ) ;
138
+ return ECPair . fromPrivateKey ( Buffer . from ( tweakedPrivateKey ) , {
139
+ network : opts . network ,
140
+ } ) ;
141
+ }
106
142
get inputCount ( ) {
107
143
return this . data . inputs . length ;
108
144
}
@@ -298,7 +334,11 @@ class Psbt {
298
334
}
299
335
getInputType ( inputIndex ) {
300
336
const input = ( 0 , utils_1 . checkForInput ) ( this . data . inputs , inputIndex ) ;
301
- const script = getScriptFromUtxo ( inputIndex , input , this . __CACHE ) ;
337
+ const { script } = getScriptAndAmountFromUtxo (
338
+ inputIndex ,
339
+ input ,
340
+ this . __CACHE ,
341
+ ) ;
302
342
const result = getMeaningfulScript (
303
343
script ,
304
344
inputIndex ,
@@ -355,13 +395,21 @@ class Psbt {
355
395
let hashCache ;
356
396
let scriptCache ;
357
397
let sighashCache ;
398
+ const scriptType = this . getInputType ( inputIndex ) ;
358
399
for ( const pSig of mySigs ) {
359
- const sig = bscript . signature . decode ( pSig . signature ) ;
400
+ const sig =
401
+ scriptType === 'taproot'
402
+ ? {
403
+ signature : pSig . signature ,
404
+ hashType : transaction_1 . Transaction . SIGHASH_DEFAULT ,
405
+ }
406
+ : bscript . signature . decode ( pSig . signature ) ;
360
407
const { hash, script } =
361
408
sighashCache !== sig . hashType
362
409
? getHashForSig (
363
410
inputIndex ,
364
411
Object . assign ( { } , input , { sighashType : sig . hashType } ) ,
412
+ this . data . inputs ,
365
413
this . __CACHE ,
366
414
true ,
367
415
)
@@ -526,13 +574,30 @@ class Psbt {
526
574
this . __CACHE ,
527
575
sighashTypes ,
528
576
) ;
529
- const partialSig = [
530
- {
531
- pubkey : keyPair . publicKey ,
532
- signature : bscript . signature . encode ( keyPair . sign ( hash ) , sighashType ) ,
533
- } ,
534
- ] ;
535
- this . data . updateInput ( inputIndex , { partialSig } ) ;
577
+ const scriptType = this . getInputType ( inputIndex ) ;
578
+ if ( scriptType === 'taproot' ) {
579
+ if ( ! keyPair . signSchnorr ) {
580
+ throw new Error (
581
+ `Need Schnorr Signer to sign taproot input #${ inputIndex } .` ,
582
+ ) ;
583
+ }
584
+ const partialSig = [
585
+ {
586
+ pubkey : keyPair . publicKey ,
587
+ signature : keyPair . signSchnorr ( hash ) ,
588
+ } ,
589
+ ] ;
590
+ // must be changed to use the `updateInput()` public API
591
+ this . data . inputs [ inputIndex ] . partialSig = partialSig ;
592
+ } else {
593
+ const partialSig = [
594
+ {
595
+ pubkey : keyPair . publicKey ,
596
+ signature : bscript . signature . encode ( keyPair . sign ( hash ) , sighashType ) ,
597
+ } ,
598
+ ] ;
599
+ this . data . updateInput ( inputIndex , { partialSig } ) ;
600
+ }
536
601
return this ;
537
602
}
538
603
signInputAsync (
@@ -671,6 +736,7 @@ function canFinalize(input, script, scriptType) {
671
736
case 'pubkey' :
672
737
case 'pubkeyhash' :
673
738
case 'witnesspubkeyhash' :
739
+ case 'taproot' :
674
740
return hasSigs ( 1 , input . partialSig ) ;
675
741
case 'multisig' :
676
742
const p2ms = payments . p2ms ( { output : script } ) ;
@@ -704,9 +770,9 @@ function isFinalized(input) {
704
770
return ! ! input . finalScriptSig || ! ! input . finalScriptWitness ;
705
771
}
706
772
function isPaymentFactory ( payment ) {
707
- return script => {
773
+ return ( script , eccLib ) => {
708
774
try {
709
- payment ( { output : script } ) ;
775
+ payment ( { output : script } , { eccLib } ) ;
710
776
return true ;
711
777
} catch ( err ) {
712
778
return false ;
@@ -719,6 +785,7 @@ const isP2PKH = isPaymentFactory(payments.p2pkh);
719
785
const isP2WPKH = isPaymentFactory ( payments . p2wpkh ) ;
720
786
const isP2WSHScript = isPaymentFactory ( payments . p2wsh ) ;
721
787
const isP2SHScript = isPaymentFactory ( payments . p2sh ) ;
788
+ const isP2TR = isPaymentFactory ( payments . p2tr ) ;
722
789
function bip32DerivationIsMine ( root ) {
723
790
return d => {
724
791
if ( ! d . masterFingerprint . equals ( root . fingerprint ) ) return false ;
@@ -920,6 +987,7 @@ function getHashAndSighashType(
920
987
const { hash, sighashType, script } = getHashForSig (
921
988
inputIndex ,
922
989
input ,
990
+ inputs ,
923
991
cache ,
924
992
false ,
925
993
sighashTypes ,
@@ -930,7 +998,14 @@ function getHashAndSighashType(
930
998
sighashType,
931
999
} ;
932
1000
}
933
- function getHashForSig ( inputIndex , input , cache , forValidate , sighashTypes ) {
1001
+ function getHashForSig (
1002
+ inputIndex ,
1003
+ input ,
1004
+ inputs ,
1005
+ cache ,
1006
+ forValidate ,
1007
+ sighashTypes ,
1008
+ ) {
934
1009
const unsignedTx = cache . __TX ;
935
1010
const sighashType =
936
1011
input . sighashType || transaction_1 . Transaction . SIGHASH_ALL ;
@@ -988,6 +1063,18 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
988
1063
prevout . value ,
989
1064
sighashType ,
990
1065
) ;
1066
+ } else if ( isP2TR ( meaningfulScript , ecc ) ) {
1067
+ const prevOuts = inputs . map ( ( i , index ) =>
1068
+ getScriptAndAmountFromUtxo ( index , i , cache ) ,
1069
+ ) ;
1070
+ const signingScripts = prevOuts . map ( o => o . script ) ;
1071
+ const values = prevOuts . map ( o => o . value ) ;
1072
+ hash = unsignedTx . hashForWitnessV1 (
1073
+ inputIndex ,
1074
+ signingScripts ,
1075
+ values ,
1076
+ transaction_1 . Transaction . SIGHASH_DEFAULT ,
1077
+ ) ;
991
1078
} else {
992
1079
// non-segwit
993
1080
if (
@@ -1050,6 +1137,15 @@ function getPayment(script, scriptType, partialSig) {
1050
1137
signature : partialSig [ 0 ] . signature ,
1051
1138
} ) ;
1052
1139
break ;
1140
+ case 'taproot' :
1141
+ payment = payments . p2tr (
1142
+ {
1143
+ output : script ,
1144
+ signature : partialSig [ 0 ] . signature ,
1145
+ } ,
1146
+ { eccLib : ecc } ,
1147
+ ) ;
1148
+ break ;
1053
1149
}
1054
1150
return payment ;
1055
1151
}
@@ -1094,7 +1190,7 @@ function getScriptFromInput(inputIndex, input, cache) {
1094
1190
res . script = input . witnessUtxo . script ;
1095
1191
}
1096
1192
}
1097
- if ( input . witnessScript || isP2WPKH ( res . script ) ) {
1193
+ if ( input . witnessScript || isP2WPKH ( res . script ) || isP2TR ( res . script , ecc ) ) {
1098
1194
res . isSegwit = true ;
1099
1195
}
1100
1196
return res ;
@@ -1267,22 +1363,26 @@ function nonWitnessUtxoTxFromCache(cache, input, inputIndex) {
1267
1363
}
1268
1364
return c [ inputIndex ] ;
1269
1365
}
1270
- function getScriptFromUtxo ( inputIndex , input , cache ) {
1366
+ function getScriptAndAmountFromUtxo ( inputIndex , input , cache ) {
1271
1367
if ( input . witnessUtxo !== undefined ) {
1272
- return input . witnessUtxo . script ;
1368
+ return {
1369
+ script : input . witnessUtxo . script ,
1370
+ value : input . witnessUtxo . value ,
1371
+ } ;
1273
1372
} else if ( input . nonWitnessUtxo !== undefined ) {
1274
1373
const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache (
1275
1374
cache ,
1276
1375
input ,
1277
1376
inputIndex ,
1278
1377
) ;
1279
- return nonWitnessUtxoTx . outs [ cache . __TX . ins [ inputIndex ] . index ] . script ;
1378
+ const o = nonWitnessUtxoTx . outs [ cache . __TX . ins [ inputIndex ] . index ] ;
1379
+ return { script : o . script , value : o . value } ;
1280
1380
} else {
1281
1381
throw new Error ( "Can't find pubkey in input without Utxo data" ) ;
1282
1382
}
1283
1383
}
1284
1384
function pubkeyInInput ( pubkey , input , inputIndex , cache ) {
1285
- const script = getScriptFromUtxo ( inputIndex , input , cache ) ;
1385
+ const { script } = getScriptAndAmountFromUtxo ( inputIndex , input , cache ) ;
1286
1386
const { meaningfulScript } = getMeaningfulScript (
1287
1387
script ,
1288
1388
inputIndex ,
@@ -1392,18 +1492,24 @@ function checkInvalidP2WSH(script) {
1392
1492
}
1393
1493
function pubkeyInScript ( pubkey , script ) {
1394
1494
const pubkeyHash = ( 0 , crypto_1 . hash160 ) ( pubkey ) ;
1495
+ const pubkeyXOnly = pubkey . slice ( 1 , 33 ) ;
1395
1496
const decompiled = bscript . decompile ( script ) ;
1396
1497
if ( decompiled === null ) throw new Error ( 'Unknown script error' ) ;
1397
1498
return decompiled . some ( element => {
1398
1499
if ( typeof element === 'number' ) return false ;
1399
- return element . equals ( pubkey ) || element . equals ( pubkeyHash ) ;
1500
+ return (
1501
+ element . equals ( pubkey ) ||
1502
+ element . equals ( pubkeyHash ) ||
1503
+ element . equals ( pubkeyXOnly )
1504
+ ) ;
1400
1505
} ) ;
1401
1506
}
1402
1507
function classifyScript ( script ) {
1403
1508
if ( isP2WPKH ( script ) ) return 'witnesspubkeyhash' ;
1404
1509
if ( isP2PKH ( script ) ) return 'pubkeyhash' ;
1405
1510
if ( isP2MS ( script ) ) return 'multisig' ;
1406
1511
if ( isP2PK ( script ) ) return 'pubkey' ;
1512
+ if ( isP2TR ( script , ecc ) ) return 'taproot' ;
1407
1513
return 'nonstandard' ;
1408
1514
}
1409
1515
function range ( n ) {
0 commit comments