@@ -2,10 +2,13 @@ import BIP32Factory from 'bip32';
22import ECPairFactory from 'ecpair' ;
33import * as ecc from 'tiny-secp256k1' ;
44import { describe , it } from 'mocha' ;
5+ import { PsbtInput , TapLeafScript } from 'bip174/src/lib/interfaces' ;
56import { regtestUtils } from './_regtest' ;
67import * as bitcoin from '../..' ;
78import { Taptree } from '../../src/types' ;
8- import { toXOnly , tapTreeToList } from '../../src/psbt/bip371' ;
9+ import { toXOnly , tapTreeToList , tapTreeFromList } from '../../src/psbt/bip371' ;
10+ import { witnessStackToScriptWitness } from '../../src/psbt/psbtutils' ;
11+ import { TapLeaf } from 'bip174/src/lib/interfaces' ;
912
1013const rng = require ( 'randombytes' ) ;
1114const regtest = regtestUtils . network ;
@@ -485,8 +488,112 @@ describe('bitcoinjs-lib (transaction with taproot)', () => {
485488 value : sendAmount ,
486489 } ) ;
487490 } ) ;
491+
492+ it ( 'can create (and broadcast via 3PBP) a taproot script-path spend Transaction - custom finalizer' , async ( ) => {
493+
494+
495+ const leafCount = 8 ;
496+ const leaves = Array . from ( { length : leafCount } ) . map (
497+ ( _ , index ) =>
498+ ( {
499+ depth : 3 ,
500+ leafVersion : 192 ,
501+ script : bitcoin . script . fromASM ( `OP_ADD OP_${ index * 2 } OP_EQUAL` ) ,
502+ } as TapLeaf ) ,
503+ ) ;
504+ const scriptTree = tapTreeFromList ( leaves ) ;
505+
506+ for ( let leafIndex = 1 ; leafIndex < leafCount ; leafIndex ++ ) {
507+ const redeem = {
508+ output : bitcoin . script . fromASM ( `OP_ADD OP_${ leafIndex * 2 } OP_EQUAL` ) ,
509+ redeemVersion : 192 ,
510+ } ;
511+
512+ const internalKey = bip32 . fromSeed ( rng ( 64 ) , regtest ) ;
513+ const { output, witness } = bitcoin . payments . p2tr ( {
514+ internalPubkey : toXOnly ( internalKey . publicKey ) ,
515+ scriptTree,
516+ redeem,
517+ network : regtest ,
518+ } ) ;
519+
520+
521+ // amount from faucet
522+ const amount = 42e4 ;
523+ // amount to send
524+ const sendAmount = amount - 1e4 ;
525+ // get faucet
526+ const unspent = await regtestUtils . faucetComplex ( output ! , amount ) ;
527+
528+ const psbt = new bitcoin . Psbt ( { network : regtest } ) ;
529+ psbt . addInput ( {
530+ hash : unspent . txId ,
531+ index : 0 ,
532+ witnessUtxo : { value : amount , script : output ! } ,
533+ } ) ;
534+
535+ const tapLeafScript : TapLeafScript = {
536+ leafVersion : redeem . redeemVersion ,
537+ script : redeem . output ,
538+ controlBlock : witness ! [ witness ! . length - 1 ] ,
539+ } ;
540+ psbt . updateInput ( 0 , { tapLeafScript : [ tapLeafScript ] } ) ;
541+
542+ const sendAddress =
543+ 'bcrt1pqknex3jwpsaatu5e5dcjw70nac3fr5k5y3hcxr4hgg6rljzp59nqs6a0vh' ;
544+ psbt . addOutput ( {
545+ value : sendAmount ,
546+ address : sendAddress ,
547+ } ) ;
548+
549+ const leafIndexFinalizerFn = buildLeafIndexFinalizer (
550+ tapLeafScript ,
551+ leafIndex ,
552+ ) ;
553+ psbt . finalizeInput ( 0 , leafIndexFinalizerFn ) ;
554+ console . log ( '### psbt finalized' , psbt . toBase64 ( ) ) ;
555+ const tx = psbt . extractTransaction ( ) ;
556+ const rawTx = tx . toBuffer ( ) ;
557+ const hex = rawTx . toString ( 'hex' ) ;
558+
559+ console . log ( '### hex' , hex )
560+ await regtestUtils . broadcast ( hex ) ;
561+ console . log ( '### verify' )
562+ await regtestUtils . verify ( {
563+ txId : tx . getId ( ) ,
564+ address : sendAddress ! ,
565+ vout : 0 ,
566+ value : sendAmount ,
567+ } ) ;
568+ }
569+ } ) ;
488570} ) ;
489571
572+ function buildLeafIndexFinalizer (
573+ tapLeafScript : TapLeafScript ,
574+ leafIndex : number ,
575+ ) {
576+ return (
577+ inputIndex : number ,
578+ _input : PsbtInput ,
579+ _tapLeafHashToFinalize ?: Buffer ,
580+ ) : {
581+ finalScriptWitness : Buffer | undefined ;
582+ } => {
583+ try {
584+ const scriptSolution = [
585+ bitcoin . script . fromASM ( `OP_${ leafIndex } OP_${ leafIndex } ` ) ,
586+ ] ;
587+ const witness = scriptSolution
588+ . concat ( tapLeafScript . script )
589+ . concat ( tapLeafScript . controlBlock ) ;
590+ return { finalScriptWitness : witnessStackToScriptWitness ( witness ) } ;
591+ } catch ( err ) {
592+ throw new Error ( `Can not finalize taproot input #${ inputIndex } : ${ err } ` ) ;
593+ }
594+ } ;
595+ }
596+
490597// Order of the curve (N) - 1
491598const N_LESS_1 = Buffer . from (
492599 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140' ,
0 commit comments