@bsv/sdk 1.6.20 → 1.6.22

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.
@@ -87,6 +87,7 @@ Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](
87
87
  | [PushDrop](#class-pushdrop) |
88
88
  | [RPuzzle](#class-rpuzzle) |
89
89
  | [Script](#class-script) |
90
+ | [ScriptEvaluationError](#class-scriptevaluationerror) |
90
91
  | [Spend](#class-spend) |
91
92
  | [UnlockingScript](#class-unlockingscript) |
92
93
 
@@ -730,6 +731,37 @@ Argument Details
730
731
 
731
732
  Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Enums](#enums), [Variables](#variables)
732
733
 
734
+ ---
735
+ ### Class: ScriptEvaluationError
736
+
737
+ ```ts
738
+ export default class ScriptEvaluationError extends Error {
739
+ txid: string;
740
+ outputIndex: number;
741
+ context: "UnlockingScript" | "LockingScript";
742
+ programCounter: number;
743
+ stackState: number[][];
744
+ altStackState: number[][];
745
+ ifStackState: boolean[];
746
+ stackMem: number;
747
+ altStackMem: number;
748
+ constructor(params: {
749
+ message: string;
750
+ txid: string;
751
+ outputIndex: number;
752
+ context: "UnlockingScript" | "LockingScript";
753
+ programCounter: number;
754
+ stackState: number[][];
755
+ altStackState: number[][];
756
+ ifStackState: boolean[];
757
+ stackMem: number;
758
+ altStackMem: number;
759
+ })
760
+ }
761
+ ```
762
+
763
+ Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Enums](#enums), [Variables](#variables)
764
+
733
765
  ---
734
766
  ### Class: Spend
735
767
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/sdk",
3
- "version": "1.6.20",
3
+ "version": "1.6.22",
4
4
  "type": "module",
5
5
  "description": "BSV Blockchain Software Development Kit",
6
6
  "main": "dist/cjs/mod.js",
@@ -4,7 +4,7 @@ import PublicKey from './PublicKey.js'
4
4
  import Point from './Point.js'
5
5
  import Curve from './Curve.js'
6
6
  import { sign, verify } from './ECDSA.js'
7
- import { sha256, sha256hmac } from './Hash.js'
7
+ import { sha256, sha256hmac, sha512hmac } from './Hash.js'
8
8
  import Random from './Random.js'
9
9
  import { fromBase58Check, toArray, toBase58Check } from './utils.js'
10
10
  import Polynomial, { PointInFiniteField } from './Polynomial.js'
@@ -410,8 +410,40 @@ export default class PrivateKey extends BigNumber {
410
410
  const poly = Polynomial.fromPrivateKey(this, threshold)
411
411
 
412
412
  const points: PointInFiniteField[] = []
413
+ const usedXCoordinates = new Set<string>()
414
+ const curve = new Curve()
415
+
416
+ /**
417
+ * Cryptographically secure x-coordinate generation for Shamir's Secret Sharing (toKeyShares)
418
+ *
419
+ * - Each x-coordinate is derived using a master seed (Random(64)) as the HMAC key and a per-attempt counter array as the message.
420
+ * - The counter array includes the share index, the attempt number (to handle rare collisions), and 32 bytes of fresh randomness for each attempt.
421
+ * - This ensures:
422
+ * 1. **Non-determinism**: Each split is unique, even for the same key and parameters, due to the per-attempt randomness.
423
+ * 2. **Uniqueness**: x-coordinates are checked for zero and duplication; retry logic ensures no repeats or invalid values.
424
+ * 3. **Cryptographic strength**: HMAC-SHA-512 is robust, and combining deterministic and random values protects against RNG compromise or bias.
425
+ * 4. **Defensive programming**: Attempts are capped (5 per share) to prevent infinite loops in pathological cases.
426
+ *
427
+ * This approach is robust against all practical attacks and is suitable for high-security environments where deterministic splits are not desired.
428
+ */
429
+ const seed = Random(64)
413
430
  for (let i = 0; i < totalShares; i++) {
414
- const x = new BigNumber(PrivateKey.fromRandom().toArray())
431
+ let x: BigNumber
432
+ let attempts = 0
433
+ do {
434
+ // To ensure no two points are ever the same, even if the system RNG is compromised,
435
+ // we'll use a different counter value for each point and use SHA-512 HMAC.
436
+ const counter = [i, attempts, ...Random(32)]
437
+ const h = sha512hmac(seed, counter)
438
+ x = new BigNumber(h).umod(curve.p)
439
+ // repeat generation if x is zero or has already been used (insanely unlikely)
440
+ attempts++
441
+ if (attempts > 5) {
442
+ throw new Error('Failed to generate unique x coordinate after 5 attempts')
443
+ }
444
+ } while (x.isZero() || usedXCoordinates.has(x.toString()))
445
+
446
+ usedXCoordinates.add(x.toString())
415
447
  const y = poly.valueAt(x)
416
448
  points.push(new PointInFiniteField(x, y))
417
449
  }