@btc-vision/bitcoin 7.0.0-beta.0 → 7.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/README.md +112 -13
  2. package/benchmark-compare/BENCHMARK.md +74 -59
  3. package/benchmark-compare/compare.bench.ts +249 -96
  4. package/benchmark-compare/harness.ts +23 -25
  5. package/benchmark-compare/package.json +1 -0
  6. package/browser/address.d.ts +4 -4
  7. package/browser/address.d.ts.map +1 -1
  8. package/browser/chunks/{psbt-parallel-B-dfm5GZ.js → psbt-parallel-jZ6QcCnM.js} +3128 -2731
  9. package/browser/index.d.ts +1 -1
  10. package/browser/index.d.ts.map +1 -1
  11. package/browser/index.js +603 -585
  12. package/browser/io/base58check.d.ts +1 -25
  13. package/browser/io/base58check.d.ts.map +1 -1
  14. package/browser/io/base64.d.ts.map +1 -1
  15. package/browser/networks.d.ts +1 -0
  16. package/browser/networks.d.ts.map +1 -1
  17. package/browser/payments/bip341.d.ts +17 -0
  18. package/browser/payments/bip341.d.ts.map +1 -1
  19. package/browser/payments/index.d.ts +3 -2
  20. package/browser/payments/index.d.ts.map +1 -1
  21. package/browser/payments/p2mr.d.ts +169 -0
  22. package/browser/payments/p2mr.d.ts.map +1 -0
  23. package/browser/payments/types.d.ts +11 -1
  24. package/browser/payments/types.d.ts.map +1 -1
  25. package/browser/psbt/bip371.d.ts +30 -0
  26. package/browser/psbt/bip371.d.ts.map +1 -1
  27. package/browser/psbt/psbtutils.d.ts +1 -0
  28. package/browser/psbt/psbtutils.d.ts.map +1 -1
  29. package/browser/psbt.d.ts.map +1 -1
  30. package/browser/workers/index.js +9 -9
  31. package/build/address.d.ts +4 -4
  32. package/build/address.d.ts.map +1 -1
  33. package/build/address.js +11 -1
  34. package/build/address.js.map +1 -1
  35. package/build/index.d.ts +1 -1
  36. package/build/index.d.ts.map +1 -1
  37. package/build/index.js.map +1 -1
  38. package/build/io/base58check.d.ts +1 -25
  39. package/build/io/base58check.d.ts.map +1 -1
  40. package/build/io/base58check.js +1 -31
  41. package/build/io/base58check.js.map +1 -1
  42. package/build/io/base64.d.ts.map +1 -1
  43. package/build/io/base64.js +3 -0
  44. package/build/io/base64.js.map +1 -1
  45. package/build/networks.d.ts +1 -0
  46. package/build/networks.d.ts.map +1 -1
  47. package/build/networks.js +12 -0
  48. package/build/networks.js.map +1 -1
  49. package/build/payments/bip341.d.ts +17 -0
  50. package/build/payments/bip341.d.ts.map +1 -1
  51. package/build/payments/bip341.js +32 -1
  52. package/build/payments/bip341.js.map +1 -1
  53. package/build/payments/index.d.ts +3 -2
  54. package/build/payments/index.d.ts.map +1 -1
  55. package/build/payments/index.js +2 -1
  56. package/build/payments/index.js.map +1 -1
  57. package/build/payments/p2mr.d.ts +178 -0
  58. package/build/payments/p2mr.d.ts.map +1 -0
  59. package/build/payments/p2mr.js +555 -0
  60. package/build/payments/p2mr.js.map +1 -0
  61. package/build/payments/types.d.ts +11 -1
  62. package/build/payments/types.d.ts.map +1 -1
  63. package/build/payments/types.js +1 -0
  64. package/build/payments/types.js.map +1 -1
  65. package/build/psbt/bip371.d.ts +30 -0
  66. package/build/psbt/bip371.d.ts.map +1 -1
  67. package/build/psbt/bip371.js +80 -15
  68. package/build/psbt/bip371.js.map +1 -1
  69. package/build/psbt/psbtutils.d.ts +1 -0
  70. package/build/psbt/psbtutils.d.ts.map +1 -1
  71. package/build/psbt/psbtutils.js +2 -0
  72. package/build/psbt/psbtutils.js.map +1 -1
  73. package/build/psbt.d.ts.map +1 -1
  74. package/build/psbt.js +3 -2
  75. package/build/psbt.js.map +1 -1
  76. package/build/pubkey.js +1 -1
  77. package/build/pubkey.js.map +1 -1
  78. package/build/tsconfig.build.tsbuildinfo +1 -1
  79. package/documentation/README.md +122 -0
  80. package/documentation/address.md +820 -0
  81. package/documentation/block.md +679 -0
  82. package/documentation/crypto.md +461 -0
  83. package/documentation/ecc.md +584 -0
  84. package/documentation/errors.md +656 -0
  85. package/documentation/io.md +942 -0
  86. package/documentation/networks.md +625 -0
  87. package/documentation/p2mr.md +380 -0
  88. package/documentation/payments.md +1485 -0
  89. package/documentation/psbt.md +1400 -0
  90. package/documentation/script.md +730 -0
  91. package/documentation/taproot.md +670 -0
  92. package/documentation/transaction.md +943 -0
  93. package/documentation/types.md +587 -0
  94. package/documentation/workers.md +1007 -0
  95. package/eslint.config.js +3 -0
  96. package/package.json +17 -14
  97. package/src/address.ts +22 -10
  98. package/src/index.ts +1 -0
  99. package/src/io/base58check.ts +1 -35
  100. package/src/io/base64.ts +5 -0
  101. package/src/networks.ts +13 -0
  102. package/src/payments/bip341.ts +36 -1
  103. package/src/payments/index.ts +4 -0
  104. package/src/payments/p2mr.ts +660 -0
  105. package/src/payments/types.ts +12 -0
  106. package/src/psbt/bip371.ts +84 -13
  107. package/src/psbt/psbtutils.ts +2 -0
  108. package/src/psbt.ts +4 -2
  109. package/src/pubkey.ts +1 -1
  110. package/test/bitcoin.core.spec.ts +1 -1
  111. package/test/fixtures/p2mr.json +270 -0
  112. package/test/integration/taproot.spec.ts +7 -3
  113. package/test/opnetTestnet.spec.ts +302 -0
  114. package/test/payments.spec.ts +3 -1
  115. package/test/psbt.spec.ts +297 -2
  116. package/test/tsconfig.json +2 -2
@@ -0,0 +1,670 @@
1
+ # BIP 341 Taproot Utilities
2
+
3
+ The `bip341.ts` module provides low-level primitives for constructing and verifying Taproot (BIP 341) Merkle script trees. These utilities are shared by both P2TR (SegWit v1) and P2MR (SegWit v2) payment types. The module handles tree hashing, key tweaking, Merkle proof generation, and root reconstruction from control blocks.
4
+
5
+ ## Overview
6
+
7
+ | Property | Value |
8
+ |----------|-------|
9
+ | BIP | 341 |
10
+ | Module | `src/payments/bip341.ts` |
11
+ | Leaf version constant | `LEAF_VERSION_TAPSCRIPT` = `0xc0` |
12
+ | Max tree depth | `MAX_TAPTREE_DEPTH` = `128` |
13
+ | Tagged hash prefixes | `TapLeaf`, `TapBranch`, `TapTweak` |
14
+ | Used by | P2TR (`p2tr.ts`), P2MR (`p2mr.ts`) |
15
+
16
+ ---
17
+
18
+ ## Constants
19
+
20
+ ```typescript
21
+ import {
22
+ LEAF_VERSION_TAPSCRIPT,
23
+ MAX_TAPTREE_DEPTH,
24
+ } from '@btc-vision/bitcoin';
25
+ ```
26
+
27
+ | Constant | Value | Description |
28
+ |----------|-------|-------------|
29
+ | `LEAF_VERSION_TAPSCRIPT` | `0xc0` (192) | Default leaf version for Tapscript leaves. Encoded in the control byte as `leaf_version \| parity_bit`. Must satisfy `(version & 0xfe) === version` (even, lowest bit reserved for parity). |
30
+ | `MAX_TAPTREE_DEPTH` | `128` | Maximum depth of a Taproot Merkle tree. The Merkle proof path length `m` must satisfy `0 <= m <= 128`. |
31
+
32
+ ---
33
+
34
+ ## Type Definitions
35
+
36
+ ### Tapleaf
37
+
38
+ A single script leaf in the tree. Defined in `src/types.ts`.
39
+
40
+ ```typescript
41
+ interface Tapleaf {
42
+ readonly output: Uint8Array; // compiled script bytes
43
+ readonly version?: number; // leaf version (defaults to LEAF_VERSION_TAPSCRIPT)
44
+ }
45
+ ```
46
+
47
+ | Field | Type | Required | Description |
48
+ |-------|------|----------|-------------|
49
+ | `output` | `Uint8Array` | Yes | The compiled Bitcoin script for this leaf. |
50
+ | `version` | `number` | No | Leaf version. Defaults to `0xc0`. Must satisfy `(version & 0xfe) === version`. |
51
+
52
+ ### Taptree
53
+
54
+ A recursive binary tree of script leaves. Defined in `src/types.ts`.
55
+
56
+ ```typescript
57
+ type Taptree = [Taptree | Tapleaf, Taptree | Tapleaf] | Tapleaf;
58
+ ```
59
+
60
+ A `Taptree` is either:
61
+ - A single `Tapleaf` (a leaf node), or
62
+ - A two-element tuple `[left, right]` where each element is itself a `Taptree` or `Tapleaf` (a branch node).
63
+
64
+ ### HashTree
65
+
66
+ The hashed representation of a `Taptree`. Defined in `src/payments/bip341.ts`.
67
+
68
+ > **Note:** `HashLeaf`, `HashBranch`, and `HashTree` are **not exported from the main `@btc-vision/bitcoin` entry point**. The `HashTree` type is re-exported from `@btc-vision/bitcoin` via the payments barrel export, but `HashLeaf` and `HashBranch` are internal types. In practice, you interact with `HashTree` values returned by `toHashTree()` and passed to `findScriptPath()`.
69
+
70
+ ```typescript
71
+ interface HashLeaf {
72
+ hash: Bytes32;
73
+ }
74
+
75
+ interface HashBranch {
76
+ hash: Bytes32;
77
+ left: HashTree;
78
+ right: HashTree;
79
+ }
80
+
81
+ type HashTree = HashLeaf | HashBranch;
82
+ ```
83
+
84
+ A `HashTree` is produced by `toHashTree()` and consumed by `findScriptPath()`. Each node contains a 32-byte hash. Branch nodes additionally store references to their left and right children, which enables recursive path-finding for Merkle proof construction.
85
+
86
+ ### TweakedPublicKey
87
+
88
+ The result of tweaking an internal public key with a Merkle root. This interface is **not exported from the main `@btc-vision/bitcoin` entry point**. It is defined internally in `src/payments/bip341.ts` and used as the return type of `tweakKey()`.
89
+
90
+ ```typescript
91
+ interface TweakedPublicKey {
92
+ parity: number; // 0 or 1
93
+ x: XOnlyPublicKey; // 32-byte x-only tweaked pubkey
94
+ }
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Taptree Structure
100
+
101
+ A Taptree is a binary tree where scripts live at the leaves. The tree is hashed bottom-up: each leaf is tagged-hashed individually, and each pair of siblings is combined via a branch hash. The root hash is used to tweak the internal public key (P2TR) or serves directly as the witness program (P2MR).
102
+
103
+ ```
104
+ Root Hash
105
+ / \
106
+ Branch AB Branch CD
107
+ / \ / \
108
+ Leaf A Leaf B Leaf C Leaf D
109
+ ```
110
+
111
+ Each leaf hash is computed as:
112
+
113
+ ```
114
+ tapleafHash = TaggedHash("TapLeaf", [version_byte] || [varint(script.length)] || script)
115
+ ```
116
+
117
+ Each branch hash is computed by sorting the two children lexicographically, then:
118
+
119
+ ```
120
+ tapBranchHash = TaggedHash("TapBranch", sorted_left || sorted_right)
121
+ ```
122
+
123
+ The lexicographic sorting ensures the tree is canonical -- swapping left and right children produces the same root hash. This means the tree structure matters only for proof length, not for commitment identity.
124
+
125
+ ### Depth and Proof Cost
126
+
127
+ The depth of a leaf in the tree determines the length of its Merkle proof. A leaf at depth `d` requires `d` sibling hashes (each 32 bytes) in the control block. Frequently-used scripts should be placed closer to the root to minimize witness size.
128
+
129
+ ```
130
+ Depth 0: 1 leaf -> 0 proof hashes -> 0 bytes of Merkle path
131
+ Depth 1: 2 leaves -> 1 proof hash -> 32 bytes of Merkle path
132
+ Depth 2: 4 leaves -> 2 proof hashes -> 64 bytes of Merkle path
133
+ Depth 3: 8 leaves -> 3 proof hashes -> 96 bytes of Merkle path
134
+ ```
135
+
136
+ ---
137
+
138
+ ## Functions
139
+
140
+ ### toHashTree()
141
+
142
+ Builds a `HashTree` from a `Taptree` by recursively hashing leaves and branches.
143
+
144
+ ```typescript
145
+ function toHashTree(scriptTree: Taptree): HashTree;
146
+ ```
147
+
148
+ | Parameter | Type | Description |
149
+ |-----------|------|-------------|
150
+ | `scriptTree` | `Taptree` | The binary tree of script leaves. |
151
+ | **Returns** | `HashTree` | The hashed tree with `hash`, `left`, and `right` at each branch. |
152
+
153
+ **Algorithm:**
154
+
155
+ 1. If the node is a `Tapleaf`, compute its `tapleafHash` and return a `HashLeaf`.
156
+ 2. If the node is a branch `[left, right]`, recursively compute `toHashTree(left)` and `toHashTree(right)`.
157
+ 3. Sort the two child hashes lexicographically (byte comparison).
158
+ 4. Compute `tapBranchHash(sortedLeft.hash, sortedRight.hash)`.
159
+ 5. Return a `HashBranch` with the combined hash and ordered children.
160
+
161
+ ```typescript
162
+ import { toHashTree, tapleafHash } from '@btc-vision/bitcoin';
163
+ import * as script from '@btc-vision/bitcoin/script';
164
+
165
+ const scriptTree = [
166
+ { output: script.compile([script.opcodes.OP_1]) },
167
+ { output: script.compile([script.opcodes.OP_2]) },
168
+ ];
169
+
170
+ const hashTree = toHashTree(scriptTree);
171
+ console.log(hashTree.hash); // 32-byte Merkle root
172
+ ```
173
+
174
+ ---
175
+
176
+ ### tapleafHash()
177
+
178
+ Computes the tagged hash of a single script leaf.
179
+
180
+ ```typescript
181
+ function tapleafHash(leaf: Tapleaf): Bytes32;
182
+ ```
183
+
184
+ | Parameter | Type | Description |
185
+ |-----------|------|-------------|
186
+ | `leaf` | `Tapleaf` | Object with `output` (script bytes) and optional `version`. |
187
+ | **Returns** | `Bytes32` | 32-byte `TapLeaf` tagged hash. |
188
+
189
+ **Computation:**
190
+
191
+ ```
192
+ TaggedHash("TapLeaf", [version] || [compact_size(script.length)] || script)
193
+ ```
194
+
195
+ Where `version` defaults to `LEAF_VERSION_TAPSCRIPT` (`0xc0`) if not specified on the leaf.
196
+
197
+ ```typescript
198
+ import { tapleafHash, LEAF_VERSION_TAPSCRIPT } from '@btc-vision/bitcoin';
199
+
200
+ const leaf = {
201
+ output: redeemScript,
202
+ version: LEAF_VERSION_TAPSCRIPT,
203
+ };
204
+
205
+ const hash = tapleafHash(leaf);
206
+ // hash is a 32-byte Bytes32
207
+ ```
208
+
209
+ ---
210
+
211
+ ### tapBranchHash()
212
+
213
+ Computes the tagged hash of two child hashes (a branch node).
214
+
215
+ ```typescript
216
+ function tapBranchHash(a: Uint8Array, b: Uint8Array): Bytes32;
217
+ ```
218
+
219
+ | Parameter | Type | Description |
220
+ |-----------|------|-------------|
221
+ | `a` | `Uint8Array` | First child hash (left branch). |
222
+ | `b` | `Uint8Array` | Second child hash (right branch). |
223
+ | **Returns** | `Bytes32` | 32-byte `TapBranch` tagged hash. |
224
+
225
+ **Computation:**
226
+
227
+ ```
228
+ TaggedHash("TapBranch", a || b)
229
+ ```
230
+
231
+ Note: The caller is responsible for passing `a` and `b` in the correct (sorted) order. The `toHashTree()` function handles sorting automatically, but if you call `tapBranchHash` directly, you must sort the inputs lexicographically yourself.
232
+
233
+ ```typescript
234
+ import { tapBranchHash, tapleafHash } from '@btc-vision/bitcoin';
235
+
236
+ const hashA = tapleafHash({ output: scriptA });
237
+ const hashB = tapleafHash({ output: scriptB });
238
+
239
+ // Sort before hashing (compare returns < 0 if hashA < hashB)
240
+ const branch = (compare(hashA, hashB) < 0)
241
+ ? tapBranchHash(hashA, hashB)
242
+ : tapBranchHash(hashB, hashA);
243
+ ```
244
+
245
+ ---
246
+
247
+ ### tapTweakHash()
248
+
249
+ Computes the tweak hash used to derive the output public key from an internal key.
250
+
251
+ ```typescript
252
+ function tapTweakHash(pubKey: XOnlyPublicKey, h: Bytes32 | undefined): Bytes32;
253
+ ```
254
+
255
+ | Parameter | Type | Description |
256
+ |-----------|------|-------------|
257
+ | `pubKey` | `XOnlyPublicKey` | 32-byte x-only internal public key. |
258
+ | `h` | `Bytes32 \| undefined` | Merkle root hash, or `undefined` for key-path-only (no script tree). |
259
+ | **Returns** | `Bytes32` | 32-byte `TapTweak` tagged hash. |
260
+
261
+ **Computation:**
262
+
263
+ ```
264
+ If h is defined: TaggedHash("TapTweak", pubKey || h)
265
+ If h is undefined: TaggedHash("TapTweak", pubKey)
266
+ ```
267
+
268
+ When no script tree is present, the tweak commits only to the internal public key. This is the key-path-only case for P2TR.
269
+
270
+ ---
271
+
272
+ ### tweakKey()
273
+
274
+ Tweaks an internal x-only public key with an optional Merkle root to produce the output public key.
275
+
276
+ ```typescript
277
+ function tweakKey(
278
+ pubKey: XOnlyPublicKey,
279
+ h: Bytes32 | undefined,
280
+ ): TweakedPublicKey | null;
281
+ ```
282
+
283
+ | Parameter | Type | Description |
284
+ |-----------|------|-------------|
285
+ | `pubKey` | `XOnlyPublicKey` | 32-byte x-only internal public key. |
286
+ | `h` | `Bytes32 \| undefined` | Merkle root hash, or `undefined` for key-path-only. |
287
+ | **Returns** | `TweakedPublicKey \| null` | The tweaked public key and parity, or `null` on failure. |
288
+
289
+ **Algorithm:**
290
+
291
+ 1. Validate that `pubKey` is a 32-byte `Uint8Array`.
292
+ 2. If `h` is provided, validate it is 32 bytes.
293
+ 3. Compute `tweakHash = tapTweakHash(pubKey, h)`.
294
+ 4. Call the ECC library's `xOnlyPointAddTweak(pubKey, tweakHash)`.
295
+ 5. Return `{ parity, x }` where `x` is the new x-only output key.
296
+
297
+ The parity bit is encoded in the control block's first byte (lowest bit) so the verifier can reconstruct the full point.
298
+
299
+ ```typescript
300
+ import { tweakKey, toHashTree } from '@btc-vision/bitcoin';
301
+ import type { XOnlyPublicKey } from '@btc-vision/bitcoin';
302
+
303
+ const hashTree = toHashTree(scriptTree);
304
+ const tweaked = tweakKey(internalPubkey, hashTree.hash);
305
+
306
+ if (tweaked) {
307
+ console.log(tweaked.x); // 32-byte output pubkey
308
+ console.log(tweaked.parity); // 0 or 1
309
+ }
310
+ ```
311
+
312
+ ---
313
+
314
+ ### rootHashFromPath()
315
+
316
+ Reconstructs the Merkle root from a **P2TR** control block and a leaf hash by walking up the Merkle proof path.
317
+
318
+ ```typescript
319
+ function rootHashFromPath(
320
+ controlBlock: Uint8Array,
321
+ leafHash: Uint8Array,
322
+ ): Bytes32;
323
+ ```
324
+
325
+ | Parameter | Type | Description |
326
+ |-----------|------|-------------|
327
+ | `controlBlock` | `Uint8Array` | The P2TR control block (minimum 33 bytes). |
328
+ | `leafHash` | `Uint8Array` | The leaf hash being verified. |
329
+ | **Returns** | `Bytes32` | The reconstructed 32-byte Merkle root. |
330
+ | **Throws** | `TypeError` | If `controlBlock.length < 33`. |
331
+
332
+ **P2TR control block layout:**
333
+
334
+ ```
335
+ [control_byte (1)] [internal_pubkey (32)] [merkle_path (32 * m)]
336
+ ```
337
+
338
+ The Merkle path starts at byte offset 33. For each 32-byte sibling hash `ej` in the path, the function computes:
339
+
340
+ ```
341
+ if kj < ej: kj = tapBranchHash(kj, ej)
342
+ else: kj = tapBranchHash(ej, kj)
343
+ ```
344
+
345
+ Where `kj` starts as `leafHash` and ends as the Merkle root after processing all `m` sibling hashes.
346
+
347
+ ```typescript
348
+ import { rootHashFromPath, tapleafHash } from '@btc-vision/bitcoin';
349
+
350
+ const leafHash = tapleafHash({ output: redeemScript });
351
+ const merkleRoot = rootHashFromPath(controlBlock, leafHash);
352
+ ```
353
+
354
+ ---
355
+
356
+ ### rootHashFromPathP2MR()
357
+
358
+ Reconstructs the Merkle root from a **P2MR** control block and a leaf hash. P2MR control blocks have no internal public key, so the Merkle path starts at byte offset 1 instead of 33.
359
+
360
+ ```typescript
361
+ function rootHashFromPathP2MR(
362
+ controlBlock: Uint8Array,
363
+ leafHash: Uint8Array,
364
+ ): Bytes32;
365
+ ```
366
+
367
+ | Parameter | Type | Description |
368
+ |-----------|------|-------------|
369
+ | `controlBlock` | `Uint8Array` | The P2MR control block (minimum 1 byte). |
370
+ | `leafHash` | `Uint8Array` | The leaf hash being verified. |
371
+ | **Returns** | `Bytes32` | The reconstructed 32-byte Merkle root. |
372
+ | **Throws** | `TypeError` | If `controlBlock.length < 1`. |
373
+
374
+ **P2MR control block layout:**
375
+
376
+ ```
377
+ [control_byte (1)] [merkle_path (32 * m)]
378
+ ```
379
+
380
+ The algorithm is identical to `rootHashFromPath`, except the path extraction starts at offset 1 instead of 33, and `m = (controlBlock.length - 1) / 32`.
381
+
382
+ ```typescript
383
+ import { rootHashFromPathP2MR, tapleafHash } from '@btc-vision/bitcoin';
384
+
385
+ const leafHash = tapleafHash({ output: redeemScript });
386
+ const merkleRoot = rootHashFromPathP2MR(controlBlock, leafHash);
387
+ ```
388
+
389
+ ---
390
+
391
+ ### findScriptPath()
392
+
393
+ Finds the Merkle proof path for a specific leaf hash within a `HashTree`. Returns the array of sibling hashes needed to prove inclusion of the leaf in the tree.
394
+
395
+ ```typescript
396
+ function findScriptPath(
397
+ node: HashTree,
398
+ hash: Bytes32,
399
+ ): Bytes32[] | undefined;
400
+ ```
401
+
402
+ | Parameter | Type | Description |
403
+ |-----------|------|-------------|
404
+ | `node` | `HashTree` | The root of the hash tree (from `toHashTree()`). |
405
+ | `hash` | `Bytes32` | The leaf hash to search for. |
406
+ | **Returns** | `Bytes32[] \| undefined` | Array of sibling hashes from leaf to root, or `undefined` if not found. |
407
+
408
+ **Algorithm:**
409
+
410
+ 1. If `node` is a branch, recursively search the left subtree.
411
+ 2. If found on the left, append the right sibling's hash and return.
412
+ 3. Otherwise, recursively search the right subtree.
413
+ 4. If found on the right, append the left sibling's hash and return.
414
+ 5. If `node` is a leaf and its hash matches, return an empty array `[]`.
415
+ 6. If no match is found anywhere, return `undefined`.
416
+
417
+ The returned array of sibling hashes is ordered from leaf level (index 0) to just below the root (last index). This is the Merkle path that goes into the control block.
418
+
419
+ ```typescript
420
+ import { toHashTree, findScriptPath, tapleafHash } from '@btc-vision/bitcoin';
421
+
422
+ const hashTree = toHashTree(scriptTree);
423
+ const leafHash = tapleafHash({ output: targetScript });
424
+
425
+ const path = findScriptPath(hashTree, leafHash);
426
+ if (path) {
427
+ console.log(`Proof length: ${path.length} hashes (${path.length * 32} bytes)`);
428
+ // path[0] = sibling at leaf level
429
+ // path[path.length - 1] = sibling just below root
430
+ }
431
+ ```
432
+
433
+ ---
434
+
435
+ ## Differences Between P2TR and P2MR Control Block Handling
436
+
437
+ Both P2TR and P2MR use the same Merkle tree construction (`toHashTree`, `tapleafHash`, `tapBranchHash`, `findScriptPath`). The difference lies in how the control block is structured and how the root hash is used.
438
+
439
+ | Aspect | P2TR (BIP 341) | P2MR (BIP 360) |
440
+ |--------|----------------|----------------|
441
+ | SegWit version | 1 (`OP_1`) | 2 (`OP_2`) |
442
+ | Control block minimum size | 33 bytes | 1 byte |
443
+ | Control block layout | `[ctrl_byte] [internal_pubkey (32)] [path...]` | `[ctrl_byte] [path...]` |
444
+ | Total control block size | `33 + 32*m` bytes | `1 + 32*m` bytes |
445
+ | Internal pubkey in control block | Yes (bytes 1-32) | No |
446
+ | Root hash function | `rootHashFromPath()` | `rootHashFromPathP2MR()` |
447
+ | Merkle path offset | Byte 33 | Byte 1 |
448
+ | Key-path spend | Yes (single Schnorr signature) | No |
449
+ | Output commitment | `tweakKey(internalPubkey, merkleRoot).x` | `merkleRoot` directly |
450
+ | Control byte parity bit | 0 or 1 (from tweaked key parity) | Always 1 |
451
+ | Address prefix | `bc1p` | `bc1z` |
452
+
453
+ ### Why the Difference?
454
+
455
+ P2TR embeds the internal public key in the control block so the verifier can:
456
+ 1. Extract the internal pubkey from the control block.
457
+ 2. Reconstruct the Merkle root from the leaf hash and proof path.
458
+ 3. Tweak the internal pubkey with the root to derive the expected output pubkey.
459
+ 4. Compare against the output pubkey in the scriptPubKey.
460
+
461
+ P2MR eliminates the internal pubkey entirely. The output IS the Merkle root, so the verifier only needs to:
462
+ 1. Reconstruct the Merkle root from the leaf hash and proof path.
463
+ 2. Compare directly against the witness program in the scriptPubKey.
464
+
465
+ This removal of the key-path spend eliminates the quantum-vulnerable elliptic curve operation from the spending path.
466
+
467
+ ---
468
+
469
+ ## Step-by-Step Example: Building a Script Tree
470
+
471
+ This example walks through building a 3-leaf script tree, computing all hashes, constructing the control block, and spending via the script path.
472
+
473
+ ### 1. Define the Script Tree
474
+
475
+ ```typescript
476
+ import {
477
+ toHashTree,
478
+ findScriptPath,
479
+ tapleafHash,
480
+ tapBranchHash,
481
+ tweakKey,
482
+ LEAF_VERSION_TAPSCRIPT,
483
+ } from '@btc-vision/bitcoin';
484
+ import * as script from '@btc-vision/bitcoin/script';
485
+ import type { Taptree, XOnlyPublicKey } from '@btc-vision/bitcoin';
486
+ import { concat } from '@btc-vision/bitcoin';
487
+
488
+ // Three scripts: a checksig, a timelock, and a hash preimage check
489
+ const scriptA = script.fromASM('<pubkeyA> OP_CHECKSIG');
490
+ const scriptB = script.fromASM('OP_10 OP_CHECKSEQUENCEVERIFY OP_DROP <pubkeyB> OP_CHECKSIG');
491
+ const scriptC = script.fromASM('OP_SHA256 <hash> OP_EQUAL');
492
+
493
+ // Arrange as a binary tree:
494
+ // root
495
+ // / \
496
+ // branch leafC
497
+ // / \
498
+ // leafA leafB
499
+
500
+ const scriptTree: Taptree = [
501
+ [
502
+ { output: scriptA },
503
+ { output: scriptB },
504
+ ],
505
+ { output: scriptC },
506
+ ];
507
+ ```
508
+
509
+ ### 2. Build the Hash Tree
510
+
511
+ ```typescript
512
+ const hashTree = toHashTree(scriptTree);
513
+
514
+ // Individual leaf hashes
515
+ const hashA = tapleafHash({ output: scriptA });
516
+ const hashB = tapleafHash({ output: scriptB });
517
+ const hashC = tapleafHash({ output: scriptC });
518
+
519
+ // hashTree.hash is the Merkle root
520
+ console.log(hashTree.hash); // 32-byte root hash
521
+ ```
522
+
523
+ The tree structure after hashing (children sorted lexicographically):
524
+
525
+ ```
526
+ hashTree.hash
527
+ / \
528
+ tapBranchHash(hashA, hashB) hashC
529
+ / \
530
+ hashA hashB
531
+ ```
532
+
533
+ ### 3. Find a Merkle Proof Path
534
+
535
+ ```typescript
536
+ // Find the proof path for scriptB
537
+ const leafHashB = tapleafHash({ output: scriptB });
538
+ const pathB = findScriptPath(hashTree, leafHashB);
539
+
540
+ if (pathB) {
541
+ // pathB = [hashA, hashC]
542
+ // pathB[0] = sibling of leafB at depth 2 (which is hashA)
543
+ // pathB[1] = sibling of the branch at depth 1 (which is hashC)
544
+ console.log(`Path length: ${pathB.length}`); // 2
545
+ }
546
+ ```
547
+
548
+ ### 4. Construct the P2TR Control Block
549
+
550
+ ```typescript
551
+ // Tweak the internal pubkey with the Merkle root
552
+ const internalPubkey: XOnlyPublicKey = /* 32-byte x-only key */;
553
+ const tweaked = tweakKey(internalPubkey, hashTree.hash);
554
+
555
+ if (tweaked && pathB) {
556
+ const controlByte = LEAF_VERSION_TAPSCRIPT | tweaked.parity; // 0xc0 | parity
557
+ const controlBlock = concat([
558
+ new Uint8Array([controlByte]),
559
+ internalPubkey, // 32 bytes
560
+ ...pathB, // 32 bytes per sibling
561
+ ]);
562
+ // controlBlock length = 1 + 32 + (2 * 32) = 97 bytes
563
+
564
+ // The witness for spending via scriptB:
565
+ const witness = [
566
+ /* scriptB inputs (e.g., signature) */,
567
+ scriptB, // the script being executed
568
+ controlBlock, // Merkle proof + internal pubkey
569
+ ];
570
+ }
571
+ ```
572
+
573
+ ### 5. Construct the P2MR Control Block
574
+
575
+ For P2MR, there is no internal pubkey in the control block, and the Merkle root is the witness program directly.
576
+
577
+ ```typescript
578
+ if (pathB) {
579
+ const controlByte = LEAF_VERSION_TAPSCRIPT | 0x01; // parity always 1 for P2MR
580
+ const controlBlock = concat([
581
+ new Uint8Array([controlByte]),
582
+ ...pathB, // 32 bytes per sibling (no internal pubkey)
583
+ ]);
584
+ // controlBlock length = 1 + (2 * 32) = 65 bytes
585
+
586
+ // The witness for spending via scriptB:
587
+ const witness = [
588
+ /* scriptB inputs (e.g., signature) */,
589
+ scriptB, // the script being executed
590
+ controlBlock, // Merkle proof only
591
+ ];
592
+ }
593
+ ```
594
+
595
+ ### 6. Verify: Reconstruct the Root from the Control Block
596
+
597
+ ```typescript
598
+ import { rootHashFromPath, rootHashFromPathP2MR } from '@btc-vision/bitcoin';
599
+
600
+ // P2TR verification
601
+ const reconstructedRootP2TR = rootHashFromPath(p2trControlBlock, leafHashB);
602
+ // Then: tweakKey(extractedInternalPubkey, reconstructedRootP2TR) must equal the output pubkey
603
+
604
+ // P2MR verification
605
+ const reconstructedRootP2MR = rootHashFromPathP2MR(p2mrControlBlock, leafHashB);
606
+ // Then: reconstructedRootP2MR must equal the witness program directly
607
+ ```
608
+
609
+ ---
610
+
611
+ ## Tagged Hash Reference
612
+
613
+ All hashing in BIP 341 uses tagged hashes as defined in BIP 340. A tagged hash is:
614
+
615
+ ```
616
+ TaggedHash(tag, msg) = SHA256(SHA256(tag) || SHA256(tag) || msg)
617
+ ```
618
+
619
+ The `SHA256(tag) || SHA256(tag)` prefix is precomputed for each known tag.
620
+
621
+ | Tag | Used By | Input |
622
+ |-----|---------|-------|
623
+ | `TapLeaf` | `tapleafHash()` | `[version_byte] \|\| [compact_size(script.length)] \|\| script` |
624
+ | `TapBranch` | `tapBranchHash()` | `left_hash \|\| right_hash` (sorted) |
625
+ | `TapTweak` | `tapTweakHash()` | `internal_pubkey \|\| merkle_root` (or just `internal_pubkey`) |
626
+
627
+ ---
628
+
629
+ ## Type Guard Functions
630
+
631
+ The `types.ts` module provides type guards for runtime validation of tree structures.
632
+
633
+ > **Note:** `isTapleaf` and `isTaptree` are **not re-exported from the main `@btc-vision/bitcoin` entry point**. Import them from the subpath:
634
+ >
635
+ > ```typescript
636
+ > import { isTapleaf, isTaptree } from '@btc-vision/bitcoin/types';
637
+ > ```
638
+
639
+ ### isTapleaf()
640
+
641
+ ```typescript
642
+ function isTapleaf(value: unknown): value is Tapleaf;
643
+ ```
644
+
645
+ Returns `true` if `value` is an object with an `output` property that is a `Uint8Array`, and an optional `version` that satisfies `(version & 0xfe) === version`.
646
+
647
+ ### isTaptree()
648
+
649
+ ```typescript
650
+ function isTaptree(value: unknown): value is Taptree;
651
+ ```
652
+
653
+ Returns `true` if `value` is either a valid `Tapleaf` or a two-element array where both elements are valid `Taptree` nodes. This check is recursive.
654
+
655
+ ---
656
+
657
+ ## API Summary
658
+
659
+ | Function | Input | Output | Description |
660
+ |----------|-------|--------|-------------|
661
+ | `toHashTree(scriptTree)` | `Taptree` | `HashTree` | Builds the full hashed Merkle tree. |
662
+ | `tapleafHash(leaf)` | `Tapleaf` | `Bytes32` | Computes a single leaf hash. |
663
+ | `tapBranchHash(a, b)` | Two `Uint8Array` | `Bytes32` | Computes a branch hash from two children. |
664
+ | `tapTweakHash(pubKey, h)` | `XOnlyPublicKey`, `Bytes32?` | `Bytes32` | Computes the tweak scalar. |
665
+ | `tweakKey(pubKey, h)` | `XOnlyPublicKey`, `Bytes32?` | `TweakedPublicKey \| null` | Tweaks the internal key to get the output key. |
666
+ | `rootHashFromPath(cb, leaf)` | P2TR control block, leaf hash | `Bytes32` | Reconstructs root from P2TR control block. |
667
+ | `rootHashFromPathP2MR(cb, leaf)` | P2MR control block, leaf hash | `Bytes32` | Reconstructs root from P2MR control block. |
668
+ | `findScriptPath(node, hash)` | `HashTree`, `Bytes32` | `Bytes32[] \| undefined` | Finds the Merkle proof path for a leaf. |
669
+ | `isTapleaf(value)` | `unknown` | `boolean` | Type guard for `Tapleaf`. |
670
+ | `isTaptree(value)` | `unknown` | `boolean` | Type guard for `Taptree`. |