@bsv/sdk 1.6.18 → 1.6.20

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 (64) hide show
  1. package/README.md +11 -11
  2. package/dist/cjs/package.json +5 -9
  3. package/dist/cjs/src/auth/transports/SimplifiedFetchTransport.js +39 -39
  4. package/dist/cjs/src/auth/transports/SimplifiedFetchTransport.js.map +1 -1
  5. package/dist/cjs/src/primitives/ECDSA.js +69 -167
  6. package/dist/cjs/src/primitives/ECDSA.js.map +1 -1
  7. package/dist/cjs/src/primitives/Hash.js +660 -436
  8. package/dist/cjs/src/primitives/Hash.js.map +1 -1
  9. package/dist/cjs/src/primitives/Point.js +285 -293
  10. package/dist/cjs/src/primitives/Point.js.map +1 -1
  11. package/dist/cjs/src/script/ScriptEvaluationError.js +27 -0
  12. package/dist/cjs/src/script/ScriptEvaluationError.js.map +1 -0
  13. package/dist/cjs/src/script/Spend.js +13 -7
  14. package/dist/cjs/src/script/Spend.js.map +1 -1
  15. package/dist/cjs/src/script/index.js +3 -1
  16. package/dist/cjs/src/script/index.js.map +1 -1
  17. package/dist/cjs/src/transaction/Beef.js +4 -4
  18. package/dist/cjs/src/transaction/Transaction.js +3 -3
  19. package/dist/cjs/src/transaction/Transaction.js.map +1 -1
  20. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  21. package/dist/esm/src/auth/transports/SimplifiedFetchTransport.js +39 -39
  22. package/dist/esm/src/auth/transports/SimplifiedFetchTransport.js.map +1 -1
  23. package/dist/esm/src/primitives/ECDSA.js +69 -167
  24. package/dist/esm/src/primitives/ECDSA.js.map +1 -1
  25. package/dist/esm/src/primitives/Hash.js +672 -444
  26. package/dist/esm/src/primitives/Hash.js.map +1 -1
  27. package/dist/esm/src/primitives/Point.js +268 -293
  28. package/dist/esm/src/primitives/Point.js.map +1 -1
  29. package/dist/esm/src/script/ScriptEvaluationError.js +33 -0
  30. package/dist/esm/src/script/ScriptEvaluationError.js.map +1 -0
  31. package/dist/esm/src/script/Spend.js +14 -8
  32. package/dist/esm/src/script/Spend.js.map +1 -1
  33. package/dist/esm/src/script/index.js +1 -0
  34. package/dist/esm/src/script/index.js.map +1 -1
  35. package/dist/esm/src/transaction/Beef.js +4 -4
  36. package/dist/esm/src/transaction/Transaction.js +3 -3
  37. package/dist/esm/src/transaction/Transaction.js.map +1 -1
  38. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  39. package/dist/types/src/auth/transports/SimplifiedFetchTransport.d.ts +1 -1
  40. package/dist/types/src/auth/transports/SimplifiedFetchTransport.d.ts.map +1 -1
  41. package/dist/types/src/primitives/ECDSA.d.ts.map +1 -1
  42. package/dist/types/src/primitives/Hash.d.ts +12 -19
  43. package/dist/types/src/primitives/Hash.d.ts.map +1 -1
  44. package/dist/types/src/primitives/Point.d.ts +37 -5
  45. package/dist/types/src/primitives/Point.d.ts.map +1 -1
  46. package/dist/types/src/script/ScriptEvaluationError.d.ts +24 -0
  47. package/dist/types/src/script/ScriptEvaluationError.d.ts.map +1 -0
  48. package/dist/types/src/script/Spend.d.ts.map +1 -1
  49. package/dist/types/src/script/index.d.ts +1 -0
  50. package/dist/types/src/script/index.d.ts.map +1 -1
  51. package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
  52. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  53. package/dist/umd/bundle.js +20 -1
  54. package/dist/umd/bundle.js.map +1 -0
  55. package/package.json +5 -9
  56. package/src/auth/transports/SimplifiedFetchTransport.ts +64 -67
  57. package/src/primitives/ECDSA.ts +80 -222
  58. package/src/primitives/Hash.ts +752 -589
  59. package/src/primitives/Point.ts +277 -336
  60. package/src/script/ScriptEvaluationError.ts +44 -0
  61. package/src/script/Spend.ts +14 -12
  62. package/src/script/index.ts +1 -0
  63. package/src/transaction/Beef.ts +4 -4
  64. package/src/transaction/Transaction.ts +11 -3
@@ -2,7 +2,206 @@ import BasePoint from './BasePoint.js'
2
2
  import JPoint from './JacobianPoint.js'
3
3
  import BigNumber from './BigNumber.js'
4
4
  import { toArray, toHex } from './utils.js'
5
- import ReductionContext from './ReductionContext.js'
5
+
6
+ // -----------------------------------------------------------------------------
7
+ // BigInt helpers & constants (secp256k1) – hoisted so we don't recreate them on
8
+ // every Point.mul() call.
9
+ // -----------------------------------------------------------------------------
10
+ export const BI_ZERO = 0n
11
+ export const BI_ONE = 1n
12
+ export const BI_TWO = 2n
13
+ export const BI_THREE = 3n
14
+ export const BI_FOUR = 4n
15
+ export const BI_EIGHT = 8n
16
+
17
+ export const P_BIGINT = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2Fn
18
+ export const N_BIGINT = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141n
19
+ export const MASK_256 = (1n << 256n) - 1n // 0xffff…ffff (256 sones)
20
+
21
+ export function red (x: bigint): bigint {
22
+ // first fold
23
+ let hi = x >> 256n
24
+ x = (x & MASK_256) + (hi << 32n) + hi * 977n
25
+
26
+ // second fold (hi ≤ 2³² + 977 here, so one more pass is enough)
27
+ hi = x >> 256n
28
+ x = (x & MASK_256) + (hi << 32n) + hi * 977n
29
+
30
+ // final conditional subtraction
31
+ if (x >= P_BIGINT) x -= P_BIGINT
32
+ return x
33
+ }
34
+
35
+ export const biMod = (a: bigint): bigint => red((a % P_BIGINT + P_BIGINT) % P_BIGINT)
36
+ export const biModSub = (a: bigint, b: bigint): bigint => (a >= b ? a - b : P_BIGINT - (b - a))
37
+ export const biModMul = (a: bigint, b: bigint): bigint => red(a * b)
38
+ export const biModAdd = (a: bigint, b: bigint): bigint => red(a + b)
39
+ export const biModInv = (a: bigint): bigint => { // binary‑ext GCD
40
+ let lm = BI_ONE; let hm = BI_ZERO; let low = biMod(a); let high = P_BIGINT
41
+ while (low > BI_ONE) { const r = high / low; [lm, hm] = [hm - lm * r, lm]; [low, high] = [high - low * r, low] }
42
+ return biMod(lm)
43
+ }
44
+ export const biModSqr = (a: bigint): bigint => biModMul(a, a)
45
+
46
+ export const biModPow = (base: bigint, exp: bigint): bigint => {
47
+ let result = BI_ONE
48
+ base = biMod(base)
49
+ let e = exp
50
+ while (e > BI_ZERO) {
51
+ if ((e & BI_ONE) === BI_ONE) result = biModMul(result, base)
52
+ base = biModMul(base, base)
53
+ e >>= BI_ONE
54
+ }
55
+ return result
56
+ }
57
+
58
+ export const P_PLUS1_DIV4 = (P_BIGINT + 1n) >> 2n
59
+
60
+ export const biModSqrt = (a: bigint): bigint | null => {
61
+ const r = biModPow(a, P_PLUS1_DIV4)
62
+ return biModMul(r, r) === biMod(a) ? r : null
63
+ }
64
+
65
+ const toBigInt = (x: BigNumber | number | number[] | string): bigint => {
66
+ if (BigNumber.isBN(x)) return BigInt('0x' + (x as BigNumber).toString(16))
67
+ if (typeof x === 'string') return BigInt('0x' + x)
68
+ if (Array.isArray(x)) return BigInt('0x' + toHex(x))
69
+ return BigInt(x as number)
70
+ }
71
+
72
+ // Generator point coordinates as bigint constants
73
+ export const GX_BIGINT = BigInt('0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')
74
+ export const GY_BIGINT = BigInt('0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8')
75
+
76
+ // Cache for precomputed windowed tables keyed by 'window:x:y'
77
+ const WNAF_TABLE_CACHE: Map<string, JacobianPointBI[]> = new Map()
78
+
79
+ export interface JacobianPointBI { X: bigint, Y: bigint, Z: bigint }
80
+
81
+ export const jpDouble = (P: JacobianPointBI): JacobianPointBI => {
82
+ const { X: X1, Y: Y1, Z: Z1 } = P
83
+ if (Y1 === BI_ZERO) return { X: BI_ZERO, Y: BI_ONE, Z: BI_ZERO }
84
+
85
+ const Y1sq = biModMul(Y1, Y1)
86
+ const S = biModMul(BI_FOUR, biModMul(X1, Y1sq))
87
+ const M = biModMul(BI_THREE, biModMul(X1, X1))
88
+ const X3 = biModSub(biModMul(M, M), biModMul(BI_TWO, S))
89
+ const Y3 = biModSub(
90
+ biModMul(M, biModSub(S, X3)),
91
+ biModMul(BI_EIGHT, biModMul(Y1sq, Y1sq))
92
+ )
93
+ const Z3 = biModMul(BI_TWO, biModMul(Y1, Z1))
94
+ return { X: X3, Y: Y3, Z: Z3 }
95
+ }
96
+
97
+ export const jpAdd = (P: JacobianPointBI, Q: JacobianPointBI): JacobianPointBI => {
98
+ if (P.Z === BI_ZERO) return Q
99
+ if (Q.Z === BI_ZERO) return P
100
+
101
+ const Z1Z1 = biModMul(P.Z, P.Z)
102
+ const Z2Z2 = biModMul(Q.Z, Q.Z)
103
+ const U1 = biModMul(P.X, Z2Z2)
104
+ const U2 = biModMul(Q.X, Z1Z1)
105
+ const S1 = biModMul(P.Y, biModMul(Z2Z2, Q.Z))
106
+ const S2 = biModMul(Q.Y, biModMul(Z1Z1, P.Z))
107
+
108
+ const H = biModSub(U2, U1)
109
+ const r = biModSub(S2, S1)
110
+ if (H === BI_ZERO) {
111
+ if (r === BI_ZERO) return jpDouble(P)
112
+ return { X: BI_ZERO, Y: BI_ONE, Z: BI_ZERO } // Infinity
113
+ }
114
+
115
+ const HH = biModMul(H, H)
116
+ const HHH = biModMul(H, HH)
117
+ const V = biModMul(U1, HH)
118
+
119
+ const X3 = biModSub(biModSub(biModMul(r, r), HHH), biModMul(BI_TWO, V))
120
+ const Y3 = biModSub(biModMul(r, biModSub(V, X3)), biModMul(S1, HHH))
121
+ const Z3 = biModMul(H, biModMul(P.Z, Q.Z))
122
+ return { X: X3, Y: Y3, Z: Z3 }
123
+ }
124
+
125
+ export const jpNeg = (P: JacobianPointBI): JacobianPointBI => {
126
+ if (P.Z === BI_ZERO) return P
127
+ return { X: P.X, Y: P_BIGINT - P.Y, Z: P.Z }
128
+ }
129
+
130
+ // Fast windowed-NAF scalar multiplication (default window = 5) in Jacobian
131
+ // coordinates. Returns Q = k * P0 as a JacobianPoint.
132
+ export const scalarMultiplyWNAF = (
133
+ k: bigint,
134
+ P0: { x: bigint, y: bigint },
135
+ window: number = 5
136
+ ): JacobianPointBI => {
137
+ const key = `${window}:${P0.x.toString(16)}:${P0.y.toString(16)}`
138
+ let tbl = WNAF_TABLE_CACHE.get(key)
139
+ let P: JacobianPointBI
140
+ if (tbl === undefined) {
141
+ // Convert affine to Jacobian and pre-compute odd multiples
142
+ const tblSize = 1 << (window - 1) // e.g. w=5 → 16 entries
143
+ tbl = new Array(tblSize)
144
+ P = { X: P0.x, Y: P0.y, Z: BI_ONE }
145
+ tbl[0] = P
146
+ const twoP = jpDouble(P)
147
+ for (let i = 1; i < tblSize; i++) {
148
+ tbl[i] = jpAdd(tbl[i - 1], twoP)
149
+ }
150
+ WNAF_TABLE_CACHE.set(key, tbl)
151
+ } else {
152
+ P = tbl[0]
153
+ }
154
+
155
+ // Build wNAF representation of k
156
+ const wnaf: number[] = []
157
+ const wBig = 1n << BigInt(window)
158
+ const wHalf = wBig >> 1n
159
+ let kTmp = k
160
+ while (kTmp > 0n) {
161
+ if ((kTmp & BI_ONE) === BI_ZERO) {
162
+ wnaf.push(0)
163
+ kTmp >>= BI_ONE
164
+ } else {
165
+ let z = kTmp & (wBig - 1n) // kTmp mod 2^w
166
+ if (z > wHalf) z -= wBig // make it odd & within (-2^{w-1}, 2^{w-1})
167
+ wnaf.push(Number(z))
168
+ kTmp -= z
169
+ kTmp >>= BI_ONE
170
+ }
171
+ }
172
+
173
+ // Accumulate from MSB to LSB
174
+ let Q: JacobianPointBI = { X: BI_ZERO, Y: BI_ONE, Z: BI_ZERO } // infinity
175
+ for (let i = wnaf.length - 1; i >= 0; i--) {
176
+ Q = jpDouble(Q)
177
+ const di = wnaf[i]
178
+ if (di !== 0) {
179
+ const idx = Math.abs(di) >> 1 // (|di|-1)/2 because di is odd
180
+ const addend = di > 0 ? tbl[idx] : jpNeg(tbl[idx])
181
+ Q = jpAdd(Q, addend)
182
+ }
183
+ }
184
+ return Q
185
+ }
186
+
187
+ export const modN = (a: bigint): bigint => {
188
+ let r = a % N_BIGINT
189
+ if (r < 0n) r += N_BIGINT
190
+ return r
191
+ }
192
+ export const modMulN = (a: bigint, b: bigint): bigint => modN(a * b)
193
+
194
+ /** modular inverse modulo n with plain extended‑gcd (not constant‑time) */
195
+ export const modInvN = (a: bigint): bigint => {
196
+ let lm = 1n; let hm = 0n
197
+ let low = modN(a); let high = N_BIGINT
198
+ while (low > 1n) {
199
+ const q = high / low
200
+ ;[lm, hm] = [hm - lm * q, lm]
201
+ ;[low, high] = [high - low * q, low]
202
+ }
203
+ return modN(lm)
204
+ }
6
205
 
7
206
  /**
8
207
  * `Point` class is a representation of an elliptic curve point with affine coordinates.
@@ -17,10 +216,6 @@ import ReductionContext from './ReductionContext.js'
17
216
  * @property inf - Flag to record if the point is at infinity in the Elliptic Curve.
18
217
  */
19
218
  export default class Point extends BasePoint {
20
- private static readonly red: any = new ReductionContext('k256')
21
- private static readonly a: BigNumber = new BigNumber(0).toRed(Point.red)
22
- private static readonly b: BigNumber = new BigNumber(7).toRed(Point.red)
23
- private static readonly zero: BigNumber = new BigNumber(0).toRed(Point.red)
24
219
  x: BigNumber | null
25
220
  y: BigNumber | null
26
221
  inf: boolean
@@ -95,13 +290,6 @@ export default class Point extends BasePoint {
95
290
  return Point.fromDER(bytes)
96
291
  }
97
292
 
98
- static redSqrtOptimized (y2: BigNumber): BigNumber {
99
- const red = Point.red
100
- const p = red.m // The modulus
101
- const exponent = p.addn(1).iushrn(2) // (p + 1) / 4
102
- return y2.redPow(exponent)
103
- }
104
-
105
293
  /**
106
294
  * Generates a point from an x coordinate and a boolean indicating whether the corresponding
107
295
  * y coordinate is odd.
@@ -118,66 +306,17 @@ export default class Point extends BasePoint {
118
306
  * const point = Point.fromX(xCoordinate, true);
119
307
  */
120
308
  static fromX (x: BigNumber | number | number[] | string, odd: boolean): Point {
121
- function mod (a: bigint, n: bigint): bigint {
122
- return ((a % n) + n) % n
123
- }
124
- function modPow (base: bigint, exponent: bigint, modulus: bigint): bigint {
125
- let result = BigInt(1)
126
- base = mod(base, modulus)
127
- while (exponent > BigInt(0)) {
128
- if ((exponent & BigInt(1)) === BigInt(1)) {
129
- result = mod(result * base, modulus)
130
- }
131
- exponent >>= BigInt(1)
132
- base = mod(base * base, modulus)
133
- }
134
- return result
135
- }
136
- function sqrtMod (a: bigint, p: bigint): bigint | null {
137
- const exponent = (p + BigInt(1)) >> BigInt(2)
138
- const sqrtCandidate = modPow(a, exponent, p)
139
- if (mod(sqrtCandidate * sqrtCandidate, p) === mod(a, p)) {
140
- return sqrtCandidate
141
- } else {
142
- return null
143
- }
144
- }
145
-
146
- // Curve parameters for secp256k1
147
- const p = BigInt(
148
- '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F'
149
- )
150
- const b = BigInt(7)
151
-
152
- let xBigInt: bigint
153
- if (x instanceof BigNumber) {
154
- xBigInt = BigInt('0x' + x.toString(16))
155
- } else if (typeof x === 'string') {
156
- xBigInt = BigInt('0x' + x)
157
- } else if (Array.isArray(x)) {
158
- xBigInt = BigInt('0x' + toHex(x).padStart(64, '0'))
159
- } else if (typeof x === 'number') {
160
- xBigInt = BigInt(x)
161
- } else {
162
- throw new Error('Invalid x-coordinate type')
163
- }
164
-
165
- xBigInt = mod(xBigInt, p)
166
-
167
- const y2 = mod(modPow(xBigInt, BigInt(3), p) + b, p)
168
-
169
- let y = sqrtMod(y2, p)
170
- if (y === null) {
171
- throw new Error('Invalid point')
172
- }
173
-
174
- const isYOdd = y % BigInt(2) === BigInt(1)
175
- if ((odd && !isYOdd) || (!odd && isYOdd)) {
176
- y = p - y
309
+ let xBigInt = toBigInt(x)
310
+ xBigInt = biMod(xBigInt)
311
+ const y2 = biModAdd(biModMul(biModSqr(xBigInt), xBigInt), 7n)
312
+ const y = biModSqrt(y2)
313
+ if (y === null) throw new Error('Invalid point')
314
+ let yBig = y
315
+ if ((yBig & BI_ONE) !== (odd ? BI_ONE : BI_ZERO)) {
316
+ yBig = biModSub(P_BIGINT, yBig)
177
317
  }
178
-
179
318
  const xBN = new BigNumber(xBigInt.toString(16), 16)
180
- const yBN = new BigNumber(y.toString(16), 16)
319
+ const yBN = new BigNumber(yBig.toString(16), 16)
181
320
  return new Point(xBN, yBN)
182
321
  }
183
322
 
@@ -448,25 +587,31 @@ export default class Point extends BasePoint {
448
587
 
449
588
  // P + (-P) = O
450
589
  if (this.neg().eq(p)) {
451
- return new Point(new BigNumber(0), new BigNumber(0))
590
+ return new Point(null, null)
452
591
  }
453
592
 
454
593
  // P + Q = O
455
594
  if (this.x?.cmp(p.x ?? new BigNumber(0)) === 0) {
456
- return new Point(new BigNumber(0), new BigNumber(0))
595
+ return new Point(null, null)
457
596
  }
458
597
 
459
- let c = this.y?.redSub(p.y ?? new BigNumber(0)) ?? new BigNumber(0)
460
- if (c.cmpn(0) !== 0) {
461
- c = c.redMul(this.x?.redSub(p.x ?? new BigNumber(0)).redInvm() ?? new BigNumber(1))
598
+ const P1 = {
599
+ X: BigInt('0x' + (this.x as BigNumber).fromRed().toString(16)),
600
+ Y: BigInt('0x' + (this.y as BigNumber).fromRed().toString(16)),
601
+ Z: BI_ONE
462
602
  }
463
-
464
- const nx = c?.redSqr().redISub(this.x ?? new BigNumber(0)).redISub(p.x ?? new BigNumber(0))
465
- const ny = (c ?? new BigNumber(1))
466
- .redMul((this.x ?? new BigNumber(0)).redSub(nx ?? new BigNumber(0)))
467
- .redISub(this.y ?? new BigNumber(0))
468
-
469
- return new Point(nx ?? new BigNumber(0), ny ?? new BigNumber(0))
603
+ const Q1 = {
604
+ X: BigInt('0x' + (p.x as BigNumber).fromRed().toString(16)),
605
+ Y: BigInt('0x' + (p.y as BigNumber).fromRed().toString(16)),
606
+ Z: BI_ONE
607
+ }
608
+ const R = jpAdd(P1, Q1)
609
+ if (R.Z === BI_ZERO) return new Point(null, null)
610
+ const zInv = biModInv(R.Z)
611
+ const zInv2 = biModMul(zInv, zInv)
612
+ const xRes = biModMul(R.X, zInv2)
613
+ const yRes = biModMul(R.Y, biModMul(zInv2, zInv))
614
+ return new Point(xRes.toString(16), yRes.toString(16))
470
615
  }
471
616
 
472
617
  /**
@@ -479,25 +624,21 @@ export default class Point extends BasePoint {
479
624
  * const result = P.dbl();
480
625
  * */
481
626
  dbl (): Point {
482
- if (this.inf) {
483
- return this
484
- }
485
-
486
- // 2P = O
487
- const ys1 = (this.y ?? new BigNumber(0)).redAdd(this.y ?? new BigNumber(0))
488
- if (ys1.cmpn(0) === 0) {
489
- return new Point(new BigNumber(0), new BigNumber(0))
627
+ if (this.inf) return this
628
+ if (this.x === null || this.y === null) {
629
+ throw new Error('Point coordinates cannot be null')
490
630
  }
491
631
 
492
- const a = this.curve.a
493
- const x2 = (this.x ?? new BigNumber(0)).redSqr()
494
- const dyinv = ys1.redInvm()
495
- const c = x2.redAdd(x2).redIAdd(x2).redIAdd(a).redMul(dyinv)
632
+ const X = BigInt('0x' + this.x.fromRed().toString(16))
633
+ const Y = BigInt('0x' + this.y.fromRed().toString(16))
634
+ if (Y === BI_ZERO) return new Point(null, null)
496
635
 
497
- const nx = c.redSqr().redISub((this.x ?? new BigNumber(0)).redAdd(this.x ?? new BigNumber(0)))
498
- const ny = c.redMul((this.x ?? new BigNumber(0)).redSub(nx)).redISub(this.y ?? new BigNumber(0))
499
-
500
- return new Point(nx, ny)
636
+ const R = jpDouble({ X, Y, Z: BI_ONE })
637
+ const zInv = biModInv(R.Z)
638
+ const zInv2 = biModMul(zInv, zInv)
639
+ const xRes = biModMul(R.X, zInv2)
640
+ const yRes = biModMul(R.Y, biModMul(zInv2, zInv))
641
+ return new Point(xRes.toString(16), yRes.toString(16))
501
642
  }
502
643
 
503
644
  /**
@@ -538,102 +679,48 @@ export default class Point extends BasePoint {
538
679
  k = new BigNumber(k as number, 16)
539
680
  }
540
681
  k = k as BigNumber
541
- if (typeof BigInt === 'function') {
542
- if (this.inf) {
543
- return this
544
- }
545
-
546
- const zero = 0n
547
- const one = 1n
548
- const p = BigInt(
549
- '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F'
550
- )
551
- const n = BigInt(
552
- '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141'
553
- )
554
-
555
- let kBig = BigInt('0x' + k.toString(16))
556
- const isNeg = kBig < zero
557
- if (isNeg) kBig = -kBig
558
- kBig = ((kBig % n) + n) % n
559
- if (kBig === zero) {
560
- return new Point(null, null)
561
- }
562
-
563
- if (this.x === null || this.y === null) {
564
- throw new Error('Point coordinates cannot be null')
565
- }
566
-
567
- let Px: bigint
568
- let Py: bigint
569
- if (this === this.curve.g) {
570
- Px = GX_BIGINT
571
- Py = GY_BIGINT
572
- } else {
573
- Px = BigInt('0x' + this.x.fromRed().toString(16))
574
- Py = BigInt('0x' + this.y.fromRed().toString(16))
575
- }
576
-
577
- const mod = (a: bigint, m: bigint): bigint => ((a % m) + m) % m
578
- const modMul = (a: bigint, b: bigint, m: bigint): bigint => mod(a * b, m)
579
- const modInv = (a: bigint, m: bigint): bigint => {
580
- let lm = one
581
- let hm = zero
582
- let low = mod(a, m)
583
- let high = m
584
- while (low > one) {
585
- const r = high / low
586
- const nm = hm - lm * r
587
- const neww = high - low * r
588
- hm = lm
589
- lm = nm
590
- high = low
591
- low = neww
592
- }
593
- return mod(lm, m)
594
- }
682
+ if (this.inf) {
683
+ return this
684
+ }
595
685
 
596
- interface JacobianPoint {
597
- X: bigint
598
- Y: bigint
599
- Z: bigint
600
- }
686
+ let kBig = BigInt('0x' + k.toString(16))
687
+ const isNeg = kBig < BI_ZERO
688
+ if (isNeg) kBig = -kBig
689
+ kBig = biMod(kBig)
690
+ if (kBig === BI_ZERO) {
691
+ return new Point(null, null)
692
+ }
601
693
 
602
- const scalarMultiply = (
603
- kVal: bigint,
604
- P0: { x: bigint, y: bigint }
605
- ): JacobianPoint => {
606
- // Delegate to the hoisted windowed-NAF implementation above. We
607
- // keep the wrapper so that the rest of the mul() code remains
608
- // untouched while providing a massive speed-up (≈4-6×).
609
- return scalarMultiplyWNAF(kVal, P0) as unknown as JacobianPoint
610
- }
694
+ if (this.x === null || this.y === null) {
695
+ throw new Error('Point coordinates cannot be null')
696
+ }
611
697
 
612
- const R = scalarMultiply(kBig, { x: Px, y: Py })
613
- if (R.Z === zero) {
614
- return new Point(null, null)
615
- }
616
- const zInv = modInv(R.Z, p)
617
- const zInv2 = modMul(zInv, zInv, p)
618
- const xRes = modMul(R.X, zInv2, p)
619
- const yRes = modMul(R.Y, modMul(zInv2, zInv, p), p)
620
-
621
- const xBN = new BigNumber(xRes.toString(16), 16)
622
- const yBN = new BigNumber(yRes.toString(16), 16)
623
- const result = new Point(xBN, yBN)
624
- if (isNeg) {
625
- return result.neg()
626
- }
627
- return result
698
+ let Px: bigint
699
+ let Py: bigint
700
+ if (this === this.curve.g) {
701
+ Px = GX_BIGINT
702
+ Py = GY_BIGINT
628
703
  } else {
629
- if (this.isInfinity()) {
630
- return this
631
- } else if (this._hasDoubles(k)) {
632
- return this._fixedNafMul(k)
633
- } else {
634
- return this._endoWnafMulAdd([this], [k]) as Point
635
- }
704
+ Px = BigInt('0x' + this.x.fromRed().toString(16))
705
+ Py = BigInt('0x' + this.y.fromRed().toString(16))
706
+ }
707
+
708
+ const R = scalarMultiplyWNAF(kBig, { x: Px, y: Py })
709
+ if (R.Z === BI_ZERO) {
710
+ return new Point(null, null)
711
+ }
712
+ const zInv = biModInv(R.Z)
713
+ const zInv2 = biModMul(zInv, zInv)
714
+ const xRes = biModMul(R.X, zInv2)
715
+ const yRes = biModMul(R.Y, biModMul(zInv2, zInv))
716
+
717
+ const xBN = new BigNumber(xRes.toString(16), 16)
718
+ const yBN = new BigNumber(yRes.toString(16), 16)
719
+ const result = new Point(xBN, yBN)
720
+ if (isNeg) {
721
+ return result.neg()
636
722
  }
723
+ return result
637
724
  }
638
725
 
639
726
  /**
@@ -1013,7 +1100,7 @@ export default class Point extends BasePoint {
1013
1100
  for (i = 0; i < points.length; i++) {
1014
1101
  const split = this.curve._endoSplit(coeffs[i])
1015
1102
  let p = points[i]
1016
- let beta: Point = p._getBeta() ?? new Point(new BigNumber(0), new BigNumber(0))
1103
+ let beta: Point = p._getBeta() ?? new Point(null, null)
1017
1104
 
1018
1105
  if (split.k1.negative !== 0) {
1019
1106
  split.k1.ineg()
@@ -1107,149 +1194,3 @@ export default class Point extends BasePoint {
1107
1194
  }
1108
1195
  }
1109
1196
  }
1110
-
1111
- // -----------------------------------------------------------------------------
1112
- // BigInt helpers & constants (secp256k1) – hoisted so we don't recreate them on
1113
- // every Point.mul() call.
1114
- // -----------------------------------------------------------------------------
1115
- const BI_ZERO = 0n
1116
- const BI_ONE = 1n
1117
- const BI_TWO = 2n
1118
- const BI_THREE = 3n
1119
- const BI_FOUR = 4n
1120
- const BI_EIGHT = 8n
1121
-
1122
- const P_BIGINT = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2Fn
1123
- const MASK_256 = (1n << 256n) - 1n // 0xffff…ffff (256 sones)
1124
-
1125
- function red (x: bigint): bigint {
1126
- // first fold
1127
- let hi = x >> 256n
1128
- x = (x & MASK_256) + (hi << 32n) + hi * 977n
1129
-
1130
- // second fold (hi ≤ 2³² + 977 here, so one more pass is enough)
1131
- hi = x >> 256n
1132
- x = (x & MASK_256) + (hi << 32n) + hi * 977n
1133
-
1134
- // final conditional subtraction
1135
- if (x >= P_BIGINT) x -= P_BIGINT
1136
- return x
1137
- }
1138
-
1139
- const biModSub = (a: bigint, b: bigint): bigint => (a >= b ? a - b : P_BIGINT - (b - a))
1140
- const biModMul = (a: bigint, b: bigint): bigint => red(a * b)
1141
-
1142
- // Generator point coordinates as bigint constants
1143
- const GX_BIGINT = BigInt('0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')
1144
- const GY_BIGINT = BigInt('0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8')
1145
-
1146
- // Cache for precomputed windowed tables keyed by 'window:x:y'
1147
- const WNAF_TABLE_CACHE: Map<string, JacobianPointBI[]> = new Map()
1148
-
1149
- interface JacobianPointBI { X: bigint, Y: bigint, Z: bigint }
1150
-
1151
- const jpDouble = (P: JacobianPointBI): JacobianPointBI => {
1152
- const { X: X1, Y: Y1, Z: Z1 } = P
1153
- if (Y1 === BI_ZERO) return { X: BI_ZERO, Y: BI_ONE, Z: BI_ZERO }
1154
-
1155
- const Y1sq = biModMul(Y1, Y1)
1156
- const S = biModMul(BI_FOUR, biModMul(X1, Y1sq))
1157
- const M = biModMul(BI_THREE, biModMul(X1, X1))
1158
- const X3 = biModSub(biModMul(M, M), biModMul(BI_TWO, S))
1159
- const Y3 = biModSub(
1160
- biModMul(M, biModSub(S, X3)),
1161
- biModMul(BI_EIGHT, biModMul(Y1sq, Y1sq))
1162
- )
1163
- const Z3 = biModMul(BI_TWO, biModMul(Y1, Z1))
1164
- return { X: X3, Y: Y3, Z: Z3 }
1165
- }
1166
-
1167
- const jpAdd = (P: JacobianPointBI, Q: JacobianPointBI): JacobianPointBI => {
1168
- if (P.Z === BI_ZERO) return Q
1169
- if (Q.Z === BI_ZERO) return P
1170
-
1171
- const Z1Z1 = biModMul(P.Z, P.Z)
1172
- const Z2Z2 = biModMul(Q.Z, Q.Z)
1173
- const U1 = biModMul(P.X, Z2Z2)
1174
- const U2 = biModMul(Q.X, Z1Z1)
1175
- const S1 = biModMul(P.Y, biModMul(Z2Z2, Q.Z))
1176
- const S2 = biModMul(Q.Y, biModMul(Z1Z1, P.Z))
1177
-
1178
- const H = biModSub(U2, U1)
1179
- const r = biModSub(S2, S1)
1180
- if (H === BI_ZERO) {
1181
- if (r === BI_ZERO) return jpDouble(P)
1182
- return { X: BI_ZERO, Y: BI_ONE, Z: BI_ZERO } // Infinity
1183
- }
1184
-
1185
- const HH = biModMul(H, H)
1186
- const HHH = biModMul(H, HH)
1187
- const V = biModMul(U1, HH)
1188
-
1189
- const X3 = biModSub(biModSub(biModMul(r, r), HHH), biModMul(BI_TWO, V))
1190
- const Y3 = biModSub(biModMul(r, biModSub(V, X3)), biModMul(S1, HHH))
1191
- const Z3 = biModMul(H, biModMul(P.Z, Q.Z))
1192
- return { X: X3, Y: Y3, Z: Z3 }
1193
- }
1194
-
1195
- const jpNeg = (P: JacobianPointBI): JacobianPointBI => {
1196
- if (P.Z === BI_ZERO) return P
1197
- return { X: P.X, Y: P_BIGINT - P.Y, Z: P.Z }
1198
- }
1199
-
1200
- // Fast windowed-NAF scalar multiplication (default window = 5) in Jacobian
1201
- // coordinates. Returns Q = k * P0 as a JacobianPoint.
1202
- const scalarMultiplyWNAF = (
1203
- k: bigint,
1204
- P0: { x: bigint, y: bigint },
1205
- window: number = 5
1206
- ): JacobianPointBI => {
1207
- const key = `${window}:${P0.x.toString(16)}:${P0.y.toString(16)}`
1208
- let tbl = WNAF_TABLE_CACHE.get(key)
1209
- let P: JacobianPointBI
1210
- if (tbl === undefined) {
1211
- // Convert affine to Jacobian and pre-compute odd multiples
1212
- const tblSize = 1 << (window - 1) // e.g. w=5 → 16 entries
1213
- tbl = new Array(tblSize)
1214
- P = { X: P0.x, Y: P0.y, Z: BI_ONE }
1215
- tbl[0] = P
1216
- const twoP = jpDouble(P)
1217
- for (let i = 1; i < tblSize; i++) {
1218
- tbl[i] = jpAdd(tbl[i - 1], twoP)
1219
- }
1220
- WNAF_TABLE_CACHE.set(key, tbl)
1221
- } else {
1222
- P = tbl[0]
1223
- }
1224
-
1225
- // Build wNAF representation of k
1226
- const wnaf: number[] = []
1227
- const wBig = 1n << BigInt(window)
1228
- const wHalf = wBig >> 1n
1229
- let kTmp = k
1230
- while (kTmp > 0n) {
1231
- if ((kTmp & BI_ONE) === BI_ZERO) {
1232
- wnaf.push(0)
1233
- kTmp >>= BI_ONE
1234
- } else {
1235
- let z = kTmp & (wBig - 1n) // kTmp mod 2^w
1236
- if (z > wHalf) z -= wBig // make it odd & within (-2^{w-1}, 2^{w-1})
1237
- wnaf.push(Number(z))
1238
- kTmp -= z
1239
- kTmp >>= BI_ONE
1240
- }
1241
- }
1242
-
1243
- // Accumulate from MSB to LSB
1244
- let Q: JacobianPointBI = { X: BI_ZERO, Y: BI_ONE, Z: BI_ZERO } // infinity
1245
- for (let i = wnaf.length - 1; i >= 0; i--) {
1246
- Q = jpDouble(Q)
1247
- const di = wnaf[i]
1248
- if (di !== 0) {
1249
- const idx = Math.abs(di) >> 1 // (|di|-1)/2 because di is odd
1250
- const addend = di > 0 ? tbl[idx] : jpNeg(tbl[idx])
1251
- Q = jpAdd(Q, addend)
1252
- }
1253
- }
1254
- return Q
1255
- }