@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/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # @btc-vision/bitcoin
2
2
 
3
+ > **Experimental Feature: P2MR (Pay-to-Merkle-Root, BIP 360)**
4
+ > This library includes experimental support for P2MR, a SegWit version 2 output type that enables script-path-only spending from a bare Merkle root. P2MR addresses start with `bc1z`. See the [P2MR section](#p2mr-pay-to-merkle-root-bip-360) below for usage details. This feature is subject to change as BIP 360 is still a draft proposal.
5
+
3
6
  ![Bitcoin](https://img.shields.io/badge/Bitcoin-000?style=for-the-badge&logo=bitcoin&logoColor=white)
4
7
  ![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?style=for-the-badge&logo=typescript&logoColor=white)
5
8
  ![NodeJS](https://img.shields.io/badge/Node%20js-339933?style=for-the-badge&logo=nodedotjs&logoColor=white)
@@ -40,21 +43,21 @@ Requires Node.js >= 24.0.0.
40
43
 
41
44
  ## Performance
42
45
 
43
- Benchmarked against `bitcoinjs-lib` v7.0.1 on Node.js v25.3.0 (Linux x64). The fork column uses the fastest backend for each scenario.
46
+ Benchmarked against `bitcoinjs-lib` v7.0.1 and `@scure/btc-signer` on Node.js v25.6.0 (Linux x64). The fork column uses the fastest backend for each scenario.
44
47
 
45
- | Operation | Inputs | @btc-vision/bitcoin | bitcoinjs-lib | Improvement |
46
- |-----------|-------:|--------------------:|-------------:|:-----------:|
47
- | PSBT Creation | 100 | 2.13ms | 305ms | **143x** |
48
- | PSBT Creation | 500 | 9.90ms | 7,020ms | **709x** |
49
- | P2WPKH Sign | 100 | 40ms | 349ms | **8.6x** |
50
- | P2WPKH Sign | 500 | 258ms | 7,710ms | **29.9x** |
51
- | P2TR Sign | 100 | 22ms | 45ms | **2.1x** |
52
- | P2TR Sign | 500 | 106ms | 575ms | **5.4x** |
53
- | E2E P2WPKH | 100 | 44ms | 333ms | **7.6x** |
54
- | E2E P2TR | 100 | 22ms | 56ms | **2.5x** |
55
- | Parallel Sign (4 workers) | 500 | 106ms | 6,770ms | **63.6x** |
48
+ | Operation | Inputs | @btc-vision/bitcoin | @scure/btc-signer | bitcoinjs-lib | Fork vs Official |
49
+ |-----------|-------:|--------------------:|------------------:|--------------:|:----------------:|
50
+ | PSBT Creation | 100 | 2.08ms | 2.60ms | 303ms | **145x** |
51
+ | PSBT Creation | 500 | 9.73ms | 11.07ms | 6,870ms | **706x** |
52
+ | P2WPKH Sign | 100 | 40ms | 124ms | 348ms | **8.7x** |
53
+ | P2WPKH Sign | 500 | 283ms | 1,200ms | 7,250ms | **25.6x** |
54
+ | P2TR Sign | 100 | 21ms | 420ms | 44ms | **2.1x** |
55
+ | P2TR Sign | 500 | 102ms | 3,290ms | 522ms | **5.1x** |
56
+ | E2E P2WPKH | 100 | 41ms | 125ms | 342ms | **8.3x** |
57
+ | E2E P2TR | 100 | 22ms | 431ms | 55ms | **2.6x** |
58
+ | Parallel Sign (4 workers) | 500 | 102ms | N/A | 6,470ms | **63.3x** |
56
59
 
57
- Parallel signing via `worker_threads` / Web Workers is exclusive to this fork. See [benchmark-compare/BENCHMARK.md](benchmark-compare/BENCHMARK.md) for detailed methodology and analysis.
60
+ Parallel signing via `worker_threads` / Web Workers is exclusive to this fork. See [benchmark-compare/BENCHMARK.md](benchmark-compare/BENCHMARK.md) for detailed methodology, three-way analysis, and ECC backend comparison.
58
61
 
59
62
  ```bash
60
63
  cd benchmark-compare && npm install && npm run bench
@@ -342,6 +345,7 @@ import { toBytes32, toBytes20, toSatoshi } from '@btc-vision/bitcoin';
342
345
  | P2WPKH | `p2wpkh()` | `P2WPKH` | SegWit v0 Public Key Hash |
343
346
  | P2WSH | `p2wsh()` | `P2WSH` | SegWit v0 Script Hash |
344
347
  | P2TR | `p2tr()` | `P2TR` | Taproot (SegWit v1) |
348
+ | P2MR | `p2mr()` | `P2MR` | Pay-to-Merkle-Root (SegWit v2, BIP 360) |
345
349
  | P2OP | `p2op()` | `P2OP` | OPNet (SegWit v16) |
346
350
  | Embed | `p2data()` | `Embed` | OP_RETURN data |
347
351
 
@@ -494,6 +498,101 @@ If `react-native-worklets` is not installed, `createSigningPool()` returns a `Se
494
498
  - `react-native-quick-crypto` is **not** required — the core library uses `@noble/hashes` and `@noble/curves` (pure JS)
495
499
  - A future `@btc-vision/react-native-secp256k1` Nitro Module would provide native C++ performance via `initEccLib(createNativeBackend())`
496
500
 
501
+ ## P2MR (Pay-to-Merkle-Root, BIP 360)
502
+
503
+ P2MR is a SegWit version 2 output type defined in [BIP 360](https://github.com/nicbn/bips/blob/bip-p2mr/bip-0360.mediawiki). It commits to a bare Merkle root with no internal public key, providing script-path-only spending. This makes P2MR outputs resistant to quantum long-exposure attacks since no public key is revealed until spend time.
504
+
505
+ **Key differences from P2TR (Taproot):**
506
+
507
+ | | P2TR (v1) | P2MR (v2) |
508
+ |---|---|---|
509
+ | Address prefix | `bc1p` | `bc1z` |
510
+ | ScriptPubKey | `OP_1 <32-byte tweaked_pubkey>` | `OP_2 <32-byte merkle_root>` |
511
+ | Key-path spend | Yes | No |
512
+ | Script-path spend | Yes | Yes |
513
+ | Control block | `[1 + 32 + 32*m]` bytes (includes internal pubkey) | `[1 + 32*m]` bytes (no pubkey) |
514
+ | Parity bit | 0 or 1 | Always 1 |
515
+ | Sighash | BIP 342 | BIP 342 (same) |
516
+
517
+ ### Create a P2MR Output
518
+
519
+ ```typescript
520
+ import { payments } from '@btc-vision/bitcoin';
521
+
522
+ // From a script tree (single leaf)
523
+ const p2mr = payments.p2mr({
524
+ scriptTree: { output: leafScript },
525
+ });
526
+ console.log(p2mr.address); // bc1z...
527
+ console.log(p2mr.output); // OP_2 <merkle_root>
528
+ console.log(p2mr.hash); // raw merkle root bytes
529
+
530
+ // From a multi-leaf script tree
531
+ const p2mrMulti = payments.p2mr({
532
+ scriptTree: [
533
+ { output: leafA },
534
+ { output: leafB },
535
+ ],
536
+ });
537
+
538
+ // From a known address or hash
539
+ const fromAddr = payments.p2mr({
540
+ address: 'bc1z4rf73uru6qdyrv9w3nq9f3dwqlqmec4sdwj03hexu7n7r7dkehjs592djq',
541
+ });
542
+ ```
543
+
544
+ ### Spend a P2MR Output (PSBT)
545
+
546
+ P2MR spending uses the same PSBT taproot signing flow. The library automatically detects P2MR inputs and routes them to script-path finalization.
547
+
548
+ ```typescript
549
+ import {
550
+ Psbt, payments, initEccLib,
551
+ LEAF_VERSION_TAPSCRIPT, tapleafHash,
552
+ } from '@btc-vision/bitcoin';
553
+ import type { EccLib, Satoshi, Script } from '@btc-vision/bitcoin';
554
+
555
+ // ECC must be initialized before signing (see Quick Start above)
556
+ initEccLib(ecc as EccLib);
557
+
558
+ // Build the P2MR payment with a redeem script to get the witness/control block
559
+ const scriptTree = { output: leafScript, version: LEAF_VERSION_TAPSCRIPT };
560
+ const p2mr = payments.p2mr({
561
+ scriptTree,
562
+ redeem: { output: leafScript, redeemVersion: LEAF_VERSION_TAPSCRIPT },
563
+ });
564
+
565
+ const psbt = new Psbt();
566
+
567
+ // Add the P2MR input with taproot-style fields
568
+ psbt.addInput({
569
+ hash: txid,
570
+ index: vout,
571
+ witnessUtxo: {
572
+ value: amount as Satoshi,
573
+ script: p2mr.output as Script,
574
+ },
575
+ tapLeafScript: [{
576
+ leafVersion: LEAF_VERSION_TAPSCRIPT,
577
+ script: leafScript,
578
+ controlBlock: p2mr.witness![1]!, // control block (second witness element)
579
+ }],
580
+ tapMerkleRoot: p2mr.hash as Uint8Array,
581
+ });
582
+
583
+ psbt.addOutput({ address: destinationAddress, value: outputAmount as Satoshi });
584
+
585
+ // Sign using the leaf hash
586
+ const leafHash = tapleafHash({ output: leafScript, version: LEAF_VERSION_TAPSCRIPT });
587
+ psbt.signTaprootInput(0, keyPair, leafHash);
588
+
589
+ // Finalize and extract — P2MR always uses script-path finalization
590
+ psbt.finalizeInput(0);
591
+ const tx = psbt.extractTransaction();
592
+ ```
593
+
594
+ The final witness stack for a P2MR script-path spend is: `[signatures...] [leaf_script] [control_block]`, where the control block is `[leafVersion | 0x01, merkle_path...]` (no internal pubkey).
595
+
497
596
  ## Running Tests
498
597
 
499
598
  ```bash
@@ -1,24 +1,24 @@
1
- # Benchmark: @btc-vision/bitcoin vs bitcoinjs-lib
1
+ # Benchmark: @btc-vision/bitcoin vs bitcoinjs-lib vs @scure/btc-signer
2
2
 
3
- Comprehensive performance comparison between `@btc-vision/bitcoin` (this fork) and the official `bitcoinjs-lib` v7.0.1.
3
+ Comprehensive performance comparison between `@btc-vision/bitcoin` (this fork), the official `bitcoinjs-lib` v7.0.1, and `@scure/btc-signer`.
4
4
 
5
5
  ## Environment
6
6
 
7
7
  | Property | Value |
8
8
  |----------|-------|
9
- | Node.js | v25.3.0 |
10
- | OS | Linux 6.8.0-90-generic x64 |
11
- | Libraries | `@btc-vision/bitcoin` 7.0.0-alpha.10, `bitcoinjs-lib` 7.0.1 |
12
- | ECC backends | `@noble/secp256k1` 3.x (pure JS), `tiny-secp256k1` 2.2.4 (WASM) |
9
+ | Node.js | v25.6.0 |
10
+ | OS | Linux 6.8.0-100-generic x64 |
11
+ | Libraries | `@btc-vision/bitcoin` 7.0.0-rc.1, `bitcoinjs-lib` 7.0.1, `@scure/btc-signer` latest |
12
+ | ECC backends | `@noble/secp256k1` 3.x (pure JS), `tiny-secp256k1` 2.2.4 (WASM), `@noble/curves` (scure built-in) |
13
13
 
14
14
  ## Methodology
15
15
 
16
16
  - **Warmup**: 5 iterations (discarded) before each measurement
17
17
  - **Iterations**: 30 for small inputs, 10 for 250 inputs, 5 for 500 inputs
18
18
  - **Metric**: Median of all iterations (resistant to outlier spikes)
19
- - **Fairness**: Both libraries use identical key material derived from the same seed. Each scenario builds its own PSBTs with the correct types for each library.
19
+ - **Fairness**: All libraries use identical key material derived from the same seed. Each scenario builds its own PSBTs/transactions with the correct types for each library.
20
20
  - **Cold-start**: Library initialization measured via separate Node.js subprocess per iteration for true cold-start timing.
21
- - **ECC backends**: The fork is tested with both Noble (pure JS) and tiny-secp256k1 (WASM). The official library only supports tiny-secp256k1.
21
+ - **ECC backends**: The fork is tested with both Noble (pure JS) and tiny-secp256k1 (WASM). The official library only supports tiny-secp256k1. Scure uses its built-in `@noble/curves` backend.
22
22
 
23
23
  ## Results
24
24
 
@@ -26,58 +26,59 @@ Comprehensive performance comparison between `@btc-vision/bitcoin` (this fork) a
26
26
 
27
27
  | Configuration | Median |
28
28
  |--------------|--------|
29
- | Fork + Noble (pure JS) | 38.68ms |
30
- | Fork + tiny-secp256k1 (WASM) | 6.94ms |
31
- | Official + tiny-secp256k1 (WASM) | 7.58ms |
29
+ | Fork + Noble (pure JS) | 53.44ms |
30
+ | Fork + tiny-secp256k1 (WASM) | 7.16ms |
31
+ | Scure (@noble/curves) | 26.27ms |
32
+ | Official + tiny-secp256k1 (WASM) | 7.16ms |
32
33
 
33
- Both libraries have similar initialization time when using the same WASM backend. The Noble backend is ~32ms slower due to pure-JS module loading, but eliminates the WASM dependency entirely -- critical for React Native and edge runtimes without WASM support.
34
+ Both the fork and official library have identical initialization time when using the same WASM backend. Scure sits in the middle at ~26ms due to its pure-JS `@noble/curves` dependency. The Noble backend is ~53ms due to pure-JS module loading, but eliminates the WASM dependency entirely -- critical for React Native and edge runtimes without WASM support.
34
35
 
35
- ### 2. PSBT Creation (addInput + addOutput)
36
+ ### 2. PSBT / Transaction Creation (addInput + addOutput)
36
37
 
37
- | Inputs | Fork | Official | Speedup |
38
- |--------|------|----------|---------|
39
- | 10 | 0.27ms | 7.51ms | **27x** |
40
- | 50 | 1.12ms | 85.13ms | **76x** |
41
- | 100 | 2.13ms | 305.06ms | **143x** |
42
- | 250 | 5.12ms | 1.79s | **350x** |
43
- | 500 | 9.90ms | 7.02s | **709x** |
38
+ | Inputs | Fork | Scure | Official | Fork Speedup |
39
+ |--------|------|-------|----------|:------------:|
40
+ | 10 | 0.26ms | 0.58ms | 6.40ms | **24x** |
41
+ | 50 | 1.09ms | 1.56ms | 83.80ms | **77x** |
42
+ | 100 | 2.08ms | 2.60ms | 303.32ms | **145x** |
43
+ | 250 | 4.96ms | 5.86ms | 1.73s | **349x** |
44
+ | 500 | 9.73ms | 11.07ms | 6.87s | **706x** |
44
45
 
45
- The fork's PSBT creation scales linearly (O(n)). The official library exhibits O(n^2) behavior -- each `addInput()` call triggers increasingly expensive internal validation passes over all existing inputs. At 500 inputs, the official library takes over **7 seconds** just to build the PSBT, while the fork completes in under **10 milliseconds**.
46
+ The fork's PSBT creation scales linearly (O(n)). Scure's `Transaction` also scales linearly but is ~1.1-1.2x slower than the fork. The official library exhibits O(n^2) behavior -- each `addInput()` call triggers increasingly expensive internal validation passes over all existing inputs. At 500 inputs, the official library takes over **6.87 seconds** just to build the PSBT, while the fork completes in under **10 milliseconds** and scure in **11ms**.
46
47
 
47
48
  ### 3. P2WPKH Signing (create + sign, SegWit v0)
48
49
 
49
- | Inputs | Fork (Noble) | Fork (tiny) | Official | Best Fork Speedup |
50
- |--------|-------------|-------------|----------|-------------------|
51
- | 10 | 7.01ms | 3.96ms | 9.50ms | **2.4x** |
52
- | 50 | 31.66ms | 19.77ms | 108.04ms | **5.5x** |
53
- | 100 | 66.03ms | 40.40ms | 349.29ms | **8.6x** |
54
- | 250 | 174.11ms | 113.19ms | 1.91s | **16.9x** |
55
- | 500 | 378.83ms | 257.67ms | 7.71s | **29.9x** |
50
+ | Inputs | Fork (Noble) | Fork (tiny) | Scure | Official | Best Fork Speedup |
51
+ |--------|-------------|-------------|-------|----------|:-----------------:|
52
+ | 10 | 6.89ms | 4.04ms | 11.05ms | 9.36ms | **2.3x** |
53
+ | 50 | 31.33ms | 19.52ms | 54.85ms | 101.59ms | **5.2x** |
54
+ | 100 | 65.27ms | 39.96ms | 123.83ms | 348.15ms | **8.7x** |
55
+ | 250 | 171.40ms | 110.99ms | 413.48ms | 1.89s | **17x** |
56
+ | 500 | 378.57ms | 282.93ms | 1.20s | 7.25s | **25.6x** |
56
57
 
57
- This benchmark includes PSBT creation + signing. The fork's advantage compounds: faster PSBT construction plus efficient sighash caching. With tiny-secp256k1, the raw signing speed matches the official library, but the PSBT overhead dominates at scale.
58
+ This benchmark includes PSBT/transaction creation + signing. The fork's advantage compounds: faster PSBT construction plus efficient sighash caching. Scure is ~3x slower than the fork (tiny) but still ~6x faster than official at 500 inputs. With tiny-secp256k1, the fork's raw signing speed matches the official library, but the PSBT overhead dominates at scale.
58
59
 
59
60
  ### 4. P2TR Taproot Signing (Schnorr, SegWit v1)
60
61
 
61
- | Inputs | Fork (Noble) | Fork (tiny) | Official | Best Fork Speedup |
62
- |--------|-------------|-------------|----------|-------------------|
63
- | 10 | 22.41ms | 2.45ms | 3.63ms | **1.5x** |
64
- | 50 | 107.09ms | 10.57ms | 19.43ms | **1.8x** |
65
- | 100 | 216.75ms | 21.51ms | 45.38ms | **2.1x** |
66
- | 250 | 542.47ms | 54.42ms | 174.53ms | **3.2x** |
67
- | 500 | 1.10s | 106.43ms | 574.64ms | **5.4x** |
62
+ | Inputs | Fork (Noble) | Fork (tiny) | Scure | Official | Best Fork Speedup |
63
+ |--------|-------------|-------------|-------|----------|:-----------------:|
64
+ | 10 | 22.55ms | 2.44ms | 39.17ms | 3.62ms | **1.5x** |
65
+ | 50 | 105.59ms | 10.72ms | 197.94ms | 18.72ms | **1.7x** |
66
+ | 100 | 211.00ms | 21.33ms | 420.13ms | 44.27ms | **2.1x** |
67
+ | 250 | 520.22ms | 51.69ms | 1.24s | 171.77ms | **3.3x** |
68
+ | 500 | 1.04s | 102.31ms | 3.29s | 521.88ms | **5.1x** |
68
69
 
69
- With tiny-secp256k1, the fork is consistently faster due to its O(n) Taproot sighash caching (the official library recomputes more per input). Noble's pure-JS Schnorr implementation is slower than WASM for raw signing, which shows at small input counts, but the fork's architectural advantages compound at scale.
70
+ With tiny-secp256k1, the fork is consistently faster due to its O(n) Taproot sighash caching (the official library recomputes more per input). Scure is the slowest here at ~3.29s for 500 inputs, as its pure-JS Schnorr implementation carries significant per-signature overhead. Noble's pure-JS Schnorr is also slower than WASM for raw signing, which shows at small input counts, but the fork's architectural advantages compound at scale.
70
71
 
71
72
  ### 5. End-to-End Lifecycle (100 inputs)
72
73
 
73
- Full lifecycle: create PSBT + add inputs/outputs + sign + finalize + extract + serialize.
74
+ Full lifecycle: create PSBT/tx + add inputs/outputs + sign + finalize + extract + serialize.
74
75
 
75
- | Type | Fork (Noble) | Fork (tiny) | Official | Best Fork Speedup |
76
- |------|-------------|-------------|----------|-------------------|
77
- | P2WPKH | 66.89ms | 44.04ms | 332.84ms | **7.6x** |
78
- | P2TR | 217.06ms | 22.38ms | 55.72ms | **2.5x** |
76
+ | Type | Fork (Noble) | Fork (tiny) | Scure | Official | Best Fork Speedup |
77
+ |------|-------------|-------------|-------|----------|:-----------------:|
78
+ | P2WPKH | 66.62ms | 41.13ms | 125.32ms | 341.76ms | **8.3x** |
79
+ | P2TR | 210.98ms | 21.60ms | 431.07ms | 55.12ms | **2.6x** |
79
80
 
80
- P2WPKH end-to-end is **7.6x faster** with the fork (tiny-secp256k1 backend). P2TR end-to-end is **2.5x faster** with the fork's tiny-secp256k1 backend. Even though Noble's pure-JS Schnorr signing is slower for P2TR, the fork with the same WASM backend still decisively outperforms the official library due to O(n) PSBT construction and efficient sighash caching.
81
+ P2WPKH end-to-end is **8.3x faster** with the fork (tiny-secp256k1 backend). Scure is ~3x slower than the fork but still ~2.7x faster than official for P2WPKH. For P2TR, the fork with tiny-secp256k1 is **2.6x faster** than official. Scure's pure-JS Schnorr signing makes it the slowest for Taproot at 431ms vs 22ms (fork tiny) and 55ms (official).
81
82
 
82
83
  ### 6. Parallel Signing (fork-exclusive)
83
84
 
@@ -85,10 +86,24 @@ Worker-based parallel signing using `NodeWorkerSigningPool` with 4 `worker_threa
85
86
 
86
87
  | Inputs | Fork Sequential | Fork Parallel (4w) | Official Sequential | Speedup vs Seq | Speedup vs Official |
87
88
  |--------|----------------|-------------------|--------------------|----|------|
88
- | 100 | 64.28ms | 21.06ms | 333.17ms | 3.1x | **15.8x** |
89
- | 500 | 383.83ms | 106.38ms | 6.77s | 3.6x | **63.6x** |
89
+ | 100 | 64.27ms | 21.73ms | 310.42ms | 3.0x | **14.3x** |
90
+ | 500 | 380.27ms | 102.25ms | 6.47s | 3.7x | **63.3x** |
90
91
 
91
- Parallel signing is **exclusive to the fork**. At 500 inputs, parallel signing completes in 106ms vs the official library's 6.77s sequential signing -- a **63.6x improvement**.
92
+ Parallel signing is **exclusive to the fork**. At 500 inputs, parallel signing completes in 102ms vs the official library's 6.47s sequential signing -- a **63.3x improvement**.
93
+
94
+ ## Scure vs Official vs Fork
95
+
96
+ `@scure/btc-signer` sits between the fork and official library in most scenarios:
97
+
98
+ **Where Scure beats Official:**
99
+ - Transaction creation is O(n) like the fork, making it dramatically faster than official's O(n^2) PSBT creation at scale (11ms vs 6.87s at 500 inputs)
100
+ - P2WPKH signing is ~6x faster than official at 500 inputs, though ~4x slower than the fork
101
+
102
+ **Where Scure is slowest:**
103
+ - P2TR (Taproot) signing is the weakest spot -- 3.29s for 500 inputs vs 102ms (fork tiny) and 522ms (official). Its pure-JS Schnorr implementation has significant per-signature overhead
104
+ - No parallel signing support
105
+
106
+ **Bottom line:** Scure is a solid zero-dependency pure-JS option that avoids official's O(n^2) PSBT creation bottleneck, but its signing performance (especially Taproot) falls behind both the fork and official library.
92
107
 
93
108
  ## The tiny-secp256k1 Reality
94
109
 
@@ -99,7 +114,7 @@ A common assumption is that `tiny-secp256k1` (WASM) is always faster. The raw si
99
114
  - For P2TR-heavy workloads with few inputs, the per-signature difference matters
100
115
 
101
116
  **Where it doesn't matter:**
102
- - The PSBT construction overhead completely dominates at scale. The official library's O(n^2) behavior means PSBT creation alone takes 7s at 500 inputs vs 10ms in the fork -- a difference so large that the choice of signing backend is irrelevant
117
+ - The PSBT construction overhead completely dominates at scale. The official library's O(n^2) behavior means PSBT creation alone takes 6.87s at 500 inputs vs 10ms in the fork -- a difference so large that the choice of signing backend is irrelevant
103
118
  - P2WPKH signing with Noble is only ~1.5x slower per operation than WASM, and the fork's PSBT improvements more than compensate
104
119
 
105
120
  **Where WASM is a liability:**
@@ -107,21 +122,21 @@ A common assumption is that `tiny-secp256k1` (WASM) is always faster. The raw si
107
122
  - WASM initialization requires WebAssembly support -- unavailable in some React Native runtimes and edge environments
108
123
  - Cold-start adds measurable overhead in serverless / Lambda contexts
109
124
 
110
- **Bottom line:** The fork with Noble (pure JS) outperforms the official library with WASM for all real-world P2WPKH workloads. For P2TR-heavy workloads, the fork with tiny-secp256k1 is the fastest option and still outperforms the official library. The Noble backend provides portability across Node.js, browsers, and React Native with no WASM dependency.
125
+ **Bottom line:** The fork with Noble (pure JS) outperforms the official library with WASM for all real-world P2WPKH workloads. For P2TR-heavy workloads, the fork with tiny-secp256k1 is the fastest option and still outperforms both the official library and scure. The Noble backend provides portability across Node.js, browsers, and React Native with no WASM dependency.
111
126
 
112
127
  ## Summary
113
128
 
114
- | Scenario | Inputs | @btc-vision/bitcoin | bitcoinjs-lib | Improvement |
115
- |----------|--------|--------------------:|-------------:|:-----------:|
116
- | PSBT Creation | 100 | 2.13ms | 305ms | **143x** |
117
- | PSBT Creation | 500 | 9.90ms | 7,020ms | **709x** |
118
- | P2WPKH Sign | 100 | 40ms | 349ms | **8.6x** |
119
- | P2WPKH Sign | 500 | 258ms | 7,710ms | **29.9x** |
120
- | P2TR Sign | 100 | 22ms | 45ms | **2.1x** |
121
- | P2TR Sign | 500 | 106ms | 575ms | **5.4x** |
122
- | E2E P2WPKH | 100 | 44ms | 333ms | **7.6x** |
123
- | E2E P2TR | 100 | 22ms | 56ms | **2.5x** |
124
- | Parallel (4w) | 500 | 106ms | 6,770ms | **63.6x** |
129
+ | Scenario | Inputs | @btc-vision/bitcoin | @scure/btc-signer | bitcoinjs-lib | Fork vs Official |
130
+ |----------|--------|--------------------:|------------------:|--------------:|:----------------:|
131
+ | PSBT Creation | 100 | 2.08ms | 2.60ms | 303ms | **145x** |
132
+ | PSBT Creation | 500 | 9.73ms | 11.07ms | 6,870ms | **706x** |
133
+ | P2WPKH Sign | 100 | 40ms | 124ms | 348ms | **8.7x** |
134
+ | P2WPKH Sign | 500 | 283ms | 1,200ms | 7,250ms | **25.6x** |
135
+ | P2TR Sign | 100 | 21ms | 420ms | 44ms | **2.1x** |
136
+ | P2TR Sign | 500 | 102ms | 3,290ms | 522ms | **5.1x** |
137
+ | E2E P2WPKH | 100 | 41ms | 125ms | 342ms | **8.3x** |
138
+ | E2E P2TR | 100 | 22ms | 431ms | 55ms | **2.6x** |
139
+ | Parallel (4w) | 500 | 102ms | N/A | 6,470ms | **63.3x** |
125
140
 
126
141
  ## How to Reproduce
127
142