@btc-vision/bitcoin 7.0.0-alpha.2 → 7.0.0-alpha.4

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 (134) hide show
  1. package/README.md +455 -155
  2. package/browser/chunks/WorkerSigningPool.sequential-DHha7j0b.js +113 -0
  3. package/browser/ecc/context.d.ts +22 -21
  4. package/browser/ecc/context.d.ts.map +1 -1
  5. package/browser/ecc/index.d.ts +1 -1
  6. package/browser/ecc/index.d.ts.map +1 -1
  7. package/browser/ecc/types.d.ts +10 -123
  8. package/browser/ecc/types.d.ts.map +1 -1
  9. package/browser/env.d.ts +13 -0
  10. package/browser/env.d.ts.map +1 -0
  11. package/browser/index.d.ts +3 -3
  12. package/browser/index.d.ts.map +1 -1
  13. package/browser/index.js +5790 -4295
  14. package/browser/io/index.d.ts +0 -1
  15. package/browser/io/index.d.ts.map +1 -1
  16. package/browser/payments/p2tr.d.ts.map +1 -1
  17. package/browser/psbt/types.d.ts +2 -68
  18. package/browser/psbt/types.d.ts.map +1 -1
  19. package/browser/psbt.d.ts +9 -11
  20. package/browser/psbt.d.ts.map +1 -1
  21. package/browser/types.d.ts +1 -1
  22. package/browser/types.d.ts.map +1 -1
  23. package/browser/workers/WorkerSigningPool.d.ts +6 -0
  24. package/browser/workers/WorkerSigningPool.d.ts.map +1 -1
  25. package/browser/workers/WorkerSigningPool.node.d.ts +6 -0
  26. package/browser/workers/WorkerSigningPool.node.d.ts.map +1 -1
  27. package/browser/workers/WorkerSigningPool.sequential.d.ts +69 -0
  28. package/browser/workers/WorkerSigningPool.sequential.d.ts.map +1 -0
  29. package/browser/workers/WorkerSigningPool.worklet.d.ts +64 -0
  30. package/browser/workers/WorkerSigningPool.worklet.d.ts.map +1 -0
  31. package/browser/workers/index.d.ts +2 -2
  32. package/browser/workers/index.d.ts.map +1 -1
  33. package/browser/workers/index.react-native.d.ts +28 -0
  34. package/browser/workers/index.react-native.d.ts.map +1 -0
  35. package/browser/workers/psbt-parallel.d.ts +2 -3
  36. package/browser/workers/psbt-parallel.d.ts.map +1 -1
  37. package/browser/workers/types.d.ts +12 -0
  38. package/browser/workers/types.d.ts.map +1 -1
  39. package/build/ecc/context.d.ts +22 -21
  40. package/build/ecc/context.d.ts.map +1 -1
  41. package/build/ecc/context.js +19 -114
  42. package/build/ecc/context.js.map +1 -1
  43. package/build/ecc/index.d.ts +1 -1
  44. package/build/ecc/index.d.ts.map +1 -1
  45. package/build/ecc/types.d.ts +7 -126
  46. package/build/ecc/types.d.ts.map +1 -1
  47. package/build/ecc/types.js +4 -1
  48. package/build/ecc/types.js.map +1 -1
  49. package/build/env.d.ts +13 -0
  50. package/build/env.d.ts.map +1 -0
  51. package/build/env.js +198 -0
  52. package/build/env.js.map +1 -0
  53. package/build/index.d.ts +4 -3
  54. package/build/index.d.ts.map +1 -1
  55. package/build/index.js +2 -1
  56. package/build/index.js.map +1 -1
  57. package/build/io/index.d.ts +0 -1
  58. package/build/io/index.d.ts.map +1 -1
  59. package/build/io/index.js +0 -2
  60. package/build/io/index.js.map +1 -1
  61. package/build/payments/p2tr.d.ts.map +1 -1
  62. package/build/payments/p2tr.js +2 -3
  63. package/build/payments/p2tr.js.map +1 -1
  64. package/build/psbt/types.d.ts +2 -68
  65. package/build/psbt/types.d.ts.map +1 -1
  66. package/build/psbt.d.ts +9 -11
  67. package/build/psbt.d.ts.map +1 -1
  68. package/build/psbt.js +38 -53
  69. package/build/psbt.js.map +1 -1
  70. package/build/tsconfig.build.tsbuildinfo +1 -1
  71. package/build/types.d.ts +1 -1
  72. package/build/types.d.ts.map +1 -1
  73. package/build/types.js +2 -16
  74. package/build/types.js.map +1 -1
  75. package/build/workers/WorkerSigningPool.d.ts +6 -0
  76. package/build/workers/WorkerSigningPool.d.ts.map +1 -1
  77. package/build/workers/WorkerSigningPool.js +8 -0
  78. package/build/workers/WorkerSigningPool.js.map +1 -1
  79. package/build/workers/WorkerSigningPool.node.d.ts +6 -0
  80. package/build/workers/WorkerSigningPool.node.d.ts.map +1 -1
  81. package/build/workers/WorkerSigningPool.node.js +9 -2
  82. package/build/workers/WorkerSigningPool.node.js.map +1 -1
  83. package/build/workers/WorkerSigningPool.sequential.d.ts +78 -0
  84. package/build/workers/WorkerSigningPool.sequential.d.ts.map +1 -0
  85. package/build/workers/WorkerSigningPool.sequential.js +160 -0
  86. package/build/workers/WorkerSigningPool.sequential.js.map +1 -0
  87. package/build/workers/WorkerSigningPool.worklet.d.ts +79 -0
  88. package/build/workers/WorkerSigningPool.worklet.d.ts.map +1 -0
  89. package/build/workers/WorkerSigningPool.worklet.js +388 -0
  90. package/build/workers/WorkerSigningPool.worklet.js.map +1 -0
  91. package/build/workers/index.d.ts +2 -2
  92. package/build/workers/index.d.ts.map +1 -1
  93. package/build/workers/index.js +9 -0
  94. package/build/workers/index.js.map +1 -1
  95. package/build/workers/index.react-native.d.ts +28 -0
  96. package/build/workers/index.react-native.d.ts.map +1 -0
  97. package/build/workers/index.react-native.js +67 -0
  98. package/build/workers/index.react-native.js.map +1 -0
  99. package/build/workers/psbt-parallel.d.ts +2 -3
  100. package/build/workers/psbt-parallel.d.ts.map +1 -1
  101. package/build/workers/psbt-parallel.js +4 -4
  102. package/build/workers/psbt-parallel.js.map +1 -1
  103. package/build/workers/types.d.ts +12 -0
  104. package/build/workers/types.d.ts.map +1 -1
  105. package/package.json +14 -4
  106. package/src/ecc/context.ts +26 -147
  107. package/src/ecc/index.ts +2 -2
  108. package/src/ecc/types.ts +7 -138
  109. package/src/env.ts +237 -0
  110. package/src/index.ts +2 -4
  111. package/src/io/index.ts +0 -3
  112. package/src/payments/p2tr.ts +2 -2
  113. package/src/psbt/types.ts +2 -84
  114. package/src/psbt.ts +63 -121
  115. package/src/types.ts +5 -28
  116. package/src/workers/WorkerSigningPool.node.ts +10 -2
  117. package/src/workers/WorkerSigningPool.sequential.ts +190 -0
  118. package/src/workers/WorkerSigningPool.ts +9 -0
  119. package/src/workers/WorkerSigningPool.worklet.ts +519 -0
  120. package/src/workers/index.react-native.ts +110 -0
  121. package/src/workers/index.ts +10 -1
  122. package/src/workers/psbt-parallel.ts +8 -8
  123. package/src/workers/types.ts +16 -0
  124. package/test/env.spec.ts +418 -0
  125. package/test/workers-pool.spec.ts +43 -0
  126. package/test/workers-sequential.spec.ts +669 -0
  127. package/test/workers-worklet.spec.ts +500 -0
  128. package/browser/io/MemoryPool.d.ts +0 -220
  129. package/browser/io/MemoryPool.d.ts.map +0 -1
  130. package/build/io/MemoryPool.d.ts +0 -220
  131. package/build/io/MemoryPool.d.ts.map +0 -1
  132. package/build/io/MemoryPool.js +0 -309
  133. package/build/io/MemoryPool.js.map +0 -1
  134. package/src/io/MemoryPool.ts +0 -343
package/src/psbt.ts CHANGED
@@ -10,7 +10,6 @@ import type {
10
10
  import { checkForInput, checkForOutput, Psbt as PsbtBase } from 'bip174';
11
11
  import { clone, equals, fromBase64, fromHex, toHex } from './io/index.js';
12
12
 
13
- import type { BIP32Interface } from '@btc-vision/bip32';
14
13
  import { fromOutputScript, toOutputScript } from './address.js';
15
14
  import { bitcoin as btcNetwork } from './networks.js';
16
15
  import * as payments from './payments/index.js';
@@ -41,7 +40,6 @@ import type {
41
40
  PsbtTxInput,
42
41
  PsbtTxOutput,
43
42
  Signer,
44
- SignerAlternative,
45
43
  SignerAsync,
46
44
  TaprootHashCheckSigner,
47
45
  ValidateSigFunction,
@@ -67,7 +65,6 @@ import {
67
65
  } from './psbt/validation.js';
68
66
  import { checkInvalidP2WSH, classifyScript, getMeaningfulScript, range } from './psbt/utils.js';
69
67
  import { witnessStackToScriptWitness } from './psbt/psbtutils.js';
70
- import type { UniversalSigner } from '@btc-vision/ecpair';
71
68
 
72
69
  // Re-export types from the types module
73
70
  export type {
@@ -84,7 +81,6 @@ export type {
84
81
  PsbtOutputExtendedScript,
85
82
  HDSigner,
86
83
  HDSignerAsync,
87
- SignerAlternative,
88
84
  Signer,
89
85
  SignerAsync,
90
86
  TaprootHashCheckSigner,
@@ -649,7 +645,7 @@ export class Psbt {
649
645
  }
650
646
 
651
647
  public signAllInputs(
652
- keyPair: Signer | SignerAlternative | BIP32Interface | UniversalSigner,
648
+ keyPair: Signer | HDSigner,
653
649
  sighashTypes?: number[],
654
650
  ): this {
655
651
  if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input');
@@ -670,7 +666,7 @@ export class Psbt {
670
666
  }
671
667
 
672
668
  public async signAllInputsAsync(
673
- keyPair: Signer | SignerAlternative | SignerAsync | BIP32Interface | UniversalSigner,
669
+ keyPair: Signer | SignerAsync | HDSigner | HDSignerAsync,
674
670
  sighashTypes?: number[],
675
671
  ): Promise<void> {
676
672
  if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input');
@@ -697,7 +693,7 @@ export class Psbt {
697
693
 
698
694
  public signInput(
699
695
  inputIndex: number,
700
- keyPair: Signer | SignerAlternative | HDSigner | BIP32Interface | UniversalSigner,
696
+ keyPair: Signer | HDSigner,
701
697
  sighashTypes?: number[],
702
698
  ): this {
703
699
  if (!keyPair || !keyPair.publicKey) {
@@ -714,7 +710,7 @@ export class Psbt {
714
710
 
715
711
  public signTaprootInput(
716
712
  inputIndex: number,
717
- keyPair: Signer | SignerAlternative | HDSigner | BIP32Interface | UniversalSigner,
713
+ keyPair: Signer | HDSigner,
718
714
  tapLeafHashToSign?: Uint8Array,
719
715
  sighashTypes?: number[],
720
716
  ): this {
@@ -736,63 +732,45 @@ export class Psbt {
736
732
  throw new Error(`Input #${inputIndex} is not of type Taproot.`);
737
733
  }
738
734
 
739
- public signInputAsync(
735
+ public async signInputAsync(
740
736
  inputIndex: number,
741
- keyPair:
742
- | Signer
743
- | SignerAlternative
744
- | SignerAsync
745
- | HDSigner
746
- | HDSignerAsync
747
- | BIP32Interface
748
- | UniversalSigner,
737
+ keyPair: Signer | SignerAsync | HDSigner | HDSignerAsync,
749
738
  sighashTypes?: number[],
750
739
  ): Promise<void> {
751
- return Promise.resolve().then(() => {
752
- if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input');
753
-
754
- const input = checkForInput(this.data.inputs, inputIndex);
755
- if (isTaprootInput(input))
756
- return this.#signTaprootInputAsync(
757
- inputIndex,
758
- input,
759
- keyPair,
760
- undefined,
761
- sighashTypes,
762
- );
763
-
764
- return this.#signInputAsync(inputIndex, keyPair, sighashTypes);
765
- });
740
+ if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input');
741
+
742
+ const input = checkForInput(this.data.inputs, inputIndex);
743
+ if (isTaprootInput(input))
744
+ return this.#signTaprootInputAsync(
745
+ inputIndex,
746
+ input,
747
+ keyPair,
748
+ undefined,
749
+ sighashTypes,
750
+ );
751
+
752
+ return this.#signInputAsync(inputIndex, keyPair, sighashTypes);
766
753
  }
767
754
 
768
- public signTaprootInputAsync(
755
+ public async signTaprootInputAsync(
769
756
  inputIndex: number,
770
- keyPair:
771
- | Signer
772
- | SignerAlternative
773
- | SignerAsync
774
- | HDSigner
775
- | HDSignerAsync
776
- | BIP32Interface
777
- | UniversalSigner,
757
+ keyPair: Signer | SignerAsync | HDSigner | HDSignerAsync,
778
758
  tapLeafHash?: Uint8Array,
779
759
  sighashTypes?: number[],
780
760
  ): Promise<void> {
781
- return Promise.resolve().then(() => {
782
- if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input');
783
-
784
- const input = checkForInput(this.data.inputs, inputIndex);
785
- if (isTaprootInput(input))
786
- return this.#signTaprootInputAsync(
787
- inputIndex,
788
- input,
789
- keyPair,
790
- tapLeafHash,
791
- sighashTypes,
792
- );
793
-
794
- throw new Error(`Input #${inputIndex} is not of type Taproot.`);
795
- });
761
+ if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input');
762
+
763
+ const input = checkForInput(this.data.inputs, inputIndex);
764
+ if (isTaprootInput(input))
765
+ return this.#signTaprootInputAsync(
766
+ inputIndex,
767
+ input,
768
+ keyPair,
769
+ tapLeafHash,
770
+ sighashTypes,
771
+ );
772
+
773
+ throw new Error(`Input #${inputIndex} is not of type Taproot.`);
796
774
  }
797
775
 
798
776
  public toBuffer(): Uint8Array {
@@ -872,15 +850,7 @@ export class Psbt {
872
850
  public checkTaprootHashesForSig(
873
851
  inputIndex: number,
874
852
  input: PsbtInput,
875
- keyPair:
876
- | Signer
877
- | SignerAlternative
878
- | SignerAsync
879
- | HDSigner
880
- | HDSignerAsync
881
- | TaprootHashCheckSigner
882
- | BIP32Interface
883
- | UniversalSigner,
853
+ keyPair: Signer | SignerAsync | HDSigner | HDSignerAsync | TaprootHashCheckSigner,
884
854
  tapLeafHashToSign?: Uint8Array,
885
855
  allowedSighashTypes?: number[],
886
856
  ): { hash: MessageHash; leafHash?: Bytes32 }[] {
@@ -1064,7 +1034,7 @@ export class Psbt {
1064
1034
 
1065
1035
  #signInput(
1066
1036
  inputIndex: number,
1067
- keyPair: Signer | SignerAlternative | HDSigner | BIP32Interface | UniversalSigner,
1037
+ keyPair: Signer | HDSigner,
1068
1038
  sighashTypes: number[] = [Transaction.SIGHASH_ALL],
1069
1039
  ): this {
1070
1040
  const pubkey =
@@ -1098,7 +1068,7 @@ export class Psbt {
1098
1068
  #signTaprootInput(
1099
1069
  inputIndex: number,
1100
1070
  input: PsbtInput,
1101
- keyPair: Signer | SignerAlternative | HDSigner | BIP32Interface | UniversalSigner,
1071
+ keyPair: Signer | HDSigner,
1102
1072
  tapLeafHashToSign?: Uint8Array,
1103
1073
  allowedSighashTypes: number[] = [Transaction.SIGHASH_DEFAULT],
1104
1074
  ): this {
@@ -1153,16 +1123,9 @@ export class Psbt {
1153
1123
  return this;
1154
1124
  }
1155
1125
 
1156
- #signInputAsync(
1126
+ async #signInputAsync(
1157
1127
  inputIndex: number,
1158
- keyPair:
1159
- | Signer
1160
- | SignerAlternative
1161
- | SignerAsync
1162
- | HDSigner
1163
- | HDSignerAsync
1164
- | BIP32Interface
1165
- | UniversalSigner,
1128
+ keyPair: Signer | SignerAsync | HDSigner | HDSignerAsync,
1166
1129
  sighashTypes: number[] = [Transaction.SIGHASH_ALL],
1167
1130
  ): Promise<void> {
1168
1131
  const pubkey =
@@ -1177,31 +1140,23 @@ export class Psbt {
1177
1140
  sighashTypes,
1178
1141
  );
1179
1142
 
1180
- return Promise.resolve(keyPair.sign(hash)).then((signature) => {
1181
- const sig = signature instanceof Uint8Array ? signature : new Uint8Array(signature);
1182
- const partialSig = [
1183
- {
1184
- pubkey,
1185
- signature: bscript.signature.encode(sig, sighashType),
1186
- },
1187
- ];
1143
+ const signature = await keyPair.sign(hash);
1144
+ const sig = signature instanceof Uint8Array ? signature : new Uint8Array(signature);
1145
+ const partialSig = [
1146
+ {
1147
+ pubkey,
1148
+ signature: bscript.signature.encode(sig, sighashType),
1149
+ },
1150
+ ];
1188
1151
 
1189
- this.data.updateInput(inputIndex, { partialSig });
1190
- this.#cache.hasSignatures = true;
1191
- });
1152
+ this.data.updateInput(inputIndex, { partialSig });
1153
+ this.#cache.hasSignatures = true;
1192
1154
  }
1193
1155
 
1194
1156
  async #signTaprootInputAsync(
1195
1157
  inputIndex: number,
1196
1158
  input: PsbtInput,
1197
- keyPair:
1198
- | Signer
1199
- | SignerAlternative
1200
- | SignerAsync
1201
- | HDSigner
1202
- | HDSignerAsync
1203
- | BIP32Interface
1204
- | UniversalSigner,
1159
+ keyPair: Signer | SignerAsync | HDSigner | HDSignerAsync,
1205
1160
  tapLeafHash?: Uint8Array,
1206
1161
  sighashTypes: number[] = [Transaction.SIGHASH_DEFAULT],
1207
1162
  ): Promise<void> {
@@ -1225,42 +1180,29 @@ export class Psbt {
1225
1180
  keyPair.signSchnorr as (hash: MessageHash) => SchnorrSignature | Promise<SchnorrSignature>
1226
1181
  ).bind(keyPair);
1227
1182
 
1228
- type TapSignatureResult = { tapKeySig: Uint8Array } | { tapScriptSig: TapScriptSig[] };
1229
- const signaturePromises: Promise<TapSignatureResult>[] = [];
1230
-
1231
- const tapKeyHash = hashesForSig.filter((h) => !h.leafHash)[0];
1183
+ const tapKeyHash = hashesForSig.find((h) => !h.leafHash);
1232
1184
  if (tapKeyHash) {
1233
- const tapKeySigPromise = Promise.resolve(signSchnorr(tapKeyHash.hash)).then((sig) => {
1234
- return {
1235
- tapKeySig: serializeTaprootSignature(sig, input.sighashType),
1236
- };
1237
- });
1238
- signaturePromises.push(tapKeySigPromise);
1185
+ const sig = await signSchnorr(tapKeyHash.hash);
1186
+ const tapKeySig = serializeTaprootSignature(sig, input.sighashType);
1187
+ this.data.updateInput(inputIndex, { tapKeySig });
1188
+ this.#cache.hasSignatures = true;
1239
1189
  }
1240
1190
 
1241
1191
  const tapScriptHashes = hashesForSig.filter(
1242
1192
  (h): h is typeof h & { leafHash: Bytes32 } => !!h.leafHash,
1243
1193
  );
1244
1194
  if (tapScriptHashes.length) {
1245
- const tapScriptSigPromises = tapScriptHashes.map(async (tsh) => {
1246
- const signature = await signSchnorr(tsh.hash);
1247
-
1248
- const tapScriptSig: TapScriptSig[] = [
1249
- {
1195
+ const tapScriptSigs = await Promise.all(
1196
+ tapScriptHashes.map(async (tsh) => {
1197
+ const signature = await signSchnorr(tsh.hash);
1198
+ return {
1250
1199
  pubkey: toXOnly(pubkey),
1251
1200
  signature: serializeTaprootSignature(signature, input.sighashType),
1252
1201
  leafHash: tsh.leafHash,
1253
- },
1254
- ];
1255
-
1256
- return { tapScriptSig };
1257
- });
1258
- signaturePromises.push(...tapScriptSigPromises);
1259
- }
1260
-
1261
- const results = await Promise.all(signaturePromises);
1262
- for (const v of results) {
1263
- this.data.updateInput(inputIndex, v as PsbtInputUpdate);
1202
+ } as TapScriptSig;
1203
+ }),
1204
+ );
1205
+ this.data.updateInput(inputIndex, { tapScriptSig: tapScriptSigs });
1264
1206
  this.#cache.hasSignatures = true;
1265
1207
  }
1266
1208
  }
package/src/types.ts CHANGED
@@ -17,10 +17,6 @@ import type {
17
17
  XOnlyPublicKey,
18
18
  } from './branded.js';
19
19
 
20
- // ============================================================================
21
- // Branded Types (re-exported from branded.ts to avoid circular dependencies)
22
- // ============================================================================
23
-
24
20
  export type {
25
21
  Bytes32,
26
22
  Bytes20,
@@ -34,22 +30,15 @@ export type {
34
30
  Script,
35
31
  } from './branded.js';
36
32
 
37
- // ============================================================================
38
- // Constants
39
- // ============================================================================
40
-
41
33
  /** @internal Do not mutate */
42
34
  const EC_P = fromHex('fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f');
35
+
43
36
  /** @internal Do not mutate — secp256k1 curve order */
44
37
  const EC_N = fromHex('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141');
45
38
 
46
39
  export const SATOSHI_MAX = 21n * 10n ** 14n;
47
40
  export const TAPLEAF_VERSION_MASK = 0xfe;
48
41
 
49
- // ============================================================================
50
- // Type Guards
51
- // ============================================================================
52
-
53
42
  export function isUInt8(value: unknown): value is number {
54
43
  return typeof value === 'number' && Number.isInteger(value) && value >= 0 && value <= 0xff;
55
44
  }
@@ -96,8 +85,7 @@ export function isBytes20(value: unknown): value is Bytes20 {
96
85
  export function isXOnlyPublicKey(value: unknown): value is XOnlyPublicKey {
97
86
  if (!(value instanceof Uint8Array) || value.length !== 32) return false;
98
87
  if (isZero(value)) return false;
99
- if (compare(value, EC_P) >= 0) return false;
100
- return true;
88
+ return compare(value, EC_P) < 0;
101
89
  }
102
90
 
103
91
  export function isPoint(value: unknown): value is PublicKey {
@@ -130,8 +118,7 @@ export function isSatoshi(value: unknown): value is Satoshi {
130
118
  export function isPrivateKey(value: unknown): value is PrivateKey {
131
119
  if (!(value instanceof Uint8Array) || value.length !== 32) return false;
132
120
  if (isZero(value)) return false;
133
- if (compare(value, EC_N) >= 0) return false;
134
- return true;
121
+ return compare(value, EC_N) < 0;
135
122
  }
136
123
 
137
124
  export function isSchnorrSignature(value: unknown): value is SchnorrSignature {
@@ -146,9 +133,7 @@ export function isScript(value: unknown): value is Script {
146
133
  return value instanceof Uint8Array;
147
134
  }
148
135
 
149
- // ============================================================================
150
136
  // Taproot Types
151
- // ============================================================================
152
137
 
153
138
  export interface Tapleaf {
154
139
  readonly output: Uint8Array;
@@ -178,23 +163,17 @@ export function isTaptree(value: unknown): value is Taptree {
178
163
  return value.every((node: unknown) => isTaptree(node));
179
164
  }
180
165
 
181
- // ============================================================================
182
- // ECC Interface (re-exported from ecc/types.ts for backward compatibility)
183
- // ============================================================================
166
+ // ECC Interface
184
167
 
185
- export type { XOnlyPointAddTweakResult, EccLib, Parity } from './ecc/types.js';
168
+ export type { CryptoBackend, XOnlyPointAddTweakResult, EccLib, Parity } from './ecc/types.js';
186
169
 
187
- // ============================================================================
188
170
  // Stack Types
189
- // ============================================================================
190
171
 
191
172
  export type StackElement = Uint8Array | number;
192
173
  export type Stack = readonly StackElement[];
193
174
  export type StackFunction = () => Stack;
194
175
 
195
- // ============================================================================
196
176
  // Utility Functions
197
- // ============================================================================
198
177
 
199
178
  export function stacksEqual(a: Uint8Array[], b: Uint8Array[]): boolean {
200
179
  if (a.length !== b.length) return false;
@@ -236,9 +215,7 @@ export function toSatoshi(value: bigint): Satoshi {
236
215
  return value as Satoshi;
237
216
  }
238
217
 
239
- // ============================================================================
240
218
  // Assertion Helpers
241
- // ============================================================================
242
219
 
243
220
  export function assertXOnlyPublicKey(
244
221
  value: unknown,
@@ -353,6 +353,15 @@ export class NodeWorkerSigningPool {
353
353
  }
354
354
  }
355
355
 
356
+ /**
357
+ * Disposes of the pool by shutting down all workers.
358
+ *
359
+ * Enables `await using pool = ...` syntax for automatic cleanup.
360
+ */
361
+ public async [Symbol.asyncDispose](): Promise<void> {
362
+ await this.shutdown();
363
+ }
364
+
356
365
  /**
357
366
  * Shuts down the pool and terminates all workers.
358
367
  *
@@ -384,7 +393,7 @@ export class NodeWorkerSigningPool {
384
393
  */
385
394
  #createWorkerScript(): string {
386
395
  // Node.js worker_threads can directly require/import modules
387
- const workerCode = `
396
+ return `
388
397
  const { parentPort } = require('worker_threads');
389
398
 
390
399
  /**
@@ -649,7 +658,6 @@ function handleSignBatch(msg) {
649
658
  });
650
659
  }
651
660
  `;
652
- return workerCode;
653
661
  }
654
662
 
655
663
  /**
@@ -0,0 +1,190 @@
1
+ /**
2
+ * Sequential signing pool for environments without worker support (React Native).
3
+ *
4
+ * Signs inputs one-by-one on the main thread using the CryptoBackend
5
+ * from EccContext. Same API shape as WorkerSigningPool so consumers
6
+ * can swap transparently.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+
11
+ import type {
12
+ ParallelSignerKeyPair,
13
+ ParallelSigningResult,
14
+ SigningResultMessage,
15
+ SigningTask,
16
+ WorkerPoolConfig,
17
+ } from './types.js';
18
+ import { SignatureType } from './types.js';
19
+ import { EccContext } from '../ecc/context.js';
20
+ import type { MessageHash, PrivateKey } from '@btc-vision/ecpair';
21
+
22
+ /**
23
+ * Sequential signing pool — signs inputs one-by-one on the main thread.
24
+ *
25
+ * Provides the same public API as WorkerSigningPool but without any
26
+ * threading. Intended for React Native or other environments where
27
+ * Web Workers and worker_threads are unavailable.
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * import { SequentialSigningPool } from '@btc-vision/bitcoin/workers';
32
+ *
33
+ * const pool = SequentialSigningPool.getInstance();
34
+ * const result = await pool.signBatch(tasks, keyPair);
35
+ * ```
36
+ */
37
+ export class SequentialSigningPool {
38
+ static #instance: SequentialSigningPool | null = null;
39
+
40
+ private constructor(_config: WorkerPoolConfig = {}) {
41
+ // Config accepted for API compatibility but not used —
42
+ // sequential pool has no workers to configure.
43
+ }
44
+
45
+ /**
46
+ * Number of workers — always 0 for sequential pool.
47
+ */
48
+ public get workerCount(): number {
49
+ return 0;
50
+ }
51
+
52
+ /**
53
+ * Idle workers — always 0.
54
+ */
55
+ public get idleWorkerCount(): number {
56
+ return 0;
57
+ }
58
+
59
+ /**
60
+ * Busy workers — always 0.
61
+ */
62
+ public get busyWorkerCount(): number {
63
+ return 0;
64
+ }
65
+
66
+ /**
67
+ * Whether workers are preserved — always false (no workers to preserve).
68
+ */
69
+ public get isPreservingWorkers(): boolean {
70
+ return false;
71
+ }
72
+
73
+ /**
74
+ * Gets the singleton instance.
75
+ */
76
+ public static getInstance(config?: WorkerPoolConfig): SequentialSigningPool {
77
+ if (!SequentialSigningPool.#instance) {
78
+ SequentialSigningPool.#instance = new SequentialSigningPool(config);
79
+ }
80
+ return SequentialSigningPool.#instance;
81
+ }
82
+
83
+ /**
84
+ * Resets the singleton instance (for testing).
85
+ */
86
+ public static resetInstance(): void {
87
+ SequentialSigningPool.#instance = null;
88
+ }
89
+
90
+ /**
91
+ * No-op — no workers to preserve.
92
+ */
93
+ public preserveWorkers(): void {
94
+ // No-op: no workers to preserve
95
+ }
96
+
97
+ /**
98
+ * No-op — no workers to release.
99
+ */
100
+ public releaseWorkers(): void {
101
+ // No-op
102
+ }
103
+
104
+ /**
105
+ * No-op — no initialization required.
106
+ */
107
+ public async initialize(): Promise<void> {
108
+ // No-op: nothing to initialize
109
+ }
110
+
111
+ /**
112
+ * Signs tasks sequentially on the main thread.
113
+ */
114
+ // eslint-disable-next-line @typescript-eslint/require-await
115
+ public async signBatch(
116
+ tasks: readonly SigningTask[],
117
+ keyPair: ParallelSignerKeyPair,
118
+ ): Promise<ParallelSigningResult> {
119
+ const startTime = performance.now();
120
+
121
+ if (tasks.length === 0) {
122
+ return {
123
+ success: true,
124
+ signatures: new Map(),
125
+ errors: new Map(),
126
+ durationMs: performance.now() - startTime,
127
+ };
128
+ }
129
+
130
+ const ecc = EccContext.get().lib;
131
+ const privateKey = keyPair.getPrivateKey();
132
+
133
+ const signatures = new Map<number, SigningResultMessage>();
134
+ const errors = new Map<number, string>();
135
+
136
+ try {
137
+ for (const task of tasks) {
138
+ try {
139
+ let signature: Uint8Array;
140
+
141
+ const hash = task.hash as MessageHash;
142
+ const key = privateKey as PrivateKey;
143
+
144
+ if (task.signatureType === SignatureType.Schnorr) {
145
+ signature = ecc.signSchnorr!(hash, key);
146
+ } else {
147
+ signature = ecc.sign(hash, key);
148
+ }
149
+
150
+ signatures.set(task.inputIndex, {
151
+ type: 'result',
152
+ taskId: task.taskId,
153
+ signature,
154
+ inputIndex: task.inputIndex,
155
+ publicKey: keyPair.publicKey,
156
+ signatureType: task.signatureType,
157
+ leafHash: task.leafHash,
158
+ });
159
+ } catch (err: unknown) {
160
+ const message = err instanceof Error ? err.message : 'Signing failed';
161
+ errors.set(task.inputIndex, message);
162
+ }
163
+ }
164
+ } finally {
165
+ // SECURITY: Zero the private key
166
+ privateKey.fill(0);
167
+ }
168
+
169
+ return {
170
+ success: errors.size === 0,
171
+ signatures,
172
+ errors,
173
+ durationMs: performance.now() - startTime,
174
+ };
175
+ }
176
+
177
+ /**
178
+ * No-op — no workers to shut down.
179
+ */
180
+ public async shutdown(): Promise<void> {
181
+ // No-op: nothing to shut down
182
+ }
183
+
184
+ /**
185
+ * Enables `await using pool = ...` syntax.
186
+ */
187
+ public async [Symbol.asyncDispose](): Promise<void> {
188
+ await this.shutdown();
189
+ }
190
+ }
@@ -389,6 +389,15 @@ export class WorkerSigningPool {
389
389
  }
390
390
  }
391
391
 
392
+ /**
393
+ * Disposes of the pool by shutting down all workers.
394
+ *
395
+ * Enables `await using pool = ...` syntax for automatic cleanup.
396
+ */
397
+ public async [Symbol.asyncDispose](): Promise<void> {
398
+ await this.shutdown();
399
+ }
400
+
392
401
  /**
393
402
  * Shuts down the pool and terminates all workers.
394
403
  *