Skip to content
Prev Previous commit
Next Next commit
Support p2tr with 1 script and no tree
* Also added caching of `hashTree`, per todo. * Added a test for this functionality
  • Loading branch information
reardencode committed Mar 18, 2022
commit 299308ae2f186ea76bd89c58a3670f0ce7cfc631
16 changes: 10 additions & 6 deletions src/payments/p2tr.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ function p2tr(a, opts) {
}
return a.witness.slice();
});
const _hashTree = lazy.value(() => {
if (a.scriptTree) return (0, taprootutils_1.toHashTree)(a.scriptTree);
if (a.hash) return { hash: a.hash };
return;
});
const network = a.network || networks_1.bitcoin;
const o = { name: 'p2tr', network };
lazy.prop(o, 'address', () => {
Expand All @@ -82,8 +87,8 @@ function p2tr(a, opts) {
return bech32_1.bech32m.encode(network.bech32, words);
});
lazy.prop(o, 'hash', () => {
if (a.hash) return a.hash;
if (a.scriptTree) return (0, taprootutils_1.toHashTree)(a.scriptTree).hash;
const hashTree = _hashTree();
if (hashTree) return hashTree.hash;
const w = _witness();
if (w && w.length > 1) {
const controlBlock = w[w.length - 1];
Expand Down Expand Up @@ -144,9 +149,8 @@ function p2tr(a, opts) {
});
lazy.prop(o, 'witness', () => {
if (a.witness) return a.witness;
if (a.scriptTree && a.redeem && a.redeem.output && a.internalPubkey) {
// todo: optimize/cache
const hashTree = (0, taprootutils_1.toHashTree)(a.scriptTree);
const hashTree = _hashTree();
if (hashTree && a.redeem && a.redeem.output && a.internalPubkey) {
const leafHash = (0, taprootutils_1.tapleafHash)({
output: a.redeem.output,
version: o.redeemVersion,
Expand Down Expand Up @@ -204,7 +208,7 @@ function p2tr(a, opts) {
throw new TypeError('Invalid pubkey for p2tr');
}
if (a.hash && a.scriptTree) {
const hash = (0, taprootutils_1.toHashTree)(a.scriptTree).hash;
const hash = _hashTree().hash;
if (!a.hash.equals(hash)) throw new TypeError('Hash mismatch');
}
const witness = _witness();
Expand Down
22 changes: 22 additions & 0 deletions test/fixtures/p2tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,28 @@
"witness": null
}
},
{
"description": "address, pubkey, and output from internalPubkey redeem, and hash (one leaf, no tree)",
"arguments": {
"internalPubkey": "aba457d16a8d59151c387f24d1eb887efbe24644c1ee64b261282e7baebdb247",
"redeem": {
"output": "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac4 OP_CHECKSIG"
},
"hash": "b424dea09f840b932a00373cdcdbd25650b8c3acfe54a9f4a641a286721b8d26"
},
"expected": {
"name": "p2tr",
"address": "bc1pnxyp0ahcg53jzgrzj57hnlgdtqtzn7qqhmgjgczk8hzhcltq974qazepzf",
"pubkey": "998817f6f84523212062953d79fd0d581629f800bed12460563dc57c7d602faa",
"output": "OP_1 998817f6f84523212062953d79fd0d581629f800bed12460563dc57c7d602faa",
"signature": null,
"input": null,
"witness": [
"2050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac4ac",
"c0aba457d16a8d59151c387f24d1eb887efbe24644c1ee64b261282e7baebdb247"
]
}
},
{
"description": "address, pubkey, output and hash from internalPubkey and a script tree with seven leafs (2)",
"arguments": {
Expand Down
17 changes: 11 additions & 6 deletions ts_src/payments/p2tr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ export function p2tr(a: Payment, opts?: PaymentOpts): Payment {
return a.witness.slice();
});

const _hashTree = lazy.value(() => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

if (a.scriptTree) return toHashTree(a.scriptTree);
if (a.hash) return { hash: a.hash };
return;
});

const network = a.network || BITCOIN_NETWORK;
const o: Payment = { name: 'p2tr', network };

Expand All @@ -100,8 +106,8 @@ export function p2tr(a: Payment, opts?: PaymentOpts): Payment {
});

lazy.prop(o, 'hash', () => {
if (a.hash) return a.hash;
if (a.scriptTree) return toHashTree(a.scriptTree).hash;
const hashTree = _hashTree();
if (hashTree) return hashTree.hash;
const w = _witness();
if (w && w.length > 1) {
const controlBlock = w[w.length - 1];
Expand Down Expand Up @@ -161,9 +167,8 @@ export function p2tr(a: Payment, opts?: PaymentOpts): Payment {

lazy.prop(o, 'witness', () => {
if (a.witness) return a.witness;
if (a.scriptTree && a.redeem && a.redeem.output && a.internalPubkey) {
// todo: optimize/cache
const hashTree = toHashTree(a.scriptTree);
const hashTree = _hashTree();
if (hashTree && a.redeem && a.redeem.output && a.internalPubkey) {
const leafHash = tapleafHash({
output: a.redeem.output,
version: o.redeemVersion,
Expand Down Expand Up @@ -227,7 +232,7 @@ export function p2tr(a: Payment, opts?: PaymentOpts): Payment {
}

if (a.hash && a.scriptTree) {
const hash = toHashTree(a.scriptTree).hash;
const hash = _hashTree()!.hash;
if (!a.hash.equals(hash)) throw new TypeError('Hash mismatch');
}

Expand Down