@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
package/eslint.config.js CHANGED
@@ -47,6 +47,9 @@ export default tseslint.config(
47
47
  'no-case-declarations': 'warn',
48
48
  '@typescript-eslint/ban-ts-comment': 'warn',
49
49
  '@typescript-eslint/no-deprecated': 'warn',
50
+
51
+ // Stupid new eslint rule that can cause nice bugs.
52
+ 'no-useless-assignment': 'off',
50
53
  },
51
54
  },
52
55
  {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@btc-vision/bitcoin",
3
3
  "type": "module",
4
- "version": "7.0.0-beta.0",
4
+ "version": "7.0.0-beta.1",
5
5
  "sideEffects": false,
6
6
  "description": "Client-side Bitcoin JavaScript library",
7
7
  "engines": {
@@ -116,6 +116,9 @@
116
116
  "stream": "stream-browserify",
117
117
  "zlib": "browserify-zlib"
118
118
  },
119
+ "react-native": {
120
+ "./build/crypto-hashes.js": "./build/crypto-hashes.native.js"
121
+ },
119
122
  "keywords": [
120
123
  "bitcoinjs",
121
124
  "bitcoin",
@@ -163,8 +166,10 @@
163
166
  "prebuild": "npm run prebuild:ecc && npm run check:circular"
164
167
  },
165
168
  "devDependencies": {
166
- "@eslint/js": "^9.39.2",
167
- "@types/node": "^25.0.10",
169
+ "@btc-vision/bs58check": "^5.0.1",
170
+ "@eslint/js": "^10.0.1",
171
+ "@scure/base": "^2.0.0",
172
+ "@types/node": "^25.2.3",
168
173
  "@types/randombytes": "^2.0.3",
169
174
  "@vitest/browser": "^4.0.18",
170
175
  "@vitest/browser-playwright": "^4.0.18",
@@ -173,26 +178,25 @@
173
178
  "bip39": "^3.1.0",
174
179
  "bip65": "^1.0.3",
175
180
  "bip68": "^1.0.4",
176
- "bs58": "^6.0.0",
177
181
  "dhttp": "^3.0.3",
178
- "eslint": "^9.39.2",
182
+ "eslint": "^10.0.0",
179
183
  "https-browserify": "^1.0.0",
180
184
  "madge": "^8.0.0",
181
185
  "minimaldata": "^1.0.2",
182
186
  "os-browserify": "^0.3.0",
183
- "playwright": "^1.58.0",
187
+ "playwright": "^1.58.2",
184
188
  "prettier": "^3.8.1",
185
189
  "randombytes": "^2.1.0",
186
190
  "regtest-client": "0.2.1",
187
- "rimraf": "^6.1.2",
191
+ "rimraf": "^6.1.3",
188
192
  "stream-browserify": "^3.0.0",
189
193
  "stream-http": "^3.2.0",
190
194
  "tiny-secp256k1": "^2.2.4",
191
195
  "ts-node": "^10.9.2",
192
- "typedoc": "^0.28.16",
196
+ "typedoc": "^0.28.17",
193
197
  "typedoc-material-theme": "^1.4.1",
194
198
  "typescript": "^5.9.3",
195
- "typescript-eslint": "^8.53.1",
199
+ "typescript-eslint": "^8.56.0",
196
200
  "vite": "^7.3.1",
197
201
  "vite-plugin-dts": "^4.5.4",
198
202
  "vite-plugin-node-polyfills": "^0.25.0",
@@ -201,8 +205,8 @@
201
205
  "vitest": "^4.0.18"
202
206
  },
203
207
  "peerDependencies": {
204
- "react-native-worklets": ">=0.7.0",
205
- "react-native-quick-crypto": ">=1.0.0"
208
+ "react-native-quick-crypto": ">=1.0.0",
209
+ "react-native-worklets": ">=0.7.0"
206
210
  },
207
211
  "peerDependenciesMeta": {
208
212
  "react-native-worklets": {
@@ -213,8 +217,8 @@
213
217
  }
214
218
  },
215
219
  "dependencies": {
216
- "@btc-vision/bip32": "^7.0.2",
217
- "@btc-vision/ecpair": "^4.0.2",
220
+ "@btc-vision/bip32": "^7.1.2",
221
+ "@btc-vision/ecpair": "^4.0.5",
218
222
  "@btc-vision/logger": "^1.0.8",
219
223
  "@noble/hashes": "^2.0.1",
220
224
  "@noble/secp256k1": "^3.0.0",
@@ -222,7 +226,6 @@
222
226
  "bech32": "^2.0.0",
223
227
  "bip174": "^3.0.0",
224
228
  "browserify-zlib": "^0.2.0",
225
- "@scure/base": "^1.2.4",
226
229
  "buffer": "^6.0.3",
227
230
  "process": "^0.11.10",
228
231
  "varuint-bitcoin": "^2.0.0"
package/src/address.ts CHANGED
@@ -15,6 +15,7 @@ import * as networks from './networks.js';
15
15
  import { p2op } from './payments/p2op.js';
16
16
  import { p2pkh } from './payments/p2pkh.js';
17
17
  import { p2sh } from './payments/p2sh.js';
18
+ import { p2mr } from './payments/p2mr.js';
18
19
  import { p2tr } from './payments/p2tr.js';
19
20
  import { p2wpkh } from './payments/p2wpkh.js';
20
21
  import { p2wsh } from './payments/p2wsh.js';
@@ -22,6 +23,7 @@ import * as bscript from './script.js';
22
23
  import { opcodes } from './script.js';
23
24
  import {
24
25
  type Bytes20,
26
+ type Script,
25
27
  isBytes20,
26
28
  isUInt8,
27
29
  toBytes20,
@@ -89,8 +91,9 @@ export function toFutureOPNetAddress(output: Uint8Array, network: Network): stri
89
91
  const opcode = output[0];
90
92
 
91
93
  // work out where the push-data really starts
92
- let pushPos = 1,
94
+ let pushPos: number = 1,
93
95
  progLen: number;
96
+
94
97
  const byte1 = output[1];
95
98
  const byte2 = output[2];
96
99
  if (byte1 !== undefined && byte1 < 0x4c) {
@@ -198,7 +201,7 @@ export function toBech32(
198
201
  * P2WPKH, P2WSH, P2TR) to avoid constructing payment objects and catching
199
202
  * exceptions. Falls back to payment constructors for exotic types.
200
203
  */
201
- export function fromOutputScript(output: Uint8Array, network?: Network): string {
204
+ export function fromOutputScript(output: Uint8Array | Script, network?: Network): string {
202
205
  network = network || networks.bitcoin;
203
206
  const len = output.length;
204
207
 
@@ -236,6 +239,13 @@ export function fromOutputScript(output: Uint8Array, network?: Network): string
236
239
  return bech32m.encode(network.bech32, words);
237
240
  }
238
241
 
242
+ // P2MR: OP_2(0x52) 0x20 <32-byte merkle root>
243
+ if (len === 34 && output[0] === 0x52 && output[1] === 0x20) {
244
+ const words = bech32m.toWords(output.subarray(2, 34));
245
+ words.unshift(2);
246
+ return bech32m.encode(network.bech32, words);
247
+ }
248
+
239
249
  // Fallback for exotic types
240
250
  try {
241
251
  return toFutureOPNetAddress(output, network);
@@ -268,12 +278,12 @@ export interface ToOutputScriptOptions {
268
278
  * Encodes address to output script with network, return output script if address matched.
269
279
  * @param address - The address to encode
270
280
  * @param networkOrOptions - Network or options object
271
- * @returns The output script as Uint8Array
281
+ * @returns The output script
272
282
  */
273
283
  export function toOutputScript(
274
284
  address: string,
275
285
  networkOrOptions?: Network | ToOutputScriptOptions,
276
- ): Uint8Array {
286
+ ): Script {
277
287
  let network: Network;
278
288
  let onFutureSegwitWarning: ((warning: string) => void) | undefined;
279
289
 
@@ -296,9 +306,9 @@ export function toOutputScript(
296
306
 
297
307
  if (decodeBase58) {
298
308
  if (decodeBase58.version === network.pubKeyHash)
299
- return p2pkh({ hash: decodeBase58.hash }).output as Uint8Array;
309
+ return p2pkh({ hash: decodeBase58.hash }).output as Script;
300
310
  if (decodeBase58.version === network.scriptHash)
301
- return p2sh({ hash: decodeBase58.hash }).output as Uint8Array;
311
+ return p2sh({ hash: decodeBase58.hash }).output as Script;
302
312
  } else {
303
313
  try {
304
314
  decodeBech32 = fromBech32(address);
@@ -313,19 +323,21 @@ export function toOutputScript(
313
323
  throw new Error(address + ' has an invalid prefix');
314
324
  if (decodeBech32.version === 0) {
315
325
  if (decodeBech32.data.length === 20)
316
- return p2wpkh({ hash: toBytes20(decodeBech32.data) }).output as Uint8Array;
326
+ return p2wpkh({ hash: toBytes20(decodeBech32.data) }).output as Script;
317
327
  if (decodeBech32.data.length === 32)
318
- return p2wsh({ hash: toBytes32(decodeBech32.data) }).output as Uint8Array;
328
+ return p2wsh({ hash: toBytes32(decodeBech32.data) }).output as Script;
319
329
  } else if (decodeBech32.version === 1) {
320
330
  if (decodeBech32.data.length === 32)
321
331
  return p2tr({ pubkey: decodeBech32.data as XOnlyPublicKey })
322
- .output as Uint8Array;
332
+ .output as Script;
333
+ } else if (decodeBech32.version === 2 && decodeBech32.data.length === 32) {
334
+ return p2mr({ hash: toBytes32(decodeBech32.data) }).output as Script;
323
335
  } else if (decodeBech32.version === FUTURE_OPNET_VERSION) {
324
336
  if (!network.bech32Opnet) throw new Error(address + ' has an invalid prefix');
325
337
  return p2op({
326
338
  program: decodeBech32.data,
327
339
  network,
328
- }).output as Uint8Array;
340
+ }).output as Script;
329
341
  } else if (
330
342
  decodeBech32.version >= FUTURE_SEGWIT_MIN_VERSION &&
331
343
  decodeBech32.version <= FUTURE_SEGWIT_MAX_VERSION &&
package/src/index.ts CHANGED
@@ -95,6 +95,7 @@ export type {
95
95
  BasePayment,
96
96
  P2SHPayment,
97
97
  P2TRPayment,
98
+ P2MRPayment,
98
99
  P2WPKHPayment,
99
100
  P2PKHPayment,
100
101
  P2MSPayment,
@@ -1,35 +1 @@
1
- /**
2
- * Base58Check encoding/decoding using @scure/base and @noble/hashes.
3
- *
4
- * Base58Check is a binary-to-text encoding with a 4-byte checksum
5
- * derived from double SHA-256, used for Bitcoin addresses and WIF keys.
6
- *
7
- * @packageDocumentation
8
- */
9
-
10
- import { createBase58check } from '@scure/base';
11
- import { sha256 } from '@noble/hashes/sha2.js';
12
-
13
- /**
14
- * Base58Check codec instance using SHA-256 for checksum.
15
- */
16
- export const base58check = createBase58check(sha256);
17
-
18
- /**
19
- * Encode a Uint8Array to a Base58Check string.
20
- * @param data - The data to encode
21
- * @returns The Base58Check encoded string
22
- */
23
- export function encode(data: Uint8Array): string {
24
- return base58check.encode(data);
25
- }
26
-
27
- /**
28
- * Decode a Base58Check string to a Uint8Array.
29
- * @param str - The Base58Check encoded string
30
- * @returns The decoded data
31
- * @throws If the checksum is invalid or the string is malformed
32
- */
33
- export function decode(str: string): Uint8Array {
34
- return base58check.decode(str);
35
- }
1
+ export { encode, decode, decodeUnsafe } from '@btc-vision/bs58check';
package/src/io/base64.ts CHANGED
@@ -11,11 +11,16 @@
11
11
  * @returns Uint8Array containing the decoded bytes
12
12
  */
13
13
  export function fromBase64(base64: string): Uint8Array {
14
+ if (base64.startsWith('0x')) {
15
+ throw new Error('Invalid base64 string: should not start with 0x');
16
+ }
17
+
14
18
  const binaryString = atob(base64);
15
19
  const bytes = new Uint8Array(binaryString.length);
16
20
  for (let i = 0; i < binaryString.length; i++) {
17
21
  bytes[i] = binaryString.charCodeAt(i);
18
22
  }
23
+
19
24
  return bytes;
20
25
  }
21
26
 
package/src/networks.ts CHANGED
@@ -92,6 +92,19 @@ export const testnet: Network = {
92
92
  wif: 0xef,
93
93
  };
94
94
 
95
+ export const opnetTestnet: Network = {
96
+ messagePrefix: '\x18Bitcoin Signed Message:\n',
97
+ bech32: 'opt',
98
+ bech32Opnet: 'opt',
99
+ bip32: {
100
+ public: 0x043587cf,
101
+ private: 0x04358394,
102
+ },
103
+ pubKeyHash: 0x6f,
104
+ scriptHash: 0xc4,
105
+ wif: 0xef,
106
+ };
107
+
95
108
  /**
96
109
  * Represents the Dogecoin mainnet configuration.
97
110
  *
@@ -59,6 +59,34 @@ export function rootHashFromPath(controlBlock: Uint8Array, leafHash: Uint8Array)
59
59
  return kj as Bytes32;
60
60
  }
61
61
 
62
+ /**
63
+ * Calculates the root hash from a P2MR control block and leaf hash.
64
+ * P2MR control blocks have no internal pubkey, so the merkle path starts at offset 1.
65
+ * @param controlBlock - The P2MR control block: [control_byte (1)] [merkle_path (32*m)]
66
+ * @param leafHash - The leaf hash.
67
+ * @returns The root hash.
68
+ * @throws {TypeError} If the control block length is less than 1.
69
+ */
70
+ export function rootHashFromPathP2MR(controlBlock: Uint8Array, leafHash: Uint8Array): Bytes32 {
71
+ if (controlBlock.length < 1)
72
+ throw new TypeError(
73
+ `The control-block length is too small. Got ${controlBlock.length}, expected min 1.`,
74
+ );
75
+ const m = (controlBlock.length - 1) / 32;
76
+
77
+ let kj = leafHash;
78
+ for (let j = 0; j < m; j++) {
79
+ const ej = controlBlock.subarray(1 + 32 * j, 33 + 32 * j);
80
+ if (compare(kj, ej) < 0) {
81
+ kj = tapBranchHash(kj, ej);
82
+ } else {
83
+ kj = tapBranchHash(ej, kj);
84
+ }
85
+ }
86
+
87
+ return kj as Bytes32;
88
+ }
89
+
62
90
  /**
63
91
  * Build a hash tree of merkle nodes from the scripts binary tree.
64
92
  * @param scriptTree - the tree of scripts to pairwise hash.
@@ -128,7 +156,14 @@ export function tweakKey(pubKey: XOnlyPublicKey, h: Bytes32 | undefined): Tweake
128
156
  };
129
157
  }
130
158
 
131
- function tapBranchHash(a: Uint8Array, b: Uint8Array): Bytes32 {
159
+ /**
160
+ * Computes the TapBranch tagged hash of two child hashes.
161
+ *
162
+ * @param a - First child hash (left branch).
163
+ * @param b - Second child hash (right branch).
164
+ * @returns The 32-byte TapBranch hash.
165
+ */
166
+ export function tapBranchHash(a: Uint8Array, b: Uint8Array): Bytes32 {
132
167
  return bcrypto.taggedHash('TapBranch', concat([a, b]));
133
168
  }
134
169
 
@@ -18,6 +18,7 @@ export {
18
18
  type P2WPKHPayment,
19
19
  type P2WSHPayment,
20
20
  type P2TRPayment,
21
+ type P2MRPayment,
21
22
  type P2OPPayment,
22
23
  type P2OPPaymentParams,
23
24
  type EmbedPayment,
@@ -35,6 +36,7 @@ export { P2MS, p2ms } from './p2ms.js';
35
36
  export { P2SH, p2sh } from './p2sh.js';
36
37
  export { P2WSH, p2wsh } from './p2wsh.js';
37
38
  export { P2TR, p2tr } from './p2tr.js';
39
+ export { P2MR, p2mr } from './p2mr.js';
38
40
  export { P2OP, p2op } from './p2op.js';
39
41
 
40
42
  // BIP341 Taproot utilities
@@ -43,6 +45,8 @@ export {
43
45
  LEAF_VERSION_TAPSCRIPT,
44
46
  MAX_TAPTREE_DEPTH,
45
47
  rootHashFromPath,
48
+ rootHashFromPathP2MR,
49
+ tapBranchHash,
46
50
  tapleafHash,
47
51
  toHashTree,
48
52
  tweakKey,