Skip to content

Commit c9b334b

Browse files
committed
test: add custom taproot finalizer (partial)
1 parent f32d706 commit c9b334b

File tree

1 file changed

+108
-1
lines changed

1 file changed

+108
-1
lines changed

test/integration/taproot.spec.ts

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ import BIP32Factory from 'bip32';
22
import ECPairFactory from 'ecpair';
33
import * as ecc from 'tiny-secp256k1';
44
import { describe, it } from 'mocha';
5+
import { PsbtInput, TapLeafScript } from 'bip174/src/lib/interfaces';
56
import { regtestUtils } from './_regtest';
67
import * as bitcoin from '../..';
78
import { 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

1013
const rng = require('randombytes');
1114
const 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
491598
const N_LESS_1 = Buffer.from(
492599
'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140',

0 commit comments

Comments
 (0)