@bsv/sdk 1.6.20 → 1.6.23

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
 
@@ -668,12 +668,12 @@ export interface ListOutputsArgs {
668
668
  includeTags?: BooleanDefaultFalse;
669
669
  includeLabels?: BooleanDefaultFalse;
670
670
  limit?: PositiveIntegerDefault10Max10000;
671
- offset?: PositiveIntegerOrZero;
671
+ offset?: number;
672
672
  seekPermission?: BooleanDefaultTrue;
673
673
  }
674
674
  ```
675
675
 
676
- See also: [BasketStringUnder300Bytes](./wallet.md#type-basketstringunder300bytes), [BooleanDefaultFalse](./wallet.md#type-booleandefaultfalse), [BooleanDefaultTrue](./wallet.md#type-booleandefaulttrue), [OutputTagStringUnder300Bytes](./wallet.md#type-outputtagstringunder300bytes), [PositiveIntegerDefault10Max10000](./wallet.md#type-positiveintegerdefault10max10000), [PositiveIntegerOrZero](./wallet.md#type-positiveintegerorzero)
676
+ See also: [BasketStringUnder300Bytes](./wallet.md#type-basketstringunder300bytes), [BooleanDefaultFalse](./wallet.md#type-booleandefaultfalse), [BooleanDefaultTrue](./wallet.md#type-booleandefaulttrue), [OutputTagStringUnder300Bytes](./wallet.md#type-outputtagstringunder300bytes), [PositiveIntegerDefault10Max10000](./wallet.md#type-positiveintegerdefault10max10000)
677
677
 
678
678
  Links: [API](#api), [Interfaces](#interfaces), [Classes](#classes), [Functions](#functions), [Types](#types), [Enums](#enums), [Variables](#variables)
679
679
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/sdk",
3
- "version": "1.6.20",
3
+ "version": "1.6.23",
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
  }
@@ -563,7 +563,10 @@ export interface InternalizeActionResult {
563
563
  * @param {BooleanDefaultFalse} [includeTags] - Whether the tags associated with the output should be returned.
564
564
  * @param {BooleanDefaultFalse} [includeLabels] - Whether the labels associated with the transaction containing the output should be returned.
565
565
  * @param {PositiveIntegerDefault10Max10000} [limit] - Optional limit on the number of outputs to return.
566
- * @param {PositiveIntegerOrZero} [offset] - Number of outputs to skip before starting to return results.
566
+ * @param {number} [offset] - If positive or zero: Number of outputs to skip before starting to return results, oldest first.
567
+ * If negative: Outputs are returned newest first and offset of -1 is the newest output.
568
+ * When using negative offsets, caution is required as new outputs may be added between calls,
569
+ * potentially causing outputs to be duplicated across calls.
567
570
  * @param {BooleanDefaultTrue} [seekPermission] — Whether to seek permission from the user for this operation if required. Default true, will return an error rather than proceed if set to false.
568
571
  */
569
572
  export interface ListOutputsArgs {
@@ -575,7 +578,7 @@ export interface ListOutputsArgs {
575
578
  includeTags?: BooleanDefaultFalse
576
579
  includeLabels?: BooleanDefaultFalse
577
580
  limit?: PositiveIntegerDefault10Max10000
578
- offset?: PositiveIntegerOrZero
581
+ offset?: number
579
582
  seekPermission?: BooleanDefaultTrue
580
583
  }
581
584