@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,587 @@
1
+ # Types - Branded Types, Type Guards, and Assertions
2
+
3
+ The type system in `@btc-vision/bitcoin` uses TypeScript branded types to provide compile-time safety for Bitcoin primitives that are structurally identical at runtime. A 32-byte `PrivateKey` and a 32-byte `MessageHash` are both `Uint8Array`, but the branding mechanism prevents one from being passed where the other is expected. Runtime type guards and assertion helpers complement these types by performing validation at boundaries where data enters the system.
4
+
5
+ ## Overview
6
+
7
+ | Category | Purpose |
8
+ |----------|---------|
9
+ | Branded types | Nominal typing over `Uint8Array` and `bigint` to prevent accidental misuse of structurally identical values |
10
+ | Type guards | Runtime `is*()` functions returning `value is T` for safe narrowing |
11
+ | Assertion helpers | `assert*()` functions throwing `TypeError` / `RangeError` with descriptive messages |
12
+ | Conversion functions | `to*()` functions that validate and cast plain values into branded types |
13
+ | Stack types | Types representing Bitcoin script execution stack elements |
14
+ | Taptree types | Recursive types representing Taproot/P2MR script trees |
15
+ | Constants | Protocol-level numeric limits |
16
+ | Utilities | Helper functions for comparing stack arrays |
17
+
18
+ ---
19
+
20
+ ## Branded Types
21
+
22
+ Branded types use the `Brand<T, B>` utility to create nominal types from structural base types. The brand is a phantom property that exists only at the type level and has zero runtime cost.
23
+
24
+ ```typescript
25
+ // The underlying branding mechanism (from @btc-vision/ecpair)
26
+ declare const __brand: unique symbol;
27
+ type Brand<T, B extends string> = T & {
28
+ readonly [__brand]: B;
29
+ };
30
+ ```
31
+
32
+ Because `__brand` is a `unique symbol`, TypeScript treats each branded type as distinct even though they share the same runtime representation. You cannot assign a `PrivateKey` to a `MessageHash` parameter without an explicit cast, despite both being 32-byte `Uint8Array` values at runtime.
33
+
34
+ ### Branded Type Reference
35
+
36
+ | Type | Base | Size | Description |
37
+ |------|------|------|-------------|
38
+ | `Bytes32` | `Uint8Array` | 32 bytes | Generic 32-byte buffer. Used for transaction hashes, Merkle roots, and other 256-bit values. |
39
+ | `Bytes20` | `Uint8Array` | 20 bytes | Generic 20-byte buffer. Used for HASH160 results (RIPEMD160(SHA256(x))), P2PKH/P2SH hashes. |
40
+ | `PublicKey` | `Uint8Array` | 33 or 65 bytes | SEC1-encoded secp256k1 public key. 33 bytes compressed (prefix `0x02`/`0x03`) or 65 bytes uncompressed (prefix `0x04`/`0x06`/`0x07`). |
41
+ | `XOnlyPublicKey` | `Uint8Array` | 32 bytes | BIP 340 x-only public key for Taproot and Schnorr signatures. Must be non-zero and less than the secp256k1 field prime `p`. |
42
+ | `Satoshi` | `bigint` | N/A | Bitcoin amount in satoshis. Must be `>= 0n` and `<= 2_100_000_000_000_000n` (21 million BTC). |
43
+ | `PrivateKey` | `Uint8Array` | 32 bytes | secp256k1 private key scalar. Must be non-zero and less than the curve order `n`. |
44
+ | `Signature` | `Uint8Array` | 8-73 bytes | DER-encoded ECDSA signature. |
45
+ | `SchnorrSignature` | `Uint8Array` | 64 bytes | BIP 340 Schnorr signature. |
46
+ | `MessageHash` | `Uint8Array` | 32 bytes | 32-byte hash of the message being signed. Semantically distinct from `PrivateKey` and `Bytes32`. **Not exported from the main `@btc-vision/bitcoin` entry point.** Import from `@btc-vision/bitcoin/types` instead. |
47
+ | `Script` | `Uint8Array` | Variable | Bitcoin script bytecode. |
48
+
49
+ ### Import
50
+
51
+ Most branded types are re-exported from the main entry point:
52
+
53
+ ```typescript
54
+ import type {
55
+ Bytes32,
56
+ Bytes20,
57
+ PublicKey,
58
+ XOnlyPublicKey,
59
+ Satoshi,
60
+ PrivateKey,
61
+ Signature,
62
+ SchnorrSignature,
63
+ Script,
64
+ } from '@btc-vision/bitcoin';
65
+ ```
66
+
67
+ > **Note:** `MessageHash` is **not** re-exported from `@btc-vision/bitcoin`. Use the `@btc-vision/bitcoin/types` subpath:
68
+ >
69
+ > ```typescript
70
+ > import type { MessageHash } from '@btc-vision/bitcoin/types';
71
+ > ```
72
+
73
+ ---
74
+
75
+ ## Type Guard Functions
76
+
77
+ Type guards are runtime `is*()` functions that return a type predicate (`value is T`). They perform the minimum checks necessary to guarantee the branded type's invariants hold.
78
+
79
+ ### Primitive Guards
80
+
81
+ > **Note:** The primitive guard functions listed below (`isUInt8`, `isUInt32`, `isNumber`, `isUint8Array`, `isUint8ArrayN`, `isArray`) are defined in `src/types.ts` but are **not re-exported from the main `@btc-vision/bitcoin` entry point**. To use them, import from the `@btc-vision/bitcoin/types` subpath.
82
+
83
+ | Function | Signature | Description |
84
+ |----------|-----------|-------------|
85
+ | `isUInt8` | `(value: unknown) => value is number` | True if `value` is an integer in `[0, 255]`. |
86
+ | `isUInt32` | `(value: unknown) => value is number` | True if `value` is an integer in `[0, 4_294_967_295]`. |
87
+ | `isNumber` | `(value: unknown) => value is number` | True if `value` is a finite number (excludes `NaN`, `Infinity`, `-Infinity`). |
88
+ | `isUint8Array` | `(value: unknown) => value is Uint8Array` | True if `value` is a `Uint8Array` instance. |
89
+ | `isUint8ArrayN` | `<N>(value: unknown, n: N) => value is Uint8Array & { readonly length: N }` | True if `value` is a `Uint8Array` of exactly `n` bytes. |
90
+ | `isArray` | `(value: unknown) => value is unknown[]` | True if `value` is an array (`Array.isArray`). |
91
+
92
+ ```typescript
93
+ // These are NOT available from '@btc-vision/bitcoin'. Use the subpath:
94
+ import { isUInt8, isUInt32, isNumber, isUint8Array, isUint8ArrayN, isArray } from '@btc-vision/bitcoin/types';
95
+ ```
96
+
97
+ ### isHex
98
+
99
+ The `isHex` function exported from `@btc-vision/bitcoin` comes from `io/hex.ts` (not from `types.ts`). It has a different signature and behavior than the internal `types.ts` version:
100
+
101
+ | Function | Signature | Description |
102
+ |----------|-----------|-------------|
103
+ | `isHex` (exported, from `io/hex.ts`) | `(value: string) => boolean` | Returns `true` if `value` is a valid hex string of even length. **Strips `0x`/`0X` prefixes** before checking. Note: the parameter type is `string` (not `unknown`), and the return type is `boolean` (not a type guard predicate). |
104
+
105
+ ```typescript
106
+ import { isHex } from '@btc-vision/bitcoin';
107
+
108
+ isHex('deadbeef'); // true
109
+ isHex('0xdeadbeef'); // true (0x prefix is stripped before validation)
110
+ isHex('DEADBEEF'); // true
111
+ isHex('deadbee'); // false (odd length after stripping)
112
+ isHex('deadbeeg'); // false (invalid character)
113
+ ```
114
+
115
+ > **Internal note:** `src/types.ts` also defines an `isHex` function with signature `(value: unknown) => value is string` that does **not** strip `0x` prefixes. This version is not re-exported from the main entry point; the `io/hex.ts` version takes precedence in `@btc-vision/bitcoin`.
116
+
117
+ ### Branded Type Guards
118
+
119
+ | Function | Signature | Checks |
120
+ |----------|-----------|--------|
121
+ | `isBytes32` | `(value: unknown) => value is Bytes32` | `Uint8Array` with `length === 32`. |
122
+ | `isBytes20` | `(value: unknown) => value is Bytes20` | `Uint8Array` with `length === 20`. |
123
+ | `isXOnlyPublicKey` | `(value: unknown) => value is XOnlyPublicKey` | `Uint8Array` with `length === 32`, non-zero, and `< EC_P` (secp256k1 field prime). |
124
+ | `isPoint` | `(value: unknown) => value is PublicKey` | Valid SEC1-encoded point: compressed (33 bytes, prefix `0x02`/`0x03`) or uncompressed (65 bytes, prefix `0x04`/`0x06`/`0x07`), with x (and y if uncompressed) coordinates non-zero and `< EC_P`. |
125
+ | `isSatoshi` | `(value: unknown) => value is Satoshi` | `bigint` in range `[0n, SATOSHI_MAX]`. |
126
+ | `isPrivateKey` | `(value: unknown) => value is PrivateKey` | `Uint8Array` with `length === 32`, non-zero, and `< EC_N` (secp256k1 curve order). |
127
+ | `isSchnorrSignature` | `(value: unknown) => value is SchnorrSignature` | `Uint8Array` with `length === 64`. |
128
+ | `isSignature` | `(value: unknown) => value is Signature` | `Uint8Array` with `length` in `[8, 73]` (DER-encoded ECDSA range). |
129
+ | `isScript` | `(value: unknown) => value is Script` | `Uint8Array` instance (any length). |
130
+
131
+ > **Note:** `isMessageHash` is **not exported from the main `@btc-vision/bitcoin` entry point**. Import from the subpath:
132
+ >
133
+ > ```typescript
134
+ > import { isMessageHash } from '@btc-vision/bitcoin/types';
135
+ > ```
136
+ >
137
+ > | Function | Signature | Checks |
138
+ > |----------|-----------|--------|
139
+ > | `isMessageHash` | `(value: unknown) => value is MessageHash` | `Uint8Array` with `length === 32`. |
140
+
141
+ ### Taproot Type Guards
142
+
143
+ > **Note:** `isTapleaf` and `isTaptree` are defined in `src/types.ts` but are **not re-exported from the main `@btc-vision/bitcoin` entry point**. Import from the subpath:
144
+ >
145
+ > ```typescript
146
+ > import { isTapleaf, isTaptree } from '@btc-vision/bitcoin/types';
147
+ > ```
148
+
149
+ | Function | Signature | Checks |
150
+ |----------|-----------|--------|
151
+ | `isTapleaf` | `(value: unknown) => value is Tapleaf` | Object with an `output` property that is a `Uint8Array`. Optional `version` must be a number satisfying `(version & TAPLEAF_VERSION_MASK) === version`. |
152
+ | `isTaptree` | `(value: unknown) => value is Taptree` | Recursively validates: either a `Tapleaf` or a two-element array where both elements are valid `Taptree` nodes. |
153
+
154
+ ### Usage Examples
155
+
156
+ ```typescript
157
+ import { isBytes32, isPoint, isSatoshi, isHex } from '@btc-vision/bitcoin';
158
+
159
+ // Validate a hash before use
160
+ function processHash(input: unknown): void {
161
+ if (!isBytes32(input)) {
162
+ throw new Error('Expected a 32-byte hash');
163
+ }
164
+ // input is now narrowed to Bytes32
165
+ console.log(input.length); // 32
166
+ }
167
+
168
+ // Validate a public key
169
+ function checkKey(key: unknown): void {
170
+ if (isPoint(key)) {
171
+ // key is PublicKey (33 or 65 bytes, valid SEC1 encoding)
172
+ console.log('Valid public key, length:', key.length);
173
+ }
174
+ }
175
+
176
+ // Validate satoshi amounts from user input
177
+ function parseFee(value: unknown): void {
178
+ if (!isSatoshi(value)) {
179
+ throw new Error('Invalid satoshi amount');
180
+ }
181
+ // value is now Satoshi
182
+ }
183
+
184
+ // Validate hex strings (note: isHex from main entry accepts string, not unknown)
185
+ const input: string = '0a1b2c3d';
186
+ if (isHex(input)) {
187
+ // Safe to decode as hex
188
+ }
189
+ ```
190
+
191
+ ---
192
+
193
+ ## Assertion Helpers
194
+
195
+ Assertion helpers throw descriptive errors when validation fails. They use TypeScript's `asserts value is T` return type to narrow the value in subsequent code. Use these at function entry points for fail-fast validation.
196
+
197
+ ### assertXOnlyPublicKey
198
+
199
+ ```typescript
200
+ function assertXOnlyPublicKey(
201
+ value: unknown,
202
+ name: string,
203
+ ): asserts value is XOnlyPublicKey;
204
+ ```
205
+
206
+ Throws `TypeError` if `value` is not a 32-byte `Uint8Array`. Throws `RangeError` if the value is zero or `>= EC_P` (secp256k1 field prime). The `name` parameter is included in the error message for debugging.
207
+
208
+ > **Source code note:** The actual error message thrown when the value exceeds `EC_P` reads `"${name} exceeds curve order"`, which is technically misleading -- the check is against the field prime (`EC_P`), not the curve order (`EC_N`). The validation logic itself is correct; only the error message text is inaccurate.
209
+
210
+ ```typescript
211
+ import { assertXOnlyPublicKey } from '@btc-vision/bitcoin';
212
+
213
+ function tweakKey(internalKey: Uint8Array): void {
214
+ assertXOnlyPublicKey(internalKey, 'internalKey');
215
+ // internalKey is now XOnlyPublicKey
216
+ // If invalid, throws:
217
+ // TypeError: internalKey must be Uint8Array, got ...
218
+ // TypeError: internalKey must be 32 bytes, got ...
219
+ // RangeError: internalKey cannot be zero
220
+ // RangeError: internalKey exceeds curve order (note: actually checks EC_P, not EC_N)
221
+ }
222
+ ```
223
+
224
+ ### assertPrivateKey
225
+
226
+ ```typescript
227
+ function assertPrivateKey(
228
+ value: unknown,
229
+ name: string,
230
+ ): asserts value is PrivateKey;
231
+ ```
232
+
233
+ Throws `TypeError` if `value` is not a 32-byte `Uint8Array`. Throws `RangeError` if the value is zero or `>= EC_N` (secp256k1 curve order). The `name` parameter is included in the error message.
234
+
235
+ ```typescript
236
+ import { assertPrivateKey } from '@btc-vision/bitcoin';
237
+
238
+ function signMessage(key: Uint8Array, msg: Uint8Array): void {
239
+ assertPrivateKey(key, 'signingKey');
240
+ // key is now PrivateKey
241
+ // If invalid, throws:
242
+ // TypeError: signingKey must be Uint8Array, got ...
243
+ // TypeError: signingKey must be 32 bytes, got ...
244
+ // RangeError: signingKey cannot be zero
245
+ // RangeError: signingKey exceeds curve order
246
+ }
247
+ ```
248
+
249
+ ---
250
+
251
+ ## Conversion Functions
252
+
253
+ Conversion functions validate a plain value and return it cast to the corresponding branded type. They throw on invalid input, making them suitable for trusted boundaries where you have a value you believe is correct but need the compiler to agree.
254
+
255
+ ### toBytes32
256
+
257
+ ```typescript
258
+ function toBytes32(value: Uint8Array): Bytes32;
259
+ ```
260
+
261
+ Throws `TypeError` if `value.length !== 32`.
262
+
263
+ ```typescript
264
+ import { toBytes32 } from '@btc-vision/bitcoin';
265
+ import * as crypto from '@btc-vision/bitcoin/crypto';
266
+
267
+ const hash = crypto.sha256(data);
268
+ const typed: Bytes32 = toBytes32(hash);
269
+ ```
270
+
271
+ ### toBytes20
272
+
273
+ ```typescript
274
+ function toBytes20(value: Uint8Array): Bytes20;
275
+ ```
276
+
277
+ Throws `TypeError` if `value.length !== 20`.
278
+
279
+ ```typescript
280
+ import { toBytes20 } from '@btc-vision/bitcoin';
281
+
282
+ const hash160Result = hash160(pubkey);
283
+ const typed: Bytes20 = toBytes20(hash160Result);
284
+ ```
285
+
286
+ ### toSatoshi
287
+
288
+ ```typescript
289
+ function toSatoshi(value: bigint): Satoshi;
290
+ ```
291
+
292
+ Throws `RangeError` if `value < 0n` or `value > SATOSHI_MAX`.
293
+
294
+ ```typescript
295
+ import { toSatoshi } from '@btc-vision/bitcoin';
296
+
297
+ const fee: Satoshi = toSatoshi(10_000n);
298
+ const amount: Satoshi = toSatoshi(50_000_000n); // 0.5 BTC
299
+
300
+ // These throw:
301
+ toSatoshi(-1n); // RangeError: Satoshi cannot be negative, got -1
302
+ toSatoshi(2_100_000_000_000_001n); // RangeError: Satoshi exceeds maximum supply ...
303
+ ```
304
+
305
+ ### toMessageHash
306
+
307
+ > **Note:** `toMessageHash` is **not exported from the main `@btc-vision/bitcoin` entry point**. Import from the subpath:
308
+
309
+ ```typescript
310
+ function toMessageHash(value: Uint8Array): MessageHash;
311
+ ```
312
+
313
+ Throws `TypeError` if `value.length !== 32`.
314
+
315
+ ```typescript
316
+ import { toMessageHash } from '@btc-vision/bitcoin/types';
317
+
318
+ const sigHash = computeSigHash(tx, index);
319
+ const typed: MessageHash = toMessageHash(sigHash);
320
+ ```
321
+
322
+ ---
323
+
324
+ ## Stack Types
325
+
326
+ Stack types represent elements on the Bitcoin script execution stack.
327
+
328
+ ```typescript
329
+ type StackElement = Uint8Array | number;
330
+ type Stack = readonly StackElement[];
331
+ type StackFunction = () => Stack;
332
+ ```
333
+
334
+ | Type | Description |
335
+ |------|-------------|
336
+ | `StackElement` | A single value on the script stack: either raw bytes (`Uint8Array`) or a small integer (`number`, representing script numbers / opcodes). |
337
+ | `Stack` | A readonly array of `StackElement` values, representing the full stack state. |
338
+ | `StackFunction` | A lazily evaluated stack: a zero-argument function returning a `Stack`. Used when witness data is computed on demand. |
339
+
340
+ ```typescript
341
+ import type { Stack, StackElement, StackFunction } from '@btc-vision/bitcoin';
342
+
343
+ // Direct stack
344
+ const witness: Stack = [
345
+ new Uint8Array([0x01, 0x02]),
346
+ 42,
347
+ ];
348
+
349
+ // Lazy stack (computed when needed)
350
+ const lazyWitness: StackFunction = () => [
351
+ computeSignature(),
352
+ redeemScript,
353
+ ];
354
+ ```
355
+
356
+ ---
357
+
358
+ ## Taptree Types
359
+
360
+ Taptree types define the recursive structure of Taproot (and P2MR) script trees.
361
+
362
+ ### Tapleaf
363
+
364
+ ```typescript
365
+ export interface Tapleaf {
366
+ readonly output: Uint8Array;
367
+ readonly version?: number;
368
+ }
369
+ ```
370
+
371
+ A single leaf in the script tree. `output` is the compiled script bytecode. `version` is the leaf version (defaults to `0xc0`, the standard tapscript version). The version must satisfy `(version & TAPLEAF_VERSION_MASK) === version`, meaning the lowest bit must be zero.
372
+
373
+ ### Taptree
374
+
375
+ ```typescript
376
+ type Taptree = [Taptree | Tapleaf, Taptree | Tapleaf] | Tapleaf;
377
+ ```
378
+
379
+ A recursive binary tree type. Each node is either a `Tapleaf` (terminal) or a two-element tuple of child nodes. This mirrors the Merkle tree structure defined in BIP 341.
380
+
381
+ > **Note:** The `Tapleaf` and `Taptree` **types** are exported from `@btc-vision/bitcoin`, but the **runtime guard functions** `isTapleaf` and `isTaptree` are **not**. Import the guards from the subpath:
382
+
383
+ ```typescript
384
+ import type { Tapleaf, Taptree } from '@btc-vision/bitcoin';
385
+ import { isTapleaf, isTaptree } from '@btc-vision/bitcoin/types';
386
+
387
+ // Single leaf
388
+ const leaf: Tapleaf = { output: scriptBytes };
389
+
390
+ // Two-leaf tree
391
+ const tree: Taptree = [
392
+ { output: script1 },
393
+ { output: script2 },
394
+ ];
395
+
396
+ // Nested tree (3 leaves)
397
+ const nested: Taptree = [
398
+ [
399
+ { output: scriptA },
400
+ { output: scriptB },
401
+ ],
402
+ { output: scriptC },
403
+ ];
404
+
405
+ // Runtime validation
406
+ if (isTapleaf(value)) {
407
+ console.log(value.output); // Uint8Array
408
+ }
409
+ if (isTaptree(value)) {
410
+ // value is a valid tree structure
411
+ }
412
+ ```
413
+
414
+ ---
415
+
416
+ ## Constants
417
+
418
+ | Constant | Value | Exported from main entry? | Description |
419
+ |----------|-------|---------------------------|-------------|
420
+ | `SATOSHI_MAX` | `2_100_000_000_000_000n` (`21n * 10n ** 14n`) | **No** | Maximum total supply of Bitcoin in satoshis (21 million BTC). Used by `isSatoshi()` and `toSatoshi()` for upper bound validation. |
421
+ | `TAPLEAF_VERSION_MASK` | `0xfe` | Yes | Bitmask for valid tapleaf versions. The lowest bit is reserved for the parity flag in the control block, so valid leaf versions have bit 0 clear. |
422
+
423
+ > **Note:** `SATOSHI_MAX` is **not re-exported from the main `@btc-vision/bitcoin` entry point**. Import it from the subpath:
424
+
425
+ ```typescript
426
+ import { TAPLEAF_VERSION_MASK } from '@btc-vision/bitcoin';
427
+ import { SATOSHI_MAX } from '@btc-vision/bitcoin/types';
428
+
429
+ // SATOSHI_MAX
430
+ console.log(SATOSHI_MAX); // 2100000000000000n
431
+
432
+ // TAPLEAF_VERSION_MASK
433
+ const LEAF_VERSION_TAPSCRIPT = 0xc0;
434
+ console.log((LEAF_VERSION_TAPSCRIPT & TAPLEAF_VERSION_MASK) === LEAF_VERSION_TAPSCRIPT); // true
435
+
436
+ const invalid = 0xc1; // bit 0 set
437
+ console.log((invalid & TAPLEAF_VERSION_MASK) === invalid); // false
438
+ ```
439
+
440
+ ---
441
+
442
+ ## Utility Functions
443
+
444
+ ### stacksEqual
445
+
446
+ > **Note:** `stacksEqual` is defined in `src/types.ts` but is **not re-exported from the main `@btc-vision/bitcoin` entry point**. Import from the subpath:
447
+
448
+ ```typescript
449
+ function stacksEqual(a: Uint8Array[], b: Uint8Array[]): boolean;
450
+ ```
451
+
452
+ Compares two arrays of `Uint8Array` for deep equality. Returns `true` if both arrays have the same length and every element at each index is byte-equal. Uses the library's internal `equals()` function, which performs an **early-return comparison** (returns `false` immediately on the first mismatched byte). This is **not** constant-time.
453
+
454
+ ```typescript
455
+ import { stacksEqual } from '@btc-vision/bitcoin/types';
456
+
457
+ const a = [new Uint8Array([1, 2]), new Uint8Array([3, 4])];
458
+ const b = [new Uint8Array([1, 2]), new Uint8Array([3, 4])];
459
+ const c = [new Uint8Array([1, 2]), new Uint8Array([5, 6])];
460
+
461
+ console.log(stacksEqual(a, b)); // true
462
+ console.log(stacksEqual(a, c)); // false
463
+ ```
464
+
465
+ ---
466
+
467
+ ## Internal Constants
468
+
469
+ The module defines two internal constants used by type guards and assertions but not exported:
470
+
471
+ | Constant | Value (hex) | Description |
472
+ |----------|-------------|-------------|
473
+ | `EC_P` | `fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f` | The secp256k1 field prime. X-only public keys and point coordinates must be strictly less than this value. |
474
+ | `EC_N` | `fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141` | The secp256k1 curve order. Private keys must be strictly less than this value and non-zero. |
475
+
476
+ These are used by `isXOnlyPublicKey()`, `isPoint()`, `isPrivateKey()`, `assertXOnlyPublicKey()`, and `assertPrivateKey()` to enforce cryptographic validity beyond simple length checks.
477
+
478
+ ---
479
+
480
+ ## Complete Example
481
+
482
+ ```typescript
483
+ import {
484
+ // Branded types (type-only imports)
485
+ type Bytes32,
486
+ type Bytes20,
487
+ type PublicKey,
488
+ type XOnlyPublicKey,
489
+ type Satoshi,
490
+ type PrivateKey,
491
+ type SchnorrSignature,
492
+ type Script,
493
+
494
+ // Type guards (exported from main entry)
495
+ isBytes32,
496
+ isPoint,
497
+ isXOnlyPublicKey,
498
+ isPrivateKey,
499
+ isSatoshi,
500
+ isSchnorrSignature,
501
+ isHex,
502
+
503
+ // Assertions
504
+ assertXOnlyPublicKey,
505
+ assertPrivateKey,
506
+
507
+ // Conversions (exported from main entry)
508
+ toBytes32,
509
+ toBytes20,
510
+ toSatoshi,
511
+
512
+ // Constants (only TAPLEAF_VERSION_MASK from main entry)
513
+ TAPLEAF_VERSION_MASK,
514
+ } from '@btc-vision/bitcoin';
515
+
516
+ // Items not on the main entry point -- use subpath import:
517
+ import {
518
+ type MessageHash,
519
+ toMessageHash,
520
+ isMessageHash,
521
+ stacksEqual,
522
+ isTapleaf,
523
+ isTaptree,
524
+ SATOSHI_MAX,
525
+ } from '@btc-vision/bitcoin/types';
526
+
527
+ // --- Creating branded types from raw data ---
528
+
529
+ // From a known-good 32-byte hash
530
+ const txid: Bytes32 = toBytes32(rawHashBytes);
531
+
532
+ // From a bigint amount
533
+ const fee: Satoshi = toSatoshi(1000n);
534
+ const onebtc: Satoshi = toSatoshi(100_000_000n);
535
+
536
+ // --- Validating untrusted input ---
537
+
538
+ function processInput(data: unknown): Bytes32 {
539
+ if (!isBytes32(data)) {
540
+ throw new Error('Expected 32-byte value');
541
+ }
542
+ return data; // TypeScript knows this is Bytes32
543
+ }
544
+
545
+ // --- Using assertions for fail-fast validation ---
546
+
547
+ function buildTaprootOutput(internalKey: unknown, privateKey: unknown): void {
548
+ assertXOnlyPublicKey(internalKey, 'internalKey');
549
+ assertPrivateKey(privateKey, 'privateKey');
550
+
551
+ // Both are now narrowed to their branded types.
552
+ // Any invalid input would have thrown with a descriptive error.
553
+ const xonly: XOnlyPublicKey = internalKey;
554
+ const key: PrivateKey = privateKey;
555
+ }
556
+
557
+ // --- Conditional narrowing with type guards ---
558
+
559
+ function handleKey(key: Uint8Array): string {
560
+ if (isXOnlyPublicKey(key)) {
561
+ return 'x-only (32 bytes, valid field element)';
562
+ }
563
+ if (isPoint(key)) {
564
+ return `SEC1 public key (${key.length} bytes)`;
565
+ }
566
+ if (isPrivateKey(key)) {
567
+ return 'private key (32 bytes, valid scalar)';
568
+ }
569
+ return 'unknown key format';
570
+ }
571
+
572
+ // --- Satoshi bounds checking ---
573
+
574
+ console.log(isSatoshi(0n)); // true
575
+ console.log(isSatoshi(SATOSHI_MAX)); // true
576
+ console.log(isSatoshi(-1n)); // false
577
+ console.log(isSatoshi(SATOSHI_MAX + 1n)); // false
578
+ ```
579
+
580
+ ---
581
+
582
+ ## Source File
583
+
584
+ All types, guards, assertions, and conversions are defined in:
585
+
586
+ - `/src/types.ts` - Main module re-exporting branded types and defining all runtime functions
587
+ - `/src/branded.ts` - Re-exports branded type definitions from `@btc-vision/ecpair`