11'use strict' ;
22Object . defineProperty ( exports , '__esModule' , { value : true } ) ;
33exports . Psbt = void 0 ;
4+ const ecc = require ( 'tiny-secp256k1' ) ; // TODO: extract
5+ const ecpair_1 = require ( 'ecpair' ) ;
46const bip174_1 = require ( 'bip174' ) ;
57const varuint = require ( 'bip174/src/lib/converter/varint' ) ;
68const utils_1 = require ( 'bip174/src/lib/utils' ) ;
@@ -11,6 +13,7 @@ const networks_1 = require('./networks');
1113const payments = require ( './payments' ) ;
1214const bscript = require ( './script' ) ;
1315const transaction_1 = require ( './transaction' ) ;
16+ const taprootutils_1 = require ( './payments/taprootutils' ) ;
1417/**
1518 * These are the default arguments for a Psbt instance.
1619 */
@@ -103,6 +106,39 @@ class Psbt {
103106 checkTxForDupeIns ( psbt . __CACHE . __TX , psbt . __CACHE ) ;
104107 return psbt ;
105108 }
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+ }
106142 get inputCount ( ) {
107143 return this . data . inputs . length ;
108144 }
@@ -298,7 +334,11 @@ class Psbt {
298334 }
299335 getInputType ( inputIndex ) {
300336 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+ ) ;
302342 const result = getMeaningfulScript (
303343 script ,
304344 inputIndex ,
@@ -355,13 +395,21 @@ class Psbt {
355395 let hashCache ;
356396 let scriptCache ;
357397 let sighashCache ;
398+ const scriptType = this . getInputType ( inputIndex ) ;
358399 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 ) ;
360407 const { hash, script } =
361408 sighashCache !== sig . hashType
362409 ? getHashForSig (
363410 inputIndex ,
364411 Object . assign ( { } , input , { sighashType : sig . hashType } ) ,
412+ this . data . inputs ,
365413 this . __CACHE ,
366414 true ,
367415 )
@@ -526,13 +574,30 @@ class Psbt {
526574 this . __CACHE ,
527575 sighashTypes ,
528576 ) ;
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+ }
536601 return this ;
537602 }
538603 signInputAsync (
@@ -671,6 +736,7 @@ function canFinalize(input, script, scriptType) {
671736 case 'pubkey' :
672737 case 'pubkeyhash' :
673738 case 'witnesspubkeyhash' :
739+ case 'taproot' :
674740 return hasSigs ( 1 , input . partialSig ) ;
675741 case 'multisig' :
676742 const p2ms = payments . p2ms ( { output : script } ) ;
@@ -704,9 +770,9 @@ function isFinalized(input) {
704770 return ! ! input . finalScriptSig || ! ! input . finalScriptWitness ;
705771}
706772function isPaymentFactory ( payment ) {
707- return script => {
773+ return ( script , eccLib ) => {
708774 try {
709- payment ( { output : script } ) ;
775+ payment ( { output : script } , { eccLib } ) ;
710776 return true ;
711777 } catch ( err ) {
712778 return false ;
@@ -719,6 +785,7 @@ const isP2PKH = isPaymentFactory(payments.p2pkh);
719785const isP2WPKH = isPaymentFactory ( payments . p2wpkh ) ;
720786const isP2WSHScript = isPaymentFactory ( payments . p2wsh ) ;
721787const isP2SHScript = isPaymentFactory ( payments . p2sh ) ;
788+ const isP2TR = isPaymentFactory ( payments . p2tr ) ;
722789function bip32DerivationIsMine ( root ) {
723790 return d => {
724791 if ( ! d . masterFingerprint . equals ( root . fingerprint ) ) return false ;
@@ -920,6 +987,7 @@ function getHashAndSighashType(
920987 const { hash, sighashType, script } = getHashForSig (
921988 inputIndex ,
922989 input ,
990+ inputs ,
923991 cache ,
924992 false ,
925993 sighashTypes ,
@@ -930,7 +998,14 @@ function getHashAndSighashType(
930998 sighashType,
931999 } ;
9321000}
933- function getHashForSig ( inputIndex , input , cache , forValidate , sighashTypes ) {
1001+ function getHashForSig (
1002+ inputIndex ,
1003+ input ,
1004+ inputs ,
1005+ cache ,
1006+ forValidate ,
1007+ sighashTypes ,
1008+ ) {
9341009 const unsignedTx = cache . __TX ;
9351010 const sighashType =
9361011 input . sighashType || transaction_1 . Transaction . SIGHASH_ALL ;
@@ -988,6 +1063,18 @@ function getHashForSig(inputIndex, input, cache, forValidate, sighashTypes) {
9881063 prevout . value ,
9891064 sighashType ,
9901065 ) ;
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+ ) ;
9911078 } else {
9921079 // non-segwit
9931080 if (
@@ -1050,6 +1137,15 @@ function getPayment(script, scriptType, partialSig) {
10501137 signature : partialSig [ 0 ] . signature ,
10511138 } ) ;
10521139 break ;
1140+ case 'taproot' :
1141+ payment = payments . p2tr (
1142+ {
1143+ output : script ,
1144+ signature : partialSig [ 0 ] . signature ,
1145+ } ,
1146+ { eccLib : ecc } ,
1147+ ) ;
1148+ break ;
10531149 }
10541150 return payment ;
10551151}
@@ -1094,7 +1190,7 @@ function getScriptFromInput(inputIndex, input, cache) {
10941190 res . script = input . witnessUtxo . script ;
10951191 }
10961192 }
1097- if ( input . witnessScript || isP2WPKH ( res . script ) ) {
1193+ if ( input . witnessScript || isP2WPKH ( res . script ) || isP2TR ( res . script , ecc ) ) {
10981194 res . isSegwit = true ;
10991195 }
11001196 return res ;
@@ -1267,22 +1363,26 @@ function nonWitnessUtxoTxFromCache(cache, input, inputIndex) {
12671363 }
12681364 return c [ inputIndex ] ;
12691365}
1270- function getScriptFromUtxo ( inputIndex , input , cache ) {
1366+ function getScriptAndAmountFromUtxo ( inputIndex , input , cache ) {
12711367 if ( input . witnessUtxo !== undefined ) {
1272- return input . witnessUtxo . script ;
1368+ return {
1369+ script : input . witnessUtxo . script ,
1370+ value : input . witnessUtxo . value ,
1371+ } ;
12731372 } else if ( input . nonWitnessUtxo !== undefined ) {
12741373 const nonWitnessUtxoTx = nonWitnessUtxoTxFromCache (
12751374 cache ,
12761375 input ,
12771376 inputIndex ,
12781377 ) ;
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 } ;
12801380 } else {
12811381 throw new Error ( "Can't find pubkey in input without Utxo data" ) ;
12821382 }
12831383}
12841384function pubkeyInInput ( pubkey , input , inputIndex , cache ) {
1285- const script = getScriptFromUtxo ( inputIndex , input , cache ) ;
1385+ const { script } = getScriptAndAmountFromUtxo ( inputIndex , input , cache ) ;
12861386 const { meaningfulScript } = getMeaningfulScript (
12871387 script ,
12881388 inputIndex ,
@@ -1392,18 +1492,24 @@ function checkInvalidP2WSH(script) {
13921492}
13931493function pubkeyInScript ( pubkey , script ) {
13941494 const pubkeyHash = ( 0 , crypto_1 . hash160 ) ( pubkey ) ;
1495+ const pubkeyXOnly = pubkey . slice ( 1 , 33 ) ;
13951496 const decompiled = bscript . decompile ( script ) ;
13961497 if ( decompiled === null ) throw new Error ( 'Unknown script error' ) ;
13971498 return decompiled . some ( element => {
13981499 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+ ) ;
14001505 } ) ;
14011506}
14021507function classifyScript ( script ) {
14031508 if ( isP2WPKH ( script ) ) return 'witnesspubkeyhash' ;
14041509 if ( isP2PKH ( script ) ) return 'pubkeyhash' ;
14051510 if ( isP2MS ( script ) ) return 'multisig' ;
14061511 if ( isP2PK ( script ) ) return 'pubkey' ;
1512+ if ( isP2TR ( script , ecc ) ) return 'taproot' ;
14071513 return 'nonstandard' ;
14081514}
14091515function range ( n ) {
0 commit comments