Skip to content

Commit 9eb0790

Browse files
committed
test: add tests for taptree conversion to tapleaf
1 parent 6cbac53 commit 9eb0790

File tree

6 files changed

+83
-6
lines changed

6 files changed

+83
-6
lines changed

src/psbt/bip371.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ exports.tweakInternalPubKey = tweakInternalPubKey;
7979
* @returns a list of BIP 371 tapleaves
8080
*/
8181
function tapTreeToList(tree) {
82+
if (!(0, types_1.isTaptree)(tree))
83+
throw new Error(
84+
'Cannot convert taptree to tapleaf list. Expecting a tapree structure.',
85+
);
8286
return _tapTreeToList(tree);
8387
}
8488
exports.tapTreeToList = tapTreeToList;

src/types.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ exports.Network = exports.typeforce.compile({
7070
});
7171
exports.TAPLEAF_VERSION_MASK = 0xfe;
7272
function isTapleaf(o) {
73-
if (!('output' in o)) return false;
73+
if (!o || !('output' in o)) return false;
7474
if (!buffer_1.Buffer.isBuffer(o.output)) return false;
7575
if (o.version !== undefined)
7676
return (o.version & exports.TAPLEAF_VERSION_MASK) === o.version;

test/payments.utils.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,11 +186,14 @@ export function from(path: string, object: any, result?: any): any {
186186
return result;
187187
}
188188

189-
function convertScriptTree(scriptTree: any): any {
190-
if (Array.isArray(scriptTree)) return scriptTree.map(convertScriptTree);
189+
export function convertScriptTree(scriptTree: any, leafVersion?: number): any {
190+
if (Array.isArray(scriptTree))
191+
return scriptTree.map(t => convertScriptTree(t, leafVersion));
191192

192193
const script = Object.assign({}, scriptTree);
193-
if (typeof script.output === 'string')
194+
if (typeof script.output === 'string') {
194195
script.output = asmToBuffer(scriptTree.output);
196+
script.version = script.version || leafVersion;
197+
}
195198
return script;
196199
}

test/psbt.spec.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import * as crypto from 'crypto';
55
import ECPairFactory from 'ecpair';
66
import { describe, it } from 'mocha';
77

8+
import { convertScriptTree } from './payments.utils';
9+
import { LEAF_VERSION_TAPSCRIPT } from '../src/payments/taprootutils';
10+
import { tapTreeToList, tapTreeFromList } from '../src/psbt/bip371';
11+
import { Taptree } from '../src/types';
812
import { initEccLib } from '../src';
913

1014
const bip32 = BIP32Factory(ecc);
@@ -13,6 +17,7 @@ const ECPair = ECPairFactory(ecc);
1317
import { networks as NETWORKS, payments, Psbt, Signer, SignerAsync } from '..';
1418

1519
import * as preFixtures from './fixtures/psbt.json';
20+
import * as taprootFixtures from './fixtures/p2tr.json';
1621

1722
const validator = (
1823
pubkey: Buffer,
@@ -1099,6 +1104,67 @@ describe(`Psbt`, () => {
10991104
});
11001105
});
11011106

1107+
describe('tapTreeToList/tapTreeFromList', () => {
1108+
it('Correctly converts a Taptree to a Tapleaf list and back', () => {
1109+
taprootFixtures.valid
1110+
.filter(f => f.arguments.scriptTree)
1111+
.map(f => f.arguments.scriptTree)
1112+
.forEach(scriptTree => {
1113+
const originalTree = convertScriptTree(
1114+
scriptTree,
1115+
LEAF_VERSION_TAPSCRIPT,
1116+
);
1117+
const list = tapTreeToList(originalTree);
1118+
const treeFromList = tapTreeFromList(list);
1119+
1120+
assert.deepStrictEqual(treeFromList, originalTree);
1121+
});
1122+
});
1123+
it('Throws if too many leaves on a given level', () => {
1124+
const list = Array.from({ length: 5 }).map(() => ({
1125+
depth: 2,
1126+
leafVersion: LEAF_VERSION_TAPSCRIPT,
1127+
script: Buffer.from([]),
1128+
}));
1129+
assert.throws(() => {
1130+
tapTreeFromList(list);
1131+
}, new RegExp('No room left to insert tapleaf in tree'));
1132+
});
1133+
it('Throws if taptree depth is exceeded', () => {
1134+
let tree: Taptree = [
1135+
{ output: Buffer.from([]) },
1136+
{ output: Buffer.from([]) },
1137+
];
1138+
Array.from({ length: 129 }).forEach(
1139+
() => (tree = [tree, { output: Buffer.from([]) }]),
1140+
);
1141+
assert.throws(() => {
1142+
tapTreeToList(tree as Taptree);
1143+
}, new RegExp('Max taptree depth exceeded.'));
1144+
});
1145+
it('Throws if tapleaf depth is to high', () => {
1146+
const list = [
1147+
{
1148+
depth: 129,
1149+
leafVersion: LEAF_VERSION_TAPSCRIPT,
1150+
script: Buffer.from([]),
1151+
},
1152+
];
1153+
assert.throws(() => {
1154+
tapTreeFromList(list);
1155+
}, new RegExp('Max taptree depth exceeded.'));
1156+
});
1157+
it('Throws if not a valid taptree structure', () => {
1158+
const tree = Array.from({ length: 3 }).map(() => ({
1159+
output: Buffer.from([]),
1160+
}));
1161+
1162+
assert.throws(() => {
1163+
tapTreeToList(tree as Taptree);
1164+
}, new RegExp('Cannot convert taptree to tapleaf list. Expecting a tapree structure.'));
1165+
});
1166+
});
1167+
11021168
describe('getFeeRate', () => {
11031169
it('Throws error if called before inputs are finalized', () => {
11041170
const f = fixtures.getFeeRate;

ts_src/psbt/bip371.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Taptree, Tapleaf, isTapleaf } from '../types';
1+
import { Taptree, Tapleaf, isTapleaf, isTaptree } from '../types';
22
import {
33
PsbtInput,
44
TapLeafScript,
@@ -111,6 +111,10 @@ export function tweakInternalPubKey(
111111
* @returns a list of BIP 371 tapleaves
112112
*/
113113
export function tapTreeToList(tree: Taptree): TapLeaf[] {
114+
if (!isTaptree(tree))
115+
throw new Error(
116+
'Cannot convert taptree to tapleaf list. Expecting a tapree structure.',
117+
);
114118
return _tapTreeToList(tree);
115119
}
116120

ts_src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export interface Tapleaf {
7979

8080
export const TAPLEAF_VERSION_MASK = 0xfe;
8181
export function isTapleaf(o: any): o is Tapleaf {
82-
if (!('output' in o)) return false;
82+
if (!o || !('output' in o)) return false;
8383
if (!NBuffer.isBuffer(o.output)) return false;
8484
if (o.version !== undefined)
8585
return (o.version & TAPLEAF_VERSION_MASK) === o.version;

0 commit comments

Comments
 (0)