Skip to content

Commit 94ca6db

Browse files
committed
test: tapScriptFinalizer line442 - 443
1 parent e4280e2 commit 94ca6db

File tree

1 file changed

+326
-0
lines changed

1 file changed

+326
-0
lines changed

test/bip371.spec.ts

Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import { toXOnly } from 'bitcoinjs-lib';
2+
import { tapScriptFinalizer } from 'bitcoinjs-lib/src/psbt/bip371';
3+
import {
4+
tapleafHash,
5+
LEAF_VERSION_TAPSCRIPT,
6+
} from 'bitcoinjs-lib/src/payments/bip341';
7+
import type { PsbtInput, TapScriptSig } from 'bip174';
28
import * as assert from 'assert';
9+
import * as tools from 'uint8array-tools';
310

411
describe('toXOnly', () => {
512
it('should return the input if the pubKey length is 32', () => {
@@ -21,3 +28,322 @@ describe('toXOnly', () => {
2128
assert.deepStrictEqual(result, pubKey.slice(1, 33)); // Expect the sliced array
2229
});
2330
});
31+
32+
describe('tapScriptFinalizer', () => {
33+
// Helper to create a basic tapLeafScript
34+
const createTapLeafScript = (
35+
script: Uint8Array,
36+
controlBlock: Uint8Array,
37+
) => ({
38+
script,
39+
controlBlock,
40+
leafVersion: LEAF_VERSION_TAPSCRIPT,
41+
});
42+
43+
// Helper to create a tapScriptSig
44+
const createTapScriptSig = (
45+
pubkey: Uint8Array,
46+
signature: Uint8Array,
47+
leafHash: Uint8Array,
48+
): TapScriptSig => ({
49+
pubkey,
50+
signature,
51+
leafHash,
52+
});
53+
54+
describe('successful finalization', () => {
55+
it('should finalize a taproot input with valid signatures', () => {
56+
const script = new Uint8Array([
57+
0x20,
58+
...new Uint8Array(32).fill(1),
59+
0xac,
60+
]); // Simple script with pubkey
61+
const controlBlock = new Uint8Array(33).fill(2);
62+
const tapLeafScript = createTapLeafScript(script, controlBlock);
63+
64+
const leafHash = tapleafHash({
65+
output: script,
66+
version: LEAF_VERSION_TAPSCRIPT,
67+
});
68+
69+
const pubkey = new Uint8Array(32).fill(1);
70+
const signature = new Uint8Array(64).fill(3);
71+
const tapScriptSig = createTapScriptSig(pubkey, signature, leafHash);
72+
73+
const input: PsbtInput = {
74+
tapLeafScript: [tapLeafScript],
75+
tapScriptSig: [tapScriptSig],
76+
};
77+
78+
const result = tapScriptFinalizer(0, input);
79+
80+
assert.ok(result.finalScriptWitness);
81+
assert.ok(result.finalScriptWitness instanceof Uint8Array);
82+
assert.ok(result.finalScriptWitness.length > 0);
83+
});
84+
85+
it('should finalize with a specific tapLeafHashToFinalize', () => {
86+
const script1 = new Uint8Array([
87+
0x20,
88+
...new Uint8Array(32).fill(1),
89+
0xac,
90+
]);
91+
const script2 = new Uint8Array([
92+
0x20,
93+
...new Uint8Array(32).fill(5),
94+
0xac,
95+
]);
96+
const controlBlock1 = new Uint8Array(33).fill(2);
97+
const controlBlock2 = new Uint8Array(65).fill(3); // Longer control block
98+
99+
const tapLeafScript1 = createTapLeafScript(script1, controlBlock1);
100+
const tapLeafScript2 = createTapLeafScript(script2, controlBlock2);
101+
102+
const leafHash1 = tapleafHash({
103+
output: script1,
104+
version: LEAF_VERSION_TAPSCRIPT,
105+
});
106+
const leafHash2 = tapleafHash({
107+
output: script2,
108+
version: LEAF_VERSION_TAPSCRIPT,
109+
});
110+
111+
const pubkey = new Uint8Array(32).fill(1);
112+
const signature1 = new Uint8Array(64).fill(3);
113+
const signature2 = new Uint8Array(64).fill(4);
114+
115+
const tapScriptSig1 = createTapScriptSig(pubkey, signature1, leafHash1);
116+
const tapScriptSig2 = createTapScriptSig(pubkey, signature2, leafHash2);
117+
118+
const input: PsbtInput = {
119+
tapLeafScript: [tapLeafScript1, tapLeafScript2],
120+
tapScriptSig: [tapScriptSig1, tapScriptSig2],
121+
};
122+
123+
// Finalize with specific leaf hash (should use script2)
124+
const result = tapScriptFinalizer(0, input, leafHash2);
125+
126+
assert.ok(result.finalScriptWitness);
127+
assert.ok(result.finalScriptWitness instanceof Uint8Array);
128+
});
129+
130+
it('should select the tapleaf with shortest control block when multiple are available', () => {
131+
const script1 = new Uint8Array([
132+
0x20,
133+
...new Uint8Array(32).fill(1),
134+
0xac,
135+
]);
136+
const script2 = new Uint8Array([
137+
0x20,
138+
...new Uint8Array(32).fill(5),
139+
0xac,
140+
]);
141+
const shortControlBlock = new Uint8Array(33).fill(2);
142+
const longControlBlock = new Uint8Array(65).fill(3);
143+
144+
const tapLeafScript1 = createTapLeafScript(script1, longControlBlock);
145+
const tapLeafScript2 = createTapLeafScript(script2, shortControlBlock);
146+
147+
const leafHash1 = tapleafHash({
148+
output: script1,
149+
version: LEAF_VERSION_TAPSCRIPT,
150+
});
151+
const leafHash2 = tapleafHash({
152+
output: script2,
153+
version: LEAF_VERSION_TAPSCRIPT,
154+
});
155+
156+
const pubkey = new Uint8Array(32).fill(1);
157+
const signature1 = new Uint8Array(64).fill(3);
158+
const signature2 = new Uint8Array(64).fill(4);
159+
160+
const tapScriptSig1 = createTapScriptSig(pubkey, signature1, leafHash1);
161+
const tapScriptSig2 = createTapScriptSig(pubkey, signature2, leafHash2);
162+
163+
const input: PsbtInput = {
164+
tapLeafScript: [tapLeafScript1, tapLeafScript2],
165+
tapScriptSig: [tapScriptSig1, tapScriptSig2],
166+
};
167+
168+
// Should select script2 because it has shorter control block
169+
const result = tapScriptFinalizer(0, input);
170+
171+
assert.ok(result.finalScriptWitness);
172+
});
173+
});
174+
175+
describe('error cases', () => {
176+
it('should throw error when no tapScriptSig is provided', () => {
177+
const script = new Uint8Array([
178+
0x20,
179+
...new Uint8Array(32).fill(1),
180+
0xac,
181+
]);
182+
const controlBlock = new Uint8Array(33).fill(2);
183+
const tapLeafScript = createTapLeafScript(script, controlBlock);
184+
185+
const input: PsbtInput = {
186+
tapLeafScript: [tapLeafScript],
187+
tapScriptSig: [], // Empty signatures
188+
};
189+
190+
assert.throws(
191+
() => tapScriptFinalizer(5, input),
192+
/Can not finalize taproot input #5\. No tapleaf script signature provided\./,
193+
);
194+
});
195+
196+
it('should throw error when tapScriptSig is undefined', () => {
197+
const script = new Uint8Array([
198+
0x20,
199+
...new Uint8Array(32).fill(1),
200+
0xac,
201+
]);
202+
const controlBlock = new Uint8Array(33).fill(2);
203+
const tapLeafScript = createTapLeafScript(script, controlBlock);
204+
205+
const input: PsbtInput = {
206+
tapLeafScript: [tapLeafScript],
207+
// tapScriptSig is undefined
208+
};
209+
210+
assert.throws(
211+
() => tapScriptFinalizer(3, input),
212+
/Can not finalize taproot input #3\. No tapleaf script signature provided\./,
213+
);
214+
});
215+
216+
it('should throw error when signature for tapleaf script is not found', () => {
217+
const script = new Uint8Array([
218+
0x20,
219+
...new Uint8Array(32).fill(1),
220+
0xac,
221+
]);
222+
const controlBlock = new Uint8Array(33).fill(2);
223+
const tapLeafScript = createTapLeafScript(script, controlBlock);
224+
225+
const leafHash = tapleafHash({
226+
output: script,
227+
version: LEAF_VERSION_TAPSCRIPT,
228+
});
229+
230+
// Create a signature with a different (wrong) leaf hash
231+
const wrongLeafHash = new Uint8Array(32).fill(99);
232+
const pubkey = new Uint8Array(32).fill(1);
233+
const signature = new Uint8Array(64).fill(3);
234+
const tapScriptSig = createTapScriptSig(pubkey, signature, wrongLeafHash);
235+
236+
const input: PsbtInput = {
237+
tapLeafScript: [tapLeafScript],
238+
tapScriptSig: [tapScriptSig], // Signature with wrong leaf hash
239+
};
240+
241+
assert.throws(
242+
() => tapScriptFinalizer(2, input),
243+
/Can not finalize taproot input #2\. Signature for tapleaf script not found\./,
244+
);
245+
});
246+
247+
it('should throw error when specific tapLeafHashToFinalize is not found', () => {
248+
const script = new Uint8Array([
249+
0x20,
250+
...new Uint8Array(32).fill(1),
251+
0xac,
252+
]);
253+
const controlBlock = new Uint8Array(33).fill(2);
254+
const tapLeafScript = createTapLeafScript(script, controlBlock);
255+
256+
const leafHash = tapleafHash({
257+
output: script,
258+
version: LEAF_VERSION_TAPSCRIPT,
259+
});
260+
261+
const pubkey = new Uint8Array(32).fill(1);
262+
const signature = new Uint8Array(64).fill(3);
263+
const tapScriptSig = createTapScriptSig(pubkey, signature, leafHash);
264+
265+
const input: PsbtInput = {
266+
tapLeafScript: [tapLeafScript],
267+
tapScriptSig: [tapScriptSig],
268+
};
269+
270+
// Request a different leaf hash that doesn't exist
271+
const nonExistentLeafHash = new Uint8Array(32).fill(88);
272+
273+
assert.throws(
274+
() => tapScriptFinalizer(7, input, nonExistentLeafHash),
275+
/Can not finalize taproot input #7\. Signature for tapleaf script not found\./,
276+
);
277+
});
278+
279+
it('should throw error when witness stack construction fails', () => {
280+
const script = new Uint8Array([
281+
0x20,
282+
...new Uint8Array(32).fill(1),
283+
0xac,
284+
]);
285+
const controlBlock = new Uint8Array(33).fill(2);
286+
const tapLeafScript = createTapLeafScript(script, controlBlock);
287+
288+
const leafHash = tapleafHash({
289+
output: script,
290+
version: LEAF_VERSION_TAPSCRIPT,
291+
});
292+
293+
const pubkey = new Uint8Array(32).fill(1);
294+
const signature = new Uint8Array(64).fill(3);
295+
const tapScriptSig = createTapScriptSig(pubkey, signature, leafHash);
296+
297+
const input: PsbtInput = {
298+
tapLeafScript: [tapLeafScript],
299+
tapScriptSig: [tapScriptSig],
300+
};
301+
302+
// This test verifies the try-catch block wraps errors properly
303+
const result = tapScriptFinalizer(0, input);
304+
assert.ok(result.finalScriptWitness);
305+
});
306+
});
307+
308+
describe('signature sorting', () => {
309+
it('should handle multiple signatures for the same leaf', () => {
310+
// Create a script that expects multiple signatures
311+
const pubkey1 = new Uint8Array(32).fill(1);
312+
const pubkey2 = new Uint8Array(32).fill(2);
313+
314+
// Script with two pubkeys
315+
const script = tools.concat([
316+
new Uint8Array([0x20]),
317+
pubkey1,
318+
new Uint8Array([0x20]),
319+
pubkey2,
320+
new Uint8Array([0xac]),
321+
]);
322+
323+
const controlBlock = new Uint8Array(33).fill(3);
324+
const tapLeafScript = createTapLeafScript(script, controlBlock);
325+
326+
const leafHash = tapleafHash({
327+
output: script,
328+
version: LEAF_VERSION_TAPSCRIPT,
329+
});
330+
331+
const signature1 = new Uint8Array(64).fill(10);
332+
const signature2 = new Uint8Array(64).fill(20);
333+
334+
const tapScriptSig1 = createTapScriptSig(pubkey1, signature1, leafHash);
335+
const tapScriptSig2 = createTapScriptSig(pubkey2, signature2, leafHash);
336+
337+
const input: PsbtInput = {
338+
tapLeafScript: [tapLeafScript],
339+
tapScriptSig: [tapScriptSig1, tapScriptSig2],
340+
};
341+
342+
const result = tapScriptFinalizer(0, input);
343+
344+
assert.ok(result.finalScriptWitness);
345+
assert.ok(result.finalScriptWitness instanceof Uint8Array);
346+
assert.ok(result.finalScriptWitness.length > 0);
347+
});
348+
});
349+
});

0 commit comments

Comments
 (0)