@@ -1367,41 +1367,105 @@ function checkFees(psbt: Psbt, cache: PsbtCache, opts: PsbtOpts): void {
13671367}
13681368
13691369function checkInputsForPartialSig ( inputs : PsbtInput [ ] , action : string ) : void {
1370- // todo: add for taproot
13711370 inputs . forEach ( input => {
1372- let throws = false ;
1373- let pSigs : PartialSig [ ] = [ ] ;
1374- if ( ( input . partialSig || [ ] ) . length === 0 ) {
1375- if ( ! input . finalScriptSig && ! input . finalScriptWitness ) return ;
1376- pSigs = getPsigsFromInputFinalScripts ( input ) ;
1377- } else {
1378- pSigs = input . partialSig ! ;
1379- }
1380- pSigs . forEach ( pSig => {
1381- const { hashType } = bscript . signature . decode ( pSig . signature ) ;
1382- const whitelist : string [ ] = [ ] ;
1383- const isAnyoneCanPay = hashType & Transaction . SIGHASH_ANYONECANPAY ;
1384- if ( isAnyoneCanPay ) whitelist . push ( 'addInput' ) ;
1385- const hashMod = hashType & 0x1f ;
1386- switch ( hashMod ) {
1387- case Transaction . SIGHASH_ALL :
1388- break ;
1389- case Transaction . SIGHASH_SINGLE :
1390- case Transaction . SIGHASH_NONE :
1391- whitelist . push ( 'addOutput' ) ;
1392- whitelist . push ( 'setInputSequence' ) ;
1393- break ;
1394- }
1395- if ( whitelist . indexOf ( action ) === - 1 ) {
1396- throws = true ;
1397- }
1398- } ) ;
1399- if ( throws ) {
1371+ const throws = isTaprootInput ( input )
1372+ ? checkTaprootInputForSigs ( input , action )
1373+ : checkInputForSig ( input , action ) ;
1374+ if ( throws )
14001375 throw new Error ( 'Can not modify transaction, signatures exist.' ) ;
1401- }
14021376 } ) ;
14031377}
14041378
1379+ function checkInputForSig ( input : PsbtInput , action : string ) : boolean {
1380+ const pSigs = extractPartialSigs ( input ) ;
1381+ return pSigs . some ( pSig =>
1382+ signatureBlocksAction ( pSig , bscript . signature . decode , action ) ,
1383+ ) ;
1384+ }
1385+
1386+ function checkTaprootInputForSigs ( input : PsbtInput , action : string ) : boolean {
1387+ const sigs = extractTaprootSigs ( input ) ;
1388+ return sigs . some ( sig =>
1389+ signatureBlocksAction ( sig , decodeSchnorSignature , action ) ,
1390+ ) ;
1391+ }
1392+
1393+ function decodeSchnorSignature (
1394+ signature : Buffer ,
1395+ ) : {
1396+ signature : Buffer ;
1397+ hashType : number ;
1398+ } {
1399+ return {
1400+ signature : signature . slice ( 0 , 64 ) ,
1401+ hashType : signature . slice ( 64 ) [ 0 ] || Transaction . SIGHASH_DEFAULT ,
1402+ } ;
1403+ }
1404+
1405+ function extractTaprootSigs ( input : PsbtInput ) : Buffer [ ] {
1406+ const sigs : Buffer [ ] = [ ] ;
1407+ if ( input . tapKeySig ) sigs . push ( input . tapKeySig ) ;
1408+ if ( input . tapScriptSig )
1409+ sigs . push ( ...input . tapScriptSig . map ( s => s . signature ) ) ;
1410+ if ( ! sigs . length ) {
1411+ const finalTapKeySig = getTapKeySigFromWithness ( input . finalScriptWitness ) ;
1412+ if ( finalTapKeySig ) sigs . push ( finalTapKeySig ) ;
1413+ }
1414+
1415+ return sigs ;
1416+ }
1417+
1418+ function getTapKeySigFromWithness (
1419+ finalScriptWitness ?: Buffer ,
1420+ ) : Buffer | undefined {
1421+ if ( ! finalScriptWitness ) return ;
1422+ const witness = finalScriptWitness . slice ( 2 ) ;
1423+ // todo: add schnor signature validation
1424+ if ( witness . length === 64 || witness . length === 65 ) return witness ;
1425+ }
1426+
1427+ function extractPartialSigs ( input : PsbtInput ) : Buffer [ ] {
1428+ let pSigs : PartialSig [ ] = [ ] ;
1429+ if ( ( input . partialSig || [ ] ) . length === 0 ) {
1430+ if ( ! input . finalScriptSig && ! input . finalScriptWitness ) return [ ] ;
1431+ pSigs = getPsigsFromInputFinalScripts ( input ) ;
1432+ } else {
1433+ pSigs = input . partialSig ! ;
1434+ }
1435+ return pSigs . map ( p => p . signature ) ;
1436+ }
1437+
1438+ type SignatureDecodeFunc = (
1439+ buffer : Buffer ,
1440+ ) => {
1441+ signature : Buffer ;
1442+ hashType : number ;
1443+ } ;
1444+ function signatureBlocksAction (
1445+ signature : Buffer ,
1446+ signatureDecodeFn : SignatureDecodeFunc ,
1447+ action : string ,
1448+ ) : boolean {
1449+ const { hashType } = signatureDecodeFn ( signature ) ;
1450+ const whitelist : string [ ] = [ ] ;
1451+ const isAnyoneCanPay = hashType & Transaction . SIGHASH_ANYONECANPAY ;
1452+ if ( isAnyoneCanPay ) whitelist . push ( 'addInput' ) ;
1453+ const hashMod = hashType & 0x1f ;
1454+ switch ( hashMod ) {
1455+ case Transaction . SIGHASH_ALL :
1456+ break ;
1457+ case Transaction . SIGHASH_SINGLE :
1458+ case Transaction . SIGHASH_NONE :
1459+ whitelist . push ( 'addOutput' ) ;
1460+ whitelist . push ( 'setInputSequence' ) ;
1461+ break ;
1462+ }
1463+ if ( whitelist . indexOf ( action ) === - 1 ) {
1464+ return true ;
1465+ }
1466+ return false ;
1467+ }
1468+
14051469function checkPartialSigSighashes ( input : PsbtInput ) : void {
14061470 if ( ! input . sighashType || ! input . partialSig ) return ;
14071471 const { partialSig, sighashType } = input ;
0 commit comments