@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.
- package/README.md +112 -13
- package/benchmark-compare/BENCHMARK.md +74 -59
- package/benchmark-compare/compare.bench.ts +249 -96
- package/benchmark-compare/harness.ts +23 -25
- package/benchmark-compare/package.json +1 -0
- package/browser/address.d.ts +4 -4
- package/browser/address.d.ts.map +1 -1
- package/browser/chunks/{psbt-parallel-B-dfm5GZ.js → psbt-parallel-jZ6QcCnM.js} +3128 -2731
- package/browser/index.d.ts +1 -1
- package/browser/index.d.ts.map +1 -1
- package/browser/index.js +603 -585
- package/browser/io/base58check.d.ts +1 -25
- package/browser/io/base58check.d.ts.map +1 -1
- package/browser/io/base64.d.ts.map +1 -1
- package/browser/networks.d.ts +1 -0
- package/browser/networks.d.ts.map +1 -1
- package/browser/payments/bip341.d.ts +17 -0
- package/browser/payments/bip341.d.ts.map +1 -1
- package/browser/payments/index.d.ts +3 -2
- package/browser/payments/index.d.ts.map +1 -1
- package/browser/payments/p2mr.d.ts +169 -0
- package/browser/payments/p2mr.d.ts.map +1 -0
- package/browser/payments/types.d.ts +11 -1
- package/browser/payments/types.d.ts.map +1 -1
- package/browser/psbt/bip371.d.ts +30 -0
- package/browser/psbt/bip371.d.ts.map +1 -1
- package/browser/psbt/psbtutils.d.ts +1 -0
- package/browser/psbt/psbtutils.d.ts.map +1 -1
- package/browser/psbt.d.ts.map +1 -1
- package/browser/workers/index.js +9 -9
- package/build/address.d.ts +4 -4
- package/build/address.d.ts.map +1 -1
- package/build/address.js +11 -1
- package/build/address.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js.map +1 -1
- package/build/io/base58check.d.ts +1 -25
- package/build/io/base58check.d.ts.map +1 -1
- package/build/io/base58check.js +1 -31
- package/build/io/base58check.js.map +1 -1
- package/build/io/base64.d.ts.map +1 -1
- package/build/io/base64.js +3 -0
- package/build/io/base64.js.map +1 -1
- package/build/networks.d.ts +1 -0
- package/build/networks.d.ts.map +1 -1
- package/build/networks.js +12 -0
- package/build/networks.js.map +1 -1
- package/build/payments/bip341.d.ts +17 -0
- package/build/payments/bip341.d.ts.map +1 -1
- package/build/payments/bip341.js +32 -1
- package/build/payments/bip341.js.map +1 -1
- package/build/payments/index.d.ts +3 -2
- package/build/payments/index.d.ts.map +1 -1
- package/build/payments/index.js +2 -1
- package/build/payments/index.js.map +1 -1
- package/build/payments/p2mr.d.ts +178 -0
- package/build/payments/p2mr.d.ts.map +1 -0
- package/build/payments/p2mr.js +555 -0
- package/build/payments/p2mr.js.map +1 -0
- package/build/payments/types.d.ts +11 -1
- package/build/payments/types.d.ts.map +1 -1
- package/build/payments/types.js +1 -0
- package/build/payments/types.js.map +1 -1
- package/build/psbt/bip371.d.ts +30 -0
- package/build/psbt/bip371.d.ts.map +1 -1
- package/build/psbt/bip371.js +80 -15
- package/build/psbt/bip371.js.map +1 -1
- package/build/psbt/psbtutils.d.ts +1 -0
- package/build/psbt/psbtutils.d.ts.map +1 -1
- package/build/psbt/psbtutils.js +2 -0
- package/build/psbt/psbtutils.js.map +1 -1
- package/build/psbt.d.ts.map +1 -1
- package/build/psbt.js +3 -2
- package/build/psbt.js.map +1 -1
- package/build/pubkey.js +1 -1
- package/build/pubkey.js.map +1 -1
- package/build/tsconfig.build.tsbuildinfo +1 -1
- package/documentation/README.md +122 -0
- package/documentation/address.md +820 -0
- package/documentation/block.md +679 -0
- package/documentation/crypto.md +461 -0
- package/documentation/ecc.md +584 -0
- package/documentation/errors.md +656 -0
- package/documentation/io.md +942 -0
- package/documentation/networks.md +625 -0
- package/documentation/p2mr.md +380 -0
- package/documentation/payments.md +1485 -0
- package/documentation/psbt.md +1400 -0
- package/documentation/script.md +730 -0
- package/documentation/taproot.md +670 -0
- package/documentation/transaction.md +943 -0
- package/documentation/types.md +587 -0
- package/documentation/workers.md +1007 -0
- package/eslint.config.js +3 -0
- package/package.json +17 -14
- package/src/address.ts +22 -10
- package/src/index.ts +1 -0
- package/src/io/base58check.ts +1 -35
- package/src/io/base64.ts +5 -0
- package/src/networks.ts +13 -0
- package/src/payments/bip341.ts +36 -1
- package/src/payments/index.ts +4 -0
- package/src/payments/p2mr.ts +660 -0
- package/src/payments/types.ts +12 -0
- package/src/psbt/bip371.ts +84 -13
- package/src/psbt/psbtutils.ts +2 -0
- package/src/psbt.ts +4 -2
- package/src/pubkey.ts +1 -1
- package/test/bitcoin.core.spec.ts +1 -1
- package/test/fixtures/p2mr.json +270 -0
- package/test/integration/taproot.spec.ts +7 -3
- package/test/opnetTestnet.spec.ts +302 -0
- package/test/payments.spec.ts +3 -1
- package/test/psbt.spec.ts +297 -2
- package/test/tsconfig.json +2 -2
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Comprehensive benchmark
|
|
2
|
+
* Comprehensive benchmark comparing three Bitcoin transaction libraries:
|
|
3
|
+
* - `@btc-vision/bitcoin` (this fork) with Noble and tiny-secp256k1 backends
|
|
4
|
+
* - `bitcoinjs-lib` v7.0.1 (official)
|
|
5
|
+
* - `@scure/btc-signer` (scure)
|
|
3
6
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```bash
|
|
9
|
+
* cd benchmark-compare && npm run bench
|
|
10
|
+
* cd benchmark-compare && npm run bench:gc # with GC control
|
|
11
|
+
* ```
|
|
7
12
|
*
|
|
13
|
+
* @remarks
|
|
8
14
|
* Scenarios:
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
+
* 1. Library Initialization (cold-start)
|
|
16
|
+
* 2. PSBT Creation (varying input counts)
|
|
17
|
+
* 3. P2WPKH Signing
|
|
18
|
+
* 4. P2TR Taproot Signing
|
|
19
|
+
* 5. End-to-End Lifecycle
|
|
20
|
+
* 6. Parallel Signing (fork-only)
|
|
15
21
|
*/
|
|
16
22
|
|
|
17
23
|
import { execSync } from 'child_process';
|
|
@@ -29,12 +35,13 @@ import {
|
|
|
29
35
|
printForkOnly,
|
|
30
36
|
} from './harness.js';
|
|
31
37
|
|
|
32
|
-
// ── Official bitcoinjs-lib ──────────────────────────────────────────────────
|
|
33
38
|
import * as bitcoin from 'bitcoinjs-lib';
|
|
34
39
|
import ECPairFactory from 'ecpair';
|
|
35
40
|
import * as tinysecp from 'tiny-secp256k1';
|
|
36
41
|
|
|
37
|
-
|
|
42
|
+
import * as scureBtc from '@scure/btc-signer';
|
|
43
|
+
import { secp256k1 } from '@noble/curves/secp256k1.js';
|
|
44
|
+
|
|
38
45
|
import {
|
|
39
46
|
Psbt as ForkPsbt,
|
|
40
47
|
initEccLib,
|
|
@@ -50,49 +57,52 @@ import {
|
|
|
50
57
|
ECPairSigner,
|
|
51
58
|
} from '@btc-vision/ecpair';
|
|
52
59
|
|
|
53
|
-
|
|
54
|
-
// Initialization helpers
|
|
55
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
56
|
-
|
|
60
|
+
/** Initializes the official bitcoinjs-lib ECC backend. */
|
|
57
61
|
function initOfficial(): void {
|
|
58
62
|
bitcoin.initEccLib(tinysecp);
|
|
59
63
|
}
|
|
60
64
|
|
|
65
|
+
/** Initializes the fork ECC backend with Noble (pure JS). */
|
|
61
66
|
function initForkNoble(): void {
|
|
62
67
|
initEccLib(createNobleBackend());
|
|
63
68
|
}
|
|
64
69
|
|
|
70
|
+
/** Initializes the fork ECC backend with tiny-secp256k1 (WASM). */
|
|
65
71
|
function initForkTiny(): void {
|
|
66
72
|
initEccLib(createLegacyBackend(tinysecp));
|
|
67
73
|
}
|
|
68
74
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Deterministic seed shared across all libraries for fair comparison.
|
|
77
|
+
* All key pairs are derived from this same 32-byte seed.
|
|
78
|
+
*/
|
|
73
79
|
const SEED = Buffer.from(
|
|
74
80
|
'e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35',
|
|
75
81
|
'hex',
|
|
76
82
|
);
|
|
77
83
|
|
|
78
|
-
// Official ECPair
|
|
79
84
|
const officialECPair = ECPairFactory(tinysecp);
|
|
80
85
|
const officialKeyPair = officialECPair.fromPrivateKey(SEED);
|
|
81
86
|
|
|
82
|
-
// Fork Noble key pair
|
|
83
87
|
const nobleBackend = createNobleBackend();
|
|
84
88
|
const forkNobleKeyPair = ECPairSigner.fromPrivateKey(nobleBackend, SEED);
|
|
85
89
|
|
|
86
|
-
// Fork tiny-secp256k1 key pair
|
|
87
90
|
const tinyBackend = createLegacyBackend(tinysecp);
|
|
88
91
|
const forkTinyKeyPair = ECPairSigner.fromPrivateKey(tinyBackend, SEED);
|
|
89
92
|
|
|
90
|
-
|
|
91
|
-
|
|
93
|
+
const scurePrivKey = new Uint8Array(SEED);
|
|
94
|
+
const scurePubKey = secp256k1.getPublicKey(scurePrivKey, true);
|
|
95
|
+
const scureXOnlyPubKey = scurePubKey.slice(1);
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Lazily-initialized tweaked signers for P2TR key-path spends.
|
|
99
|
+
* Must be created after ECC init, so they're built on first access.
|
|
100
|
+
*/
|
|
92
101
|
let _forkNobleTweaked: ReturnType<typeof forkNobleKeyPair.tweak> | null = null;
|
|
93
102
|
let _forkTinyTweaked: ReturnType<typeof forkTinyKeyPair.tweak> | null = null;
|
|
94
103
|
let _officialTweaked: ReturnType<typeof officialKeyPair.tweak> | null = null;
|
|
95
104
|
|
|
105
|
+
/** @returns The fork Noble tweaked signer for Taproot key-path spends. */
|
|
96
106
|
function getForkNobleTweakedSigner() {
|
|
97
107
|
if (!_forkNobleTweaked) {
|
|
98
108
|
const xonly = toXOnly(forkNobleKeyPair.publicKey);
|
|
@@ -101,6 +111,7 @@ function getForkNobleTweakedSigner() {
|
|
|
101
111
|
return _forkNobleTweaked;
|
|
102
112
|
}
|
|
103
113
|
|
|
114
|
+
/** @returns The fork tiny-secp256k1 tweaked signer for Taproot key-path spends. */
|
|
104
115
|
function getForkTinyTweakedSigner() {
|
|
105
116
|
if (!_forkTinyTweaked) {
|
|
106
117
|
const xonly = toXOnly(forkTinyKeyPair.publicKey);
|
|
@@ -109,6 +120,7 @@ function getForkTinyTweakedSigner() {
|
|
|
109
120
|
return _forkTinyTweaked;
|
|
110
121
|
}
|
|
111
122
|
|
|
123
|
+
/** @returns The official tweaked signer for Taproot key-path spends. */
|
|
112
124
|
function getOfficialTweakedSigner() {
|
|
113
125
|
if (!_officialTweaked) {
|
|
114
126
|
const xonly = bitcoin.toXOnly(officialKeyPair.publicKey);
|
|
@@ -117,10 +129,7 @@ function getOfficialTweakedSigner() {
|
|
|
117
129
|
return _officialTweaked;
|
|
118
130
|
}
|
|
119
131
|
|
|
120
|
-
|
|
121
|
-
// Helper: build a fake prev tx output script for P2WPKH
|
|
122
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
123
|
-
|
|
132
|
+
/** Builds a P2WPKH witness UTXO for the official library. */
|
|
124
133
|
function makeWitnessUtxoOfficial(pubkey: Uint8Array): { script: Uint8Array; value: bigint } {
|
|
125
134
|
const p2wpkh = bitcoin.payments.p2wpkh({ pubkey });
|
|
126
135
|
return {
|
|
@@ -129,6 +138,7 @@ function makeWitnessUtxoOfficial(pubkey: Uint8Array): { script: Uint8Array; valu
|
|
|
129
138
|
};
|
|
130
139
|
}
|
|
131
140
|
|
|
141
|
+
/** Builds a P2WPKH witness UTXO for the fork library. */
|
|
132
142
|
function makeWitnessUtxoFork(pubkey: Uint8Array): { script: Uint8Array; value: bigint } {
|
|
133
143
|
const p2wpkh = forkPayments.p2wpkh({ pubkey });
|
|
134
144
|
return {
|
|
@@ -137,7 +147,13 @@ function makeWitnessUtxoFork(pubkey: Uint8Array): { script: Uint8Array; value: b
|
|
|
137
147
|
};
|
|
138
148
|
}
|
|
139
149
|
|
|
140
|
-
|
|
150
|
+
/** Builds a P2WPKH witness UTXO for `@scure/btc-signer`. */
|
|
151
|
+
function makeWitnessUtxoScure(pubkey: Uint8Array): { script: Uint8Array; amount: bigint } {
|
|
152
|
+
const p2wpkh = scureBtc.p2wpkh(pubkey);
|
|
153
|
+
return { script: p2wpkh.script, amount: 100_000n };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Builds a P2TR (Taproot) witness UTXO for the official library. */
|
|
141
157
|
function makeTaprootWitnessUtxoOfficial(pubkey: Uint8Array): { script: Uint8Array; value: bigint } {
|
|
142
158
|
const xonly = bitcoin.toXOnly(pubkey);
|
|
143
159
|
const p2tr = bitcoin.payments.p2tr({ internalPubkey: xonly });
|
|
@@ -147,6 +163,7 @@ function makeTaprootWitnessUtxoOfficial(pubkey: Uint8Array): { script: Uint8Arra
|
|
|
147
163
|
};
|
|
148
164
|
}
|
|
149
165
|
|
|
166
|
+
/** Builds a P2TR (Taproot) witness UTXO for the fork library. */
|
|
150
167
|
function makeTaprootWitnessUtxoFork(pubkey: Uint8Array): { script: Uint8Array; value: bigint } {
|
|
151
168
|
const xonly = toXOnly(pubkey);
|
|
152
169
|
const p2tr = forkPayments.p2tr({ internalPubkey: xonly });
|
|
@@ -156,23 +173,31 @@ function makeTaprootWitnessUtxoFork(pubkey: Uint8Array): { script: Uint8Array; v
|
|
|
156
173
|
};
|
|
157
174
|
}
|
|
158
175
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
176
|
+
/** Builds a P2TR (Taproot) witness UTXO for `@scure/btc-signer`. */
|
|
177
|
+
function makeTaprootWitnessUtxoScure(xOnlyPubkey: Uint8Array): { script: Uint8Array; amount: bigint } {
|
|
178
|
+
const p2tr = scureBtc.p2tr(xOnlyPubkey);
|
|
179
|
+
return { script: p2tr.script, amount: 100_000n };
|
|
180
|
+
}
|
|
162
181
|
|
|
182
|
+
/** @returns A random 32-byte transaction hash for benchmark inputs. */
|
|
163
183
|
function randomTxHash(): Buffer {
|
|
164
184
|
return randomBytes(32);
|
|
165
185
|
}
|
|
166
186
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
187
|
+
/**
|
|
188
|
+
* Scenario 1: Measures cold-start initialization time for each library
|
|
189
|
+
* by spawning isolated Node.js subprocesses per iteration.
|
|
190
|
+
*/
|
|
171
191
|
async function scenarioInit(): Promise<ComparisonRow[]> {
|
|
172
192
|
console.log('\n--- Scenario 1: Library Initialization (cold-start) ---\n');
|
|
173
193
|
|
|
174
194
|
const iterations = 5;
|
|
175
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Spawns a subprocess to measure cold-start import + init time.
|
|
198
|
+
* @param label - Display name for the result.
|
|
199
|
+
* @param scriptBody - ESM script body to execute in a child process.
|
|
200
|
+
*/
|
|
176
201
|
function measureColdStart(label: string, scriptBody: string): BenchResult {
|
|
177
202
|
const scriptPath = join(__dirname, `_bench_init_${Date.now()}.mjs`);
|
|
178
203
|
writeFileSync(scriptPath, scriptBody, 'utf-8');
|
|
@@ -232,6 +257,18 @@ console.log((performance.now() - t0).toFixed(4));`,
|
|
|
232
257
|
);
|
|
233
258
|
console.log(` Fork (tiny-secp256k1): ${fmt(forkTinyInit.median)}`);
|
|
234
259
|
|
|
260
|
+
const scureInit = measureColdStart(
|
|
261
|
+
'Scure (btc-signer)',
|
|
262
|
+
`const t0 = performance.now();
|
|
263
|
+
import * as btc from '@scure/btc-signer';
|
|
264
|
+
import { secp256k1 } from '@noble/curves/secp256k1.js';
|
|
265
|
+
const privKey = new Uint8Array(32).fill(1);
|
|
266
|
+
const pubKey = secp256k1.getPublicKey(privKey, true);
|
|
267
|
+
btc.p2wpkh(pubKey);
|
|
268
|
+
console.log((performance.now() - t0).toFixed(4));`,
|
|
269
|
+
);
|
|
270
|
+
console.log(` Scure (btc-signer): ${fmt(scureInit.median)}`);
|
|
271
|
+
|
|
235
272
|
const officialInit = measureColdStart(
|
|
236
273
|
'Official (tiny-secp256k1)',
|
|
237
274
|
`const t0 = performance.now();
|
|
@@ -249,18 +286,18 @@ console.log((performance.now() - t0).toFixed(4));`,
|
|
|
249
286
|
detail: 'cold-start',
|
|
250
287
|
forkNoble: forkNobleInit,
|
|
251
288
|
fork: forkTinyInit,
|
|
289
|
+
scure: scureInit,
|
|
252
290
|
official: officialInit,
|
|
253
291
|
}];
|
|
254
292
|
}
|
|
255
293
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
294
|
+
/**
|
|
295
|
+
* Scenario 2: Measures PSBT/transaction creation time (addInput + addOutput)
|
|
296
|
+
* without signing, across varying input counts (10-500).
|
|
297
|
+
*/
|
|
260
298
|
async function scenarioPsbtCreation(): Promise<ComparisonRow[]> {
|
|
261
299
|
console.log('\n--- Scenario 2: PSBT Creation ---\n');
|
|
262
300
|
|
|
263
|
-
// Make sure both ECC libs are initialized
|
|
264
301
|
initOfficial();
|
|
265
302
|
initForkNoble();
|
|
266
303
|
|
|
@@ -269,8 +306,6 @@ async function scenarioPsbtCreation(): Promise<ComparisonRow[]> {
|
|
|
269
306
|
|
|
270
307
|
for (const numInputs of inputCounts) {
|
|
271
308
|
const iters = numInputs >= 500 ? 5 : numInputs >= 250 ? 10 : 30;
|
|
272
|
-
|
|
273
|
-
// Pre-generate hashes
|
|
274
309
|
const hashes = Array.from({ length: numInputs }, () => randomTxHash());
|
|
275
310
|
|
|
276
311
|
const forkResult = await measure(
|
|
@@ -298,6 +333,31 @@ async function scenarioPsbtCreation(): Promise<ComparisonRow[]> {
|
|
|
298
333
|
{ iterations: iters },
|
|
299
334
|
);
|
|
300
335
|
|
|
336
|
+
const scureResult = await measure(
|
|
337
|
+
`Scure Tx ${numInputs}in`,
|
|
338
|
+
() => {
|
|
339
|
+
const witnessUtxo = makeWitnessUtxoScure(scurePubKey);
|
|
340
|
+
const tx = new scureBtc.Transaction();
|
|
341
|
+
for (let i = 0; i < numInputs; i++) {
|
|
342
|
+
tx.addInput({
|
|
343
|
+
txid: hashes[i]!,
|
|
344
|
+
index: 0,
|
|
345
|
+
witnessUtxo: {
|
|
346
|
+
script: witnessUtxo.script,
|
|
347
|
+
amount: witnessUtxo.amount,
|
|
348
|
+
},
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
for (let j = 0; j < 5; j++) {
|
|
352
|
+
tx.addOutput({
|
|
353
|
+
script: witnessUtxo.script,
|
|
354
|
+
amount: 10_000n,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
{ iterations: iters },
|
|
359
|
+
);
|
|
360
|
+
|
|
301
361
|
const officialResult = await measure(
|
|
302
362
|
`Official PSBT ${numInputs}in`,
|
|
303
363
|
() => {
|
|
@@ -321,7 +381,7 @@ async function scenarioPsbtCreation(): Promise<ComparisonRow[]> {
|
|
|
321
381
|
);
|
|
322
382
|
|
|
323
383
|
console.log(
|
|
324
|
-
` ${String(numInputs).padStart(4)} inputs: Fork=${fmt(forkResult.median)}, Official=${fmt(officialResult.median)}`,
|
|
384
|
+
` ${String(numInputs).padStart(4)} inputs: Fork=${fmt(forkResult.median)}, Scure=${fmt(scureResult.median)}, Official=${fmt(officialResult.median)}`,
|
|
325
385
|
);
|
|
326
386
|
|
|
327
387
|
rows.push({
|
|
@@ -329,6 +389,7 @@ async function scenarioPsbtCreation(): Promise<ComparisonRow[]> {
|
|
|
329
389
|
detail: `${numInputs} inputs`,
|
|
330
390
|
forkNoble: forkResult,
|
|
331
391
|
fork: null,
|
|
392
|
+
scure: scureResult,
|
|
332
393
|
official: officialResult,
|
|
333
394
|
});
|
|
334
395
|
}
|
|
@@ -336,10 +397,10 @@ async function scenarioPsbtCreation(): Promise<ComparisonRow[]> {
|
|
|
336
397
|
return rows;
|
|
337
398
|
}
|
|
338
399
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
400
|
+
/**
|
|
401
|
+
* Scenario 3: Measures P2WPKH (SegWit v0) transaction creation + signing
|
|
402
|
+
* across varying input counts (10-500).
|
|
403
|
+
*/
|
|
343
404
|
async function scenarioP2wpkhSigning(): Promise<ComparisonRow[]> {
|
|
344
405
|
console.log('\n--- Scenario 3: P2WPKH Signing ---\n');
|
|
345
406
|
|
|
@@ -352,7 +413,6 @@ async function scenarioP2wpkhSigning(): Promise<ComparisonRow[]> {
|
|
|
352
413
|
const iters = numInputs >= 500 ? 5 : numInputs >= 250 ? 10 : 30;
|
|
353
414
|
const hashes = Array.from({ length: numInputs }, () => randomTxHash());
|
|
354
415
|
|
|
355
|
-
// --- Fork with Noble ---
|
|
356
416
|
initForkNoble();
|
|
357
417
|
const forkNobleResult = await measure(
|
|
358
418
|
`Fork Noble P2WPKH ${numInputs}`,
|
|
@@ -380,7 +440,6 @@ async function scenarioP2wpkhSigning(): Promise<ComparisonRow[]> {
|
|
|
380
440
|
{ iterations: iters },
|
|
381
441
|
);
|
|
382
442
|
|
|
383
|
-
// --- Fork with tiny-secp256k1 ---
|
|
384
443
|
initForkTiny();
|
|
385
444
|
const forkTinyResult = await measure(
|
|
386
445
|
`Fork tiny P2WPKH ${numInputs}`,
|
|
@@ -408,7 +467,32 @@ async function scenarioP2wpkhSigning(): Promise<ComparisonRow[]> {
|
|
|
408
467
|
{ iterations: iters },
|
|
409
468
|
);
|
|
410
469
|
|
|
411
|
-
|
|
470
|
+
const scureResult = await measure(
|
|
471
|
+
`Scure P2WPKH ${numInputs}`,
|
|
472
|
+
() => {
|
|
473
|
+
const witnessUtxo = makeWitnessUtxoScure(scurePubKey);
|
|
474
|
+
const tx = new scureBtc.Transaction();
|
|
475
|
+
for (let i = 0; i < numInputs; i++) {
|
|
476
|
+
tx.addInput({
|
|
477
|
+
txid: hashes[i]!,
|
|
478
|
+
index: 0,
|
|
479
|
+
witnessUtxo: {
|
|
480
|
+
script: witnessUtxo.script,
|
|
481
|
+
amount: witnessUtxo.amount,
|
|
482
|
+
},
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
for (let j = 0; j < 5; j++) {
|
|
486
|
+
tx.addOutput({
|
|
487
|
+
script: witnessUtxo.script,
|
|
488
|
+
amount: 10_000n,
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
tx.sign(scurePrivKey);
|
|
492
|
+
},
|
|
493
|
+
{ iterations: iters },
|
|
494
|
+
);
|
|
495
|
+
|
|
412
496
|
initOfficial();
|
|
413
497
|
const officialResult = await measure(
|
|
414
498
|
`Official P2WPKH ${numInputs}`,
|
|
@@ -434,7 +518,7 @@ async function scenarioP2wpkhSigning(): Promise<ComparisonRow[]> {
|
|
|
434
518
|
);
|
|
435
519
|
|
|
436
520
|
console.log(
|
|
437
|
-
` ${String(numInputs).padStart(4)} inputs: Noble=${fmt(forkNobleResult.median)}, tiny=${fmt(forkTinyResult.median)}, Official=${fmt(officialResult.median)}`,
|
|
521
|
+
` ${String(numInputs).padStart(4)} inputs: Noble=${fmt(forkNobleResult.median)}, tiny=${fmt(forkTinyResult.median)}, Scure=${fmt(scureResult.median)}, Official=${fmt(officialResult.median)}`,
|
|
438
522
|
);
|
|
439
523
|
|
|
440
524
|
rows.push({
|
|
@@ -442,6 +526,7 @@ async function scenarioP2wpkhSigning(): Promise<ComparisonRow[]> {
|
|
|
442
526
|
detail: `${numInputs} inputs`,
|
|
443
527
|
forkNoble: forkNobleResult,
|
|
444
528
|
fork: forkTinyResult,
|
|
529
|
+
scure: scureResult,
|
|
445
530
|
official: officialResult,
|
|
446
531
|
});
|
|
447
532
|
}
|
|
@@ -449,10 +534,10 @@ async function scenarioP2wpkhSigning(): Promise<ComparisonRow[]> {
|
|
|
449
534
|
return rows;
|
|
450
535
|
}
|
|
451
536
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
537
|
+
/**
|
|
538
|
+
* Scenario 4: Measures P2TR Taproot (Schnorr, SegWit v1) transaction creation + signing
|
|
539
|
+
* across varying input counts (10-500).
|
|
540
|
+
*/
|
|
456
541
|
async function scenarioP2trSigning(): Promise<ComparisonRow[]> {
|
|
457
542
|
console.log('\n--- Scenario 4: P2TR Taproot Signing ---\n');
|
|
458
543
|
|
|
@@ -463,7 +548,6 @@ async function scenarioP2trSigning(): Promise<ComparisonRow[]> {
|
|
|
463
548
|
const iters = numInputs >= 500 ? 5 : numInputs >= 250 ? 10 : 30;
|
|
464
549
|
const hashes = Array.from({ length: numInputs }, () => randomTxHash());
|
|
465
550
|
|
|
466
|
-
// --- Fork with Noble ---
|
|
467
551
|
initForkNoble();
|
|
468
552
|
const nobleTweaked = getForkNobleTweakedSigner();
|
|
469
553
|
const forkNobleXOnly = toXOnly(forkNobleKeyPair.publicKey);
|
|
@@ -494,7 +578,6 @@ async function scenarioP2trSigning(): Promise<ComparisonRow[]> {
|
|
|
494
578
|
{ iterations: iters },
|
|
495
579
|
);
|
|
496
580
|
|
|
497
|
-
// --- Fork with tiny-secp256k1 ---
|
|
498
581
|
initForkTiny();
|
|
499
582
|
const tinyTweaked = getForkTinyTweakedSigner();
|
|
500
583
|
const forkTinyXOnly = toXOnly(forkTinyKeyPair.publicKey);
|
|
@@ -525,7 +608,33 @@ async function scenarioP2trSigning(): Promise<ComparisonRow[]> {
|
|
|
525
608
|
{ iterations: iters },
|
|
526
609
|
);
|
|
527
610
|
|
|
528
|
-
|
|
611
|
+
const scureResult = await measure(
|
|
612
|
+
`Scure P2TR ${numInputs}`,
|
|
613
|
+
() => {
|
|
614
|
+
const witnessUtxo = makeTaprootWitnessUtxoScure(scureXOnlyPubKey);
|
|
615
|
+
const tx = new scureBtc.Transaction();
|
|
616
|
+
for (let i = 0; i < numInputs; i++) {
|
|
617
|
+
tx.addInput({
|
|
618
|
+
txid: hashes[i]!,
|
|
619
|
+
index: 0,
|
|
620
|
+
witnessUtxo: {
|
|
621
|
+
script: witnessUtxo.script,
|
|
622
|
+
amount: witnessUtxo.amount,
|
|
623
|
+
},
|
|
624
|
+
tapInternalKey: scureXOnlyPubKey,
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
for (let j = 0; j < 5; j++) {
|
|
628
|
+
tx.addOutput({
|
|
629
|
+
script: witnessUtxo.script,
|
|
630
|
+
amount: 10_000n,
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
tx.sign(scurePrivKey);
|
|
634
|
+
},
|
|
635
|
+
{ iterations: iters },
|
|
636
|
+
);
|
|
637
|
+
|
|
529
638
|
initOfficial();
|
|
530
639
|
const officialTweaked = getOfficialTweakedSigner();
|
|
531
640
|
const xonlyOfficial = bitcoin.toXOnly(officialKeyPair.publicKey);
|
|
@@ -554,7 +663,7 @@ async function scenarioP2trSigning(): Promise<ComparisonRow[]> {
|
|
|
554
663
|
);
|
|
555
664
|
|
|
556
665
|
console.log(
|
|
557
|
-
` ${String(numInputs).padStart(4)} inputs: Noble=${fmt(forkNobleResult.median)}, tiny=${fmt(forkTinyResult.median)}, Official=${fmt(officialResult.median)}`,
|
|
666
|
+
` ${String(numInputs).padStart(4)} inputs: Noble=${fmt(forkNobleResult.median)}, tiny=${fmt(forkTinyResult.median)}, Scure=${fmt(scureResult.median)}, Official=${fmt(officialResult.median)}`,
|
|
558
667
|
);
|
|
559
668
|
|
|
560
669
|
rows.push({
|
|
@@ -562,6 +671,7 @@ async function scenarioP2trSigning(): Promise<ComparisonRow[]> {
|
|
|
562
671
|
detail: `${numInputs} inputs`,
|
|
563
672
|
forkNoble: forkNobleResult,
|
|
564
673
|
fork: forkTinyResult,
|
|
674
|
+
scure: scureResult,
|
|
565
675
|
official: officialResult,
|
|
566
676
|
});
|
|
567
677
|
}
|
|
@@ -569,10 +679,11 @@ async function scenarioP2trSigning(): Promise<ComparisonRow[]> {
|
|
|
569
679
|
return rows;
|
|
570
680
|
}
|
|
571
681
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
682
|
+
/**
|
|
683
|
+
* Scenario 5: Measures the full end-to-end lifecycle with 100 inputs:
|
|
684
|
+
* create PSBT/tx, add inputs/outputs, sign, finalize, extract, serialize to hex.
|
|
685
|
+
* Tests both P2WPKH and P2TR for all libraries.
|
|
686
|
+
*/
|
|
576
687
|
async function scenarioEndToEnd(): Promise<ComparisonRow[]> {
|
|
577
688
|
console.log('\n--- Scenario 5: End-to-End Lifecycle (100 inputs) ---\n');
|
|
578
689
|
|
|
@@ -580,7 +691,6 @@ async function scenarioEndToEnd(): Promise<ComparisonRow[]> {
|
|
|
580
691
|
const iters = 20;
|
|
581
692
|
const hashes = Array.from({ length: numInputs }, () => randomTxHash());
|
|
582
693
|
|
|
583
|
-
// --- Fork Noble (P2WPKH) ---
|
|
584
694
|
initForkNoble();
|
|
585
695
|
const forkNobleE2E = await measure(
|
|
586
696
|
'Fork Noble E2E P2WPKH',
|
|
@@ -611,7 +721,6 @@ async function scenarioEndToEnd(): Promise<ComparisonRow[]> {
|
|
|
611
721
|
{ iterations: iters },
|
|
612
722
|
);
|
|
613
723
|
|
|
614
|
-
// --- Fork tiny-secp256k1 (P2WPKH) ---
|
|
615
724
|
initForkTiny();
|
|
616
725
|
const forkTinyE2E = await measure(
|
|
617
726
|
'Fork tiny E2E P2WPKH',
|
|
@@ -642,7 +751,34 @@ async function scenarioEndToEnd(): Promise<ComparisonRow[]> {
|
|
|
642
751
|
{ iterations: iters },
|
|
643
752
|
);
|
|
644
753
|
|
|
645
|
-
|
|
754
|
+
const scureE2E = await measure(
|
|
755
|
+
'Scure E2E P2WPKH',
|
|
756
|
+
() => {
|
|
757
|
+
const witnessUtxo = makeWitnessUtxoScure(scurePubKey);
|
|
758
|
+
const tx = new scureBtc.Transaction();
|
|
759
|
+
for (let i = 0; i < numInputs; i++) {
|
|
760
|
+
tx.addInput({
|
|
761
|
+
txid: hashes[i]!,
|
|
762
|
+
index: 0,
|
|
763
|
+
witnessUtxo: {
|
|
764
|
+
script: witnessUtxo.script,
|
|
765
|
+
amount: witnessUtxo.amount,
|
|
766
|
+
},
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
for (let j = 0; j < 5; j++) {
|
|
770
|
+
tx.addOutput({
|
|
771
|
+
script: witnessUtxo.script,
|
|
772
|
+
amount: 10_000n,
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
tx.sign(scurePrivKey);
|
|
776
|
+
tx.finalize();
|
|
777
|
+
tx.hex;
|
|
778
|
+
},
|
|
779
|
+
{ iterations: iters },
|
|
780
|
+
);
|
|
781
|
+
|
|
646
782
|
initOfficial();
|
|
647
783
|
const officialE2E = await measure(
|
|
648
784
|
'Official E2E P2WPKH',
|
|
@@ -670,9 +806,8 @@ async function scenarioEndToEnd(): Promise<ComparisonRow[]> {
|
|
|
670
806
|
{ iterations: iters },
|
|
671
807
|
);
|
|
672
808
|
|
|
673
|
-
console.log(` P2WPKH 100in: Noble=${fmt(forkNobleE2E.median)}, tiny=${fmt(forkTinyE2E.median)}, Official=${fmt(officialE2E.median)}`);
|
|
809
|
+
console.log(` P2WPKH 100in: Noble=${fmt(forkNobleE2E.median)}, tiny=${fmt(forkTinyE2E.median)}, Scure=${fmt(scureE2E.median)}, Official=${fmt(officialE2E.median)}`);
|
|
674
810
|
|
|
675
|
-
// --- Fork Noble (P2TR) ---
|
|
676
811
|
initForkNoble();
|
|
677
812
|
const e2eNobleTweaked = getForkNobleTweakedSigner();
|
|
678
813
|
const e2eNobleXOnly = toXOnly(forkNobleKeyPair.publicKey);
|
|
@@ -706,7 +841,6 @@ async function scenarioEndToEnd(): Promise<ComparisonRow[]> {
|
|
|
706
841
|
{ iterations: iters },
|
|
707
842
|
);
|
|
708
843
|
|
|
709
|
-
// --- Fork tiny-secp256k1 (P2TR) ---
|
|
710
844
|
initForkTiny();
|
|
711
845
|
const e2eTinyTweaked = getForkTinyTweakedSigner();
|
|
712
846
|
const e2eTinyXOnly = toXOnly(forkTinyKeyPair.publicKey);
|
|
@@ -740,7 +874,35 @@ async function scenarioEndToEnd(): Promise<ComparisonRow[]> {
|
|
|
740
874
|
{ iterations: iters },
|
|
741
875
|
);
|
|
742
876
|
|
|
743
|
-
|
|
877
|
+
const scureE2ETr = await measure(
|
|
878
|
+
'Scure E2E P2TR',
|
|
879
|
+
() => {
|
|
880
|
+
const witnessUtxo = makeTaprootWitnessUtxoScure(scureXOnlyPubKey);
|
|
881
|
+
const tx = new scureBtc.Transaction();
|
|
882
|
+
for (let i = 0; i < numInputs; i++) {
|
|
883
|
+
tx.addInput({
|
|
884
|
+
txid: hashes[i]!,
|
|
885
|
+
index: 0,
|
|
886
|
+
witnessUtxo: {
|
|
887
|
+
script: witnessUtxo.script,
|
|
888
|
+
amount: witnessUtxo.amount,
|
|
889
|
+
},
|
|
890
|
+
tapInternalKey: scureXOnlyPubKey,
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
for (let j = 0; j < 5; j++) {
|
|
894
|
+
tx.addOutput({
|
|
895
|
+
script: witnessUtxo.script,
|
|
896
|
+
amount: 10_000n,
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
tx.sign(scurePrivKey);
|
|
900
|
+
tx.finalize();
|
|
901
|
+
tx.hex;
|
|
902
|
+
},
|
|
903
|
+
{ iterations: iters },
|
|
904
|
+
);
|
|
905
|
+
|
|
744
906
|
initOfficial();
|
|
745
907
|
const e2eOfficialTweaked = getOfficialTweakedSigner();
|
|
746
908
|
const xonly = bitcoin.toXOnly(officialKeyPair.publicKey);
|
|
@@ -771,7 +933,7 @@ async function scenarioEndToEnd(): Promise<ComparisonRow[]> {
|
|
|
771
933
|
{ iterations: iters },
|
|
772
934
|
);
|
|
773
935
|
|
|
774
|
-
console.log(` P2TR 100in: Noble=${fmt(forkNobleE2ETr.median)}, tiny=${fmt(forkTinyE2ETr.median)}, Official=${fmt(officialE2ETr.median)}`);
|
|
936
|
+
console.log(` P2TR 100in: Noble=${fmt(forkNobleE2ETr.median)}, tiny=${fmt(forkTinyE2ETr.median)}, Scure=${fmt(scureE2ETr.median)}, Official=${fmt(officialE2ETr.median)}`);
|
|
775
937
|
|
|
776
938
|
return [
|
|
777
939
|
{
|
|
@@ -779,6 +941,7 @@ async function scenarioEndToEnd(): Promise<ComparisonRow[]> {
|
|
|
779
941
|
detail: '100 inputs',
|
|
780
942
|
forkNoble: forkNobleE2E,
|
|
781
943
|
fork: forkTinyE2E,
|
|
944
|
+
scure: scureE2E,
|
|
782
945
|
official: officialE2E,
|
|
783
946
|
},
|
|
784
947
|
{
|
|
@@ -786,15 +949,16 @@ async function scenarioEndToEnd(): Promise<ComparisonRow[]> {
|
|
|
786
949
|
detail: '100 inputs',
|
|
787
950
|
forkNoble: forkNobleE2ETr,
|
|
788
951
|
fork: forkTinyE2ETr,
|
|
952
|
+
scure: scureE2ETr,
|
|
789
953
|
official: officialE2ETr,
|
|
790
954
|
},
|
|
791
955
|
];
|
|
792
956
|
}
|
|
793
957
|
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
958
|
+
/**
|
|
959
|
+
* Scenario 6: Measures fork-exclusive parallel signing using `NodeWorkerSigningPool`
|
|
960
|
+
* with 4 worker threads. Compares sequential fork, parallel fork, and official sequential.
|
|
961
|
+
*/
|
|
798
962
|
async function scenarioParallelSigning(): Promise<{
|
|
799
963
|
rows: { scenario: string; detail: string; result: BenchResult }[];
|
|
800
964
|
results: Record<string, BenchResult>;
|
|
@@ -814,7 +978,6 @@ async function scenarioParallelSigning(): Promise<{
|
|
|
814
978
|
const iters = numInputs >= 500 ? 5 : 10;
|
|
815
979
|
const hashes = Array.from({ length: numInputs }, () => randomTxHash());
|
|
816
980
|
|
|
817
|
-
// --- Fork Sequential ---
|
|
818
981
|
initForkNoble();
|
|
819
982
|
const seqResult = await measure(
|
|
820
983
|
`Fork Sequential ${numInputs}`,
|
|
@@ -842,13 +1005,11 @@ async function scenarioParallelSigning(): Promise<{
|
|
|
842
1005
|
{ iterations: iters, warmup: 2 },
|
|
843
1006
|
);
|
|
844
1007
|
|
|
845
|
-
// --- Fork Parallel (4 workers) ---
|
|
846
1008
|
NodeWorkerSigningPool.resetInstance();
|
|
847
1009
|
const pool = NodeWorkerSigningPool.getInstance({ workerCount: 4 });
|
|
848
1010
|
await pool.initialize();
|
|
849
1011
|
pool.preserveWorkers();
|
|
850
1012
|
|
|
851
|
-
// Warmup the pool
|
|
852
1013
|
{
|
|
853
1014
|
const warmupPsbt = new ForkPsbt({ network: forkNetworks.bitcoin });
|
|
854
1015
|
const witnessUtxo = makeWitnessUtxoFork(forkNobleKeyPair.publicKey);
|
|
@@ -897,7 +1058,6 @@ async function scenarioParallelSigning(): Promise<{
|
|
|
897
1058
|
|
|
898
1059
|
await pool.shutdown();
|
|
899
1060
|
|
|
900
|
-
// --- Official Sequential (for comparison) ---
|
|
901
1061
|
initOfficial();
|
|
902
1062
|
const officialSeqResult = await measure(
|
|
903
1063
|
`Official Sequential ${numInputs}`,
|
|
@@ -942,17 +1102,13 @@ async function scenarioParallelSigning(): Promise<{
|
|
|
942
1102
|
return { rows, results };
|
|
943
1103
|
}
|
|
944
1104
|
|
|
945
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
946
|
-
// Main
|
|
947
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
948
|
-
|
|
949
|
-
// dirname workaround for ESM
|
|
950
1105
|
const __filename = fileURLToPath(import.meta.url);
|
|
951
1106
|
const __dirname = dirname(__filename);
|
|
952
1107
|
|
|
1108
|
+
/** Runs all benchmark scenarios and prints comparison tables + JSON summary. */
|
|
953
1109
|
async function main(): Promise<void> {
|
|
954
1110
|
console.log('='.repeat(80));
|
|
955
|
-
console.log(' @btc-vision/bitcoin vs bitcoinjs-lib — Benchmark Comparison');
|
|
1111
|
+
console.log(' @btc-vision/bitcoin vs bitcoinjs-lib vs @scure/btc-signer — Benchmark Comparison');
|
|
956
1112
|
console.log(` Node ${process.version} | ${process.platform} ${process.arch}`);
|
|
957
1113
|
console.log(` GC control: ${typeof globalThis.gc === 'function' ? 'YES' : 'NO (run with --expose-gc for best results)'}`);
|
|
958
1114
|
console.log('='.repeat(80));
|
|
@@ -960,59 +1116,56 @@ async function main(): Promise<void> {
|
|
|
960
1116
|
const allRows: ComparisonRow[] = [];
|
|
961
1117
|
const allResults: Record<string, BenchResult> = {};
|
|
962
1118
|
|
|
963
|
-
// Scenario 1: Initialization
|
|
964
1119
|
const initRows = await scenarioInit();
|
|
965
1120
|
allRows.push(...initRows);
|
|
966
1121
|
for (const r of initRows) {
|
|
967
1122
|
if (r.forkNoble) allResults[`init_fork_noble`] = r.forkNoble;
|
|
968
1123
|
if (r.fork) allResults[`init_fork_tiny`] = r.fork;
|
|
1124
|
+
if (r.scure) allResults[`init_scure`] = r.scure;
|
|
969
1125
|
if (r.official) allResults[`init_official`] = r.official;
|
|
970
1126
|
}
|
|
971
1127
|
|
|
972
|
-
// Scenario 2: PSBT Creation
|
|
973
1128
|
const createRows = await scenarioPsbtCreation();
|
|
974
1129
|
allRows.push(...createRows);
|
|
975
1130
|
for (const r of createRows) {
|
|
976
1131
|
if (r.forkNoble) allResults[`create_fork_${r.detail}`] = r.forkNoble;
|
|
1132
|
+
if (r.scure) allResults[`create_scure_${r.detail}`] = r.scure;
|
|
977
1133
|
if (r.official) allResults[`create_official_${r.detail}`] = r.official;
|
|
978
1134
|
}
|
|
979
1135
|
|
|
980
|
-
// Scenario 3: P2WPKH Signing
|
|
981
1136
|
const wpkhRows = await scenarioP2wpkhSigning();
|
|
982
1137
|
allRows.push(...wpkhRows);
|
|
983
1138
|
for (const r of wpkhRows) {
|
|
984
1139
|
if (r.forkNoble) allResults[`wpkh_noble_${r.detail}`] = r.forkNoble;
|
|
985
1140
|
if (r.fork) allResults[`wpkh_tiny_${r.detail}`] = r.fork;
|
|
1141
|
+
if (r.scure) allResults[`wpkh_scure_${r.detail}`] = r.scure;
|
|
986
1142
|
if (r.official) allResults[`wpkh_official_${r.detail}`] = r.official;
|
|
987
1143
|
}
|
|
988
1144
|
|
|
989
|
-
// Scenario 4: P2TR Signing
|
|
990
1145
|
const trRows = await scenarioP2trSigning();
|
|
991
1146
|
allRows.push(...trRows);
|
|
992
1147
|
for (const r of trRows) {
|
|
993
1148
|
if (r.forkNoble) allResults[`tr_noble_${r.detail}`] = r.forkNoble;
|
|
994
1149
|
if (r.fork) allResults[`tr_tiny_${r.detail}`] = r.fork;
|
|
1150
|
+
if (r.scure) allResults[`tr_scure_${r.detail}`] = r.scure;
|
|
995
1151
|
if (r.official) allResults[`tr_official_${r.detail}`] = r.official;
|
|
996
1152
|
}
|
|
997
1153
|
|
|
998
|
-
// Scenario 5: End-to-End
|
|
999
1154
|
const e2eRows = await scenarioEndToEnd();
|
|
1000
1155
|
allRows.push(...e2eRows);
|
|
1001
1156
|
for (const r of e2eRows) {
|
|
1002
1157
|
if (r.forkNoble) allResults[`e2e_noble_${r.detail}_${r.scenario}`] = r.forkNoble;
|
|
1003
1158
|
if (r.fork) allResults[`e2e_tiny_${r.detail}_${r.scenario}`] = r.fork;
|
|
1159
|
+
if (r.scure) allResults[`e2e_scure_${r.detail}_${r.scenario}`] = r.scure;
|
|
1004
1160
|
if (r.official) allResults[`e2e_official_${r.detail}_${r.scenario}`] = r.official;
|
|
1005
1161
|
}
|
|
1006
1162
|
|
|
1007
|
-
// Scenario 6: Parallel Signing
|
|
1008
1163
|
const parallelData = await scenarioParallelSigning();
|
|
1009
1164
|
Object.assign(allResults, parallelData.results);
|
|
1010
1165
|
|
|
1011
|
-
|
|
1012
|
-
printComparison('Summary: @btc-vision/bitcoin vs bitcoinjs-lib', allRows);
|
|
1166
|
+
printComparison('Summary: @btc-vision/bitcoin vs bitcoinjs-lib vs @scure/btc-signer', allRows);
|
|
1013
1167
|
printForkOnly('Parallel Signing (fork-exclusive)', parallelData.rows);
|
|
1014
1168
|
|
|
1015
|
-
// ── JSON summary ──
|
|
1016
1169
|
const summary = buildSummary(allResults);
|
|
1017
1170
|
console.log('\n--- JSON Summary ---');
|
|
1018
1171
|
console.log(JSON.stringify(summary, null, 2));
|