/** Alias to Uint8Array. */
export type Bytes = Uint8Array
/** Signature instance, which allows recovering pubkey from it. */
-export type RecoveredSignature = Secp256k1['Signature'] & { recovery: number }
+export type RecoveredSignature = Signature & { recovery: number }
/** Weierstrass elliptic curve options. */
export type WeierstrassOpts<T> = Readonly<{
p: bigint
type MaybePromise<T> = T | Promise<T>
+type Point = InstanceType<typeof Secp256k1.Point>
+type Signature = InstanceType<typeof Secp256k1.Signature>
+
export class Secp256k1 {
/**
* Curve params. secp256k1 is short weierstrass / koblitz curve. Equation is y² == x³ + ax + b.
/** Point in 3d xyz projective coordinates. 3d takes less inversions than 2d. */
static Point = class {
- static BASE: Secp256k1['Point']
- static ZERO: Secp256k1['Point']
+ static BASE: Point
+ static ZERO: Point
readonly X: bigint
readonly Y: bigint
readonly Z: bigint
return Secp256k1.secp256k1_CURVE
}
/** Create 3d xyz point from 2d xy. (0, 0) => (0, 1, 0), not (0, 0, 1) */
- static fromAffine (ap: AffinePoint): Secp256k1['Point'] {
+ static fromAffine (ap: AffinePoint): Point {
const { x, y } = ap
- return x === 0n && y === 0n ? Secp256k1.I : new Secp256k1.Point(x, y, 1n)
+ return x === 0n && y === 0n ? Secp256k1.I : new this(x, y, 1n)
}
/** Convert Uint8Array or hex string to Point. */
- static fromBytes (bytes: Bytes): Secp256k1['Point'] {
+ static fromBytes (bytes: Bytes): Point {
Secp256k1.abytes(bytes)
const { publicKey: comp, publicKeyUncompressed: uncomp } = Secp256k1.lengths // e.g. for 32-byte: 33, 65
- let p: Secp256k1['Point'] | undefined = undefined
+ let p: Point | undefined = undefined
const length = bytes.length
const head = bytes[0]
const tail = bytes.subarray(1)
// Validate point
return p ? p.assertValidity() : Secp256k1.err('bad point: not on curve')
}
- static fromHex (hex: string): Secp256k1['Point'] {
+ static fromHex (hex: string): Point {
return Secp256k1.Point.fromBytes(Secp256k1.hexToBytes(hex))
}
get x (): bigint {
return this.toAffine().y
}
/** Equality check: compare points P&Q. */
- equals (other: Secp256k1['Point']): boolean {
+ equals (other: Point): boolean {
const { X: X1, Y: Y1, Z: Z1 } = this
const { X: X2, Y: Y2, Z: Z2 } = Secp256k1.apoint(other) // checks class equality
const X1Z2 = Secp256k1.M(X1 * Z2)
return this.equals(Secp256k1.I)
}
/** Flip point over y coordinate. */
- negate (): Secp256k1['Point'] {
+ negate (): Point {
return new Secp256k1.Point(this.X, Secp256k1.M(-this.Y), this.Z)
}
/** Point doubling: P+P, complete formula. */
- double (): Secp256k1['Point'] {
+ double (): Point {
return this.add(this)
}
/**
* Cost: `12M + 0S + 3*a + 3*b3 + 23add`.
*/
// prettier-ignore
- add (other: Secp256k1['Point']): Secp256k1['Point'] {
+ add (other: Point): Point {
const { X: X1, Y: Y1, Z: Z1 } = this
const { X: X2, Y: Y2, Z: Z2 } = Secp256k1.apoint(other)
const a = 0n
Z3 = Secp256k1.M(Z3 + t0) // step 40
return new Secp256k1.Point(X3, Y3, Z3)
}
- subtract (other: Secp256k1['Point']): Secp256k1['Point'] {
+ subtract (other: Point): Point {
return this.add(Secp256k1.apoint(other).negate())
}
/**
* @param n scalar by which point is multiplied
* @param safe safe mode guards against timing attacks; unsafe mode is faster
*/
- multiply (n: bigint, safe = true): Secp256k1['Point'] {
+ multiply (n: bigint, safe = true): Point {
if (!safe && n === 0n) return Secp256k1.I
Secp256k1.FnIsValidNot0(n)
if (n === 1n) return this
// init result point & fake point
let p = Secp256k1.I
let f = Secp256k1.G
- for (let d: Secp256k1['Point'] = this; n > 0n; d = d.double(), n >>= 1n) {
+ for (let d: Point = this; n > 0n; d = d.double(), n >>= 1n) {
// if bit is present, add to point
// if not present, add to fake, for timing safety
if (n & 1n) p = p.add(d)
}
return p
}
- multiplyUnsafe (scalar: bigint): Secp256k1['Point'] {
+ multiplyUnsafe (scalar: bigint): Point {
return this.multiply(scalar, false)
}
/** Convert point to 2d xy affine point. (X, Y, Z) ∋ (x=X/Z, y=Y/Z) */
return { x: Secp256k1.M(x * iz), y: Secp256k1.M(y * iz) }
}
/** Checks if the point is valid and on-curve. */
- assertValidity (): Secp256k1['Point'] {
+ assertValidity (): Point {
const { x, y } = this.toAffine() // convert to 2d xy affine point.
Secp256k1.FpIsValidNot0(x) // must be in range 1 <= x,y < P
Secp256k1.FpIsValidNot0(y)
}
/** Generator / base point */
- static G: Secp256k1['Point'] = new this.Point(this.Gx, this.Gy, 1n)
+ static G: Point = new this.Point(this.Gx, this.Gy, 1n)
/** Identity / zero point */
- static I: Secp256k1['Point'] = new this.Point(0n, 1n, 0n)
+ static I: Point = new this.Point(0n, 1n, 0n)
// Static aliases
static {
this.Point.BASE = this.G
this.Point.ZERO = this.I
}
/** `Q = u1⋅G + u2⋅R`. Verifies Q is not ZERO. Unsafe: non-CT. */
- static doubleScalarMulUns = (R: Secp256k1['Point'], u1: bigint, u2: bigint): Secp256k1['Point'] => {
+ static doubleScalarMulUns = (R: Point, u1: bigint, u2: bigint): Point => {
return this.G.multiply(u1, false).add(R.multiply(u2, false)).assertValidity()
}
static bytesToNumBE = (b: Bytes): bigint => this.big('0x' + (this.bytesToHex(b) || '0'))
if (recovery != null) this.recovery = recovery
Object.freeze(this)
}
- static fromBytes (b: Bytes, format: ECDSASignatureFormat = Secp256k1.SIG_COMPACT): Secp256k1['Signature'] {
+ static fromBytes (b: Bytes, format: ECDSASignatureFormat = Secp256k1.SIG_COMPACT): Signature {
Secp256k1.assertSigLength(b, format)
let rec: number | undefined
if (format === Secp256k1.SIG_RECOVERED) {
static pwindows = Math.ceil(this.scalarBits / this.W) + 1 // 33 for W=8, NOT 32 - see wNAF loop
static pwindowSize = 2 ** (this.W - 1) // 128 for W=8
static precompute = () => {
- const points: Secp256k1['Point'][] = []
+ const points: Point[] = []
let p = this.G
let b = p
for (let w = 0; w < this.pwindows; w++) {
}
return points
}
- static Gpows: Secp256k1['Point'][] | undefined = undefined // precomputes for base point G
+ static Gpows: Point[] | undefined = undefined // precomputes for base point G
// const-time negate
- static ctneg = (cnd: boolean, p: Secp256k1['Point']) => {
+ static ctneg = (cnd: boolean, p: Point) => {
const n = p.negate()
return cnd ? n : p
}
*
* !! Precomputes can be disabled by commenting-out call of the wNAF() inside Point#multiply().
*/
- static wNAF = (n: bigint): { p: Secp256k1['Point']; f: Secp256k1['Point'] } => {
+ static wNAF = (n: bigint): { p: Point; f: Point } => {
const comp = this.Gpows || (this.Gpows = this.precompute())
let p = this.I
let f = this.G // f must be G, or could become I in the end