]> git.codecow.com Git - libnemo.git/commitdiff
Progress on classification.
authorChris Duncan <chris@zoso.dev>
Sun, 30 Nov 2025 07:53:58 +0000 (23:53 -0800)
committerChris Duncan <chris@zoso.dev>
Sun, 30 Nov 2025 07:53:58 +0000 (23:53 -0800)
src/lib/crypto/secp256k1.ts

index 254ab8104569252a2a7e521747e1a181ac722159..d928ce808211617106b09f21b5ac80190888b41b 100644 (file)
@@ -1,9 +1,5 @@
 /*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */
 
-import { Point, Signature, getPublicKey, hashes } from "@noble/secp256k1"
-import { randomBytes } from "crypto"
-import { _verify } from "../wallet/verify"
-
 /**
  * 5KB JS implementation of secp256k1 ECDSA / Schnorr signatures & ECDH.
  * Compliant with RFC6979 & BIP340.
@@ -277,15 +273,15 @@ export class Secp256k1 {
                        return Secp256k1.secp256k1_CURVE
                }
                /** Create 3d xyz point from 2d xy. (0, 0) => (0, 1, 0), not (0, 0, 1) */
-               static fromAffine (ap: AffinePoint): Point {
+               static fromAffine (ap: AffinePoint): Secp256k1['Point'] {
                        const { x, y } = ap
                        return x === 0n && y === 0n ? Secp256k1.I : new Secp256k1.Point(x, y, 1n)
                }
                /** Convert Uint8Array or hex string to Point. */
-               static fromBytes (bytes: Bytes): Point {
+               static fromBytes (bytes: Bytes): Secp256k1['Point'] {
                        Secp256k1.abytes(bytes)
                        const { publicKey: comp, publicKeyUncompressed: uncomp } = Secp256k1.lengths // e.g. for 32-byte: 33, 65
-                       let p: Point | undefined = undefined
+                       let p: Secp256k1['Point'] | undefined = undefined
                        const length = bytes.length
                        const head = bytes[0]
                        const tail = bytes.subarray(1)
@@ -298,14 +294,14 @@ export class Secp256k1 {
                                const evenY = Secp256k1.isEven(y)
                                const evenH = Secp256k1.isEven(Secp256k1.big(head))
                                if (evenH !== evenY) y = Secp256k1.M(-y)
-                               p = new Point(x, y, 1n)
+                               p = new this(x, y, 1n)
                        }
                        // Uncompressed 65-byte point, 0x04 prefix
-                       if (length === uncomp && head === 0x04) p = new Point(x, Secp256k1.sliceBytesNumBE(tail, Secp256k1.L, Secp256k1.L2), 1n)
+                       if (length === uncomp && head === 0x04) p = new this(x, Secp256k1.sliceBytesNumBE(tail, Secp256k1.L, Secp256k1.L2), 1n)
                        // Validate point
                        return p ? p.assertValidity() : Secp256k1.err('bad point: not on curve')
                }
-               static fromHex (hex: string): Point {
+               static fromHex (hex: string): Secp256k1['Point'] {
                        return Secp256k1.Point.fromBytes(Secp256k1.hexToBytes(hex))
                }
                get x (): bigint {
@@ -315,7 +311,7 @@ export class Secp256k1 {
                        return this.toAffine().y
                }
                /** Equality check: compare points P&Q. */
-               equals (other: Point): boolean {
+               equals (other: Secp256k1['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)
@@ -328,11 +324,11 @@ export class Secp256k1 {
                        return this.equals(Secp256k1.I)
                }
                /** Flip point over y coordinate. */
-               negate (): Point {
-                       return new Point(this.X, Secp256k1.M(-this.Y), this.Z)
+               negate (): Secp256k1['Point'] {
+                       return new Secp256k1.Point(this.X, Secp256k1.M(-this.Y), this.Z)
                }
                /** Point doubling: P+P, complete formula. */
-               double (): Point {
+               double (): Secp256k1['Point'] {
                        return this.add(this)
                }
                /**
@@ -341,7 +337,7 @@ export class Secp256k1 {
                 * Cost: `12M + 0S + 3*a + 3*b3 + 23add`.
                 */
                // prettier-ignore
-               add (other: Point): Point {
+               add (other: Secp256k1['Point']): Secp256k1['Point'] {
                        const { X: X1, Y: Y1, Z: Z1 } = this
                        const { X: X2, Y: Y2, Z: Z2 } = Secp256k1.apoint(other)
                        const a = 0n
@@ -364,9 +360,9 @@ export class Secp256k1 {
                        t0 = Secp256k1.M(t5 * t4) // step 35
                        X3 = Secp256k1.M(t3 * X3); X3 = Secp256k1.M(X3 - t0); t0 = Secp256k1.M(t3 * t1); Z3 = Secp256k1.M(t5 * Z3)
                        Z3 = Secp256k1.M(Z3 + t0) // step 40
-                       return new Point(X3, Y3, Z3)
+                       return new Secp256k1.Point(X3, Y3, Z3)
                }
-               subtract (other: Point): Point {
+               subtract (other: Secp256k1['Point']): Secp256k1['Point'] {
                        return this.add(Secp256k1.apoint(other).negate())
                }
                /**
@@ -376,15 +372,15 @@ export class Secp256k1 {
                 * @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): Point {
+               multiply (n: bigint, safe = true): Secp256k1['Point'] {
                        if (!safe && n === 0n) return Secp256k1.I
                        Secp256k1.FnIsValidNot0(n)
                        if (n === 1n) return this
-                       if (this.equals(Secp256k1.G)) return wNAF(n).p
+                       if (this.equals(Secp256k1.G)) return Secp256k1.wNAF(n).p
                        // init result point & fake point
                        let p = Secp256k1.I
                        let f = Secp256k1.G
-                       for (let d: Point = this; n > 0n; d = d.double(), n >>= 1n) {
+                       for (let d: Secp256k1['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)
@@ -392,7 +388,7 @@ export class Secp256k1 {
                        }
                        return p
                }
-               multiplyUnsafe (scalar: bigint): Point {
+               multiplyUnsafe (scalar: bigint): Secp256k1['Point'] {
                        return this.multiply(scalar, false)
                }
                /** Convert point to 2d xy affine point. (X, Y, Z) ∋ (x=X/Z, y=Y/Z) */
@@ -408,7 +404,7 @@ export class Secp256k1 {
                        return { x: Secp256k1.M(x * iz), y: Secp256k1.M(y * iz) }
                }
                /** Checks if the point is valid and on-curve. */
-               assertValidity (): Point {
+               assertValidity (): Secp256k1['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)
@@ -427,17 +423,18 @@ export class Secp256k1 {
                        return Secp256k1.bytesToHex(this.toBytes(isCompressed))
                }
        }
+
        /** Generator / base point */
-       static G: Point = new Point(this.Gx, this.Gy, 1n)
+       static G: Secp256k1['Point'] = new this.Point(this.Gx, this.Gy, 1n)
        /** Identity / zero point */
-       static I: Point = new Point(0n, 1n, 0n)
+       static I: Secp256k1['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: Point, u1: bigint, u2: bigint): Point => {
+       static doubleScalarMulUns = (R: Secp256k1['Point'], u1: bigint, u2: bigint): Secp256k1['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'))
@@ -535,10 +532,10 @@ export class Secp256k1 {
                        }
                        const r = Secp256k1.sliceBytesNumBE(b, 0, Secp256k1.L)
                        const s = Secp256k1.sliceBytesNumBE(b, Secp256k1.L, Secp256k1.L2)
-                       return new Signature(r, s, rec)
+                       return new this(r, s, rec)
                }
                addRecoveryBit (bit: number): RecoveredSignature {
-                       return new Signature(this.r, this.s, bit) as RecoveredSignature
+                       return new Secp256k1.Signature(this.r, this.s, bit) as RecoveredSignature
                }
                hasHighS (): boolean {
                        return Secp256k1.highS(this.s)
@@ -643,7 +640,7 @@ export class Secp256k1 {
                        k.fill(0)
                }
                // h = hmac(K || V || ...)
-               const h = (...b: Bytes[]) => hashes.hmacSha256Async(k, this.concatBytes(v, ...b))
+               const h = (...b: Bytes[]) => this.hashes.hmacSha256Async(k, this.concatBytes(v, ...b))
                const reseed = async (seed = this.NULL) => {
                        // HMAC-DRBG reseed() function. Steps D-G
                        k = await h(this.byte0, seed) // k = hmac(K || V || 0x00 || seed)
@@ -676,19 +673,19 @@ export class Secp256k1 {
        ): T => {
                let { lowS, extraEntropy } = opts // generates low-s sigs by default
                // RFC6979 3.2: we skip step A
-               const int2octets = numTo32b // int to octets
-               const h1i = bits2int_modN(messageHash) // msg bigint
+               const int2octets = this.numTo32b // int to octets
+               const h1i = this.bits2int_modN(messageHash) // msg bigint
                const h1o = int2octets(h1i) // msg octets
-               const d = secretKeyToScalar(secretKey) // validate private key, convert to bigint
+               const d = this.secretKeyToScalar(secretKey) // validate private key, convert to bigint
                const seedArgs = [int2octets(d), h1o] // Step D of RFC6979 3.2
                /** RFC6979 3.6: additional k' (optional). See {@link ECDSAExtraEntropy}. */
                if (extraEntropy != null && extraEntropy !== false) {
                        // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
                        // gen random bytes OR pass as-is
-                       const e = extraEntropy === true ? randomBytes(L) : extraEntropy
-                       seedArgs.push(abytes(e, undefined, 'extraEntropy')) // check for being bytes
+                       const e = extraEntropy === true ? this.randomBytes(this.L) : extraEntropy
+                       seedArgs.push(this.abytes(e, undefined, 'extraEntropy')) // check for being bytes
                }
-               const seed = concatBytes(...seedArgs)
+               const seed = this.concatBytes(...seedArgs)
                const m = h1i // convert msg to bigint
                // Converts signature params into point w r/s, checks result for validity.
                // To transform k => Signature:
@@ -701,22 +698,22 @@ export class Secp256k1 {
                const k2sig = (kBytes: Bytes): Bytes | undefined => {
                        // RFC 6979 Section 3.2, step 3: k = bits2int(T)
                        // Important: all mod() calls here must be done over N
-                       const k = bits2int(kBytes)
-                       if (!(1n <= k && k < N)) return // Valid scalars (including k) must be in 1..N-1
-                       const ik = invert(k, N) // k^-1 mod n
-                       const q = G.multiply(k).toAffine() // q = k⋅G
-                       const r = modN(q.x) // r = q.x mod n
+                       const k = this.bits2int(kBytes)
+                       if (!(1n <= k && k < this.N)) return // Valid scalars (including k) must be in 1..N-1
+                       const ik = this.invert(k, this.N) // k^-1 mod n
+                       const q = this.G.multiply(k).toAffine() // q = k⋅G
+                       const r = this.modN(q.x) // r = q.x mod n
                        if (r === 0n) return
-                       const s = modN(ik * modN(m + r * d)) // s = k^-1(m + rd) mod n
+                       const s = this.modN(ik * this.modN(m + r * d)) // s = k^-1(m + rd) mod n
                        if (s === 0n) return
                        let recovery = (q.x === r ? 0 : 2) | Number(q.y & 1n) // recovery bit (2 or 3, when q.x > n)
                        let normS = s // normalized S
-                       if (lowS && highS(s)) {
+                       if (lowS && this.highS(s)) {
                                // if lowS was passed, ensure s is always
-                               normS = modN(-s) // in the bottom half of CURVE.n
+                               normS = this.modN(-s) // in the bottom half of CURVE.n
                                recovery ^= 1
                        }
-                       const sig = new Signature(r, normS, recovery) as RecoveredSignature // use normS, not s
+                       const sig = new this.Signature(r, normS, recovery) as RecoveredSignature // use normS, not s
                        return sig.toBytes(opts.format)
                }
                return hmacDrbg(seed, k2sig)
@@ -725,20 +722,20 @@ export class Secp256k1 {
        // Follows [SEC1](https://secg.org/sec1-v2.pdf) 4.1.4.
        static _verify = (sig: Bytes, messageHash: Bytes, publicKey: Bytes, opts: ECDSAVerifyOpts = {}) => {
                const { lowS, format } = opts
-               if (sig instanceof Signature) err('Signature must be in Uint8Array, use .toBytes()')
-               assertSigLength(sig, format)
-               abytes(publicKey, undefined, 'publicKey')
+               if (sig instanceof this.Signature) this.err('Signature must be in Uint8Array, use .toBytes()')
+               this.assertSigLength(sig, format)
+               this.abytes(publicKey, undefined, 'publicKey')
                try {
-                       const { r, s } = Signature.fromBytes(sig, format)
-                       const h = bits2int_modN(messageHash) // Truncate hash
-                       const P = Point.fromBytes(publicKey) // Validate public key
-                       if (lowS && highS(s)) return false // lowS bans sig.s >= CURVE.n/2
-                       const is = invert(s, N) // s^-1
-                       const u1 = modN(h * is) // u1 = hs^-1 mod n
-                       const u2 = modN(r * is) // u2 = rs^-1 mod n
-                       const R = doubleScalarMulUns(P, u1, u2).toAffine() // R = u1⋅G + u2⋅P
+                       const { r, s } = this.Signature.fromBytes(sig, format)
+                       const h = this.bits2int_modN(messageHash) // Truncate hash
+                       const P = this.Point.fromBytes(publicKey) // Validate public key
+                       if (lowS && this.highS(s)) return false // lowS bans sig.s >= CURVE.n/2
+                       const is = this.invert(s, this.N) // s^-1
+                       const u1 = this.modN(h * is) // u1 = hs^-1 mod n
+                       const u2 = this.modN(r * is) // u2 = rs^-1 mod n
+                       const R = this.doubleScalarMulUns(P, u1, u2).toAffine() // R = u1⋅G + u2⋅P
                        // Stop if R is identity / zero point. Check is done inside `doubleScalarMulUns`
-                       const v = modN(R.x) // R.x must be in N's field, not P's
+                       const v = this.modN(R.x) // R.x must be in N's field, not P's
                        return v === r // mod(R.x, n) == r
                } catch (error) {
                        return false
@@ -747,7 +744,7 @@ export class Secp256k1 {
 
        static setDefaults = (opts: ECDSASignOpts): Required<ECDSASignOpts> => {
                const res: ECDSASignOpts = {}
-               Object.keys(defaultSignOpts).forEach((k: string) => {
+               Object.keys(this.defaultSignOpts).forEach((k: string) => {
                        // @ts-ignore
                        res[k] = opts[k] ?? defaultSignOpts[k]
                })
@@ -768,9 +765,9 @@ export class Secp256k1 {
         * ```
         */
        static sign = (message: Bytes, secretKey: Bytes, opts: ECDSASignOpts = {}): Bytes => {
-               opts = setDefaults(opts)
-               message = prepMsg(message, opts, false) as Bytes
-               return _sign(message, secretKey, opts, hmacDrbg)
+               opts = this.setDefaults(opts)
+               message = this.prepMsg(message, opts, false) as Bytes
+               return this._sign(message, secretKey, opts, this.hmacDrbg)
        }
 
        /**
@@ -791,9 +788,9 @@ export class Secp256k1 {
                secretKey: Bytes,
                opts: ECDSASignOpts = {}
        ): Promise<Bytes> => {
-               opts = setDefaults(opts)
-               message = await prepMsg(message, opts, true)
-               return _sign(message, secretKey, opts, hmacDrbgAsync)
+               opts = this.setDefaults(opts)
+               message = await this.prepMsg(message, opts, true)
+               return this._sign(message, secretKey, opts, this.hmacDrbgAsync)
        }
 
        /**
@@ -817,9 +814,9 @@ export class Secp256k1 {
                publicKey: Bytes,
                opts: ECDSAVerifyOpts = {}
        ): boolean => {
-               opts = setDefaults(opts)
-               message = prepMsg(message, opts, false) as Bytes
-               return _verify(signature, message, publicKey, opts)
+               opts = this.setDefaults(opts)
+               message = this.prepMsg(message, opts, false) as Bytes
+               return this._verify(signature, message, publicKey, opts)
        }
 
        /**
@@ -843,27 +840,27 @@ export class Secp256k1 {
                publicKey: Bytes,
                opts: ECDSAVerifyOpts = {}
        ): Promise<boolean> => {
-               opts = setDefaults(opts)
-               message = await prepMsg(message, opts, true)
-               return _verify(sig, message, publicKey, opts)
+               opts = this.setDefaults(opts)
+               message = await this.prepMsg(message, opts, true)
+               return this._verify(sig, message, publicKey, opts)
        }
 
        static _recover = (signature: Bytes, messageHash: Bytes) => {
-               const sig = Signature.fromBytes(signature, 'recovered')
+               const sig = this.Signature.fromBytes(signature, 'recovered')
                const { r, s, recovery } = sig
                // 0 or 1 recovery id determines sign of "y" coordinate.
                // 2 or 3 means q.x was >N.
-               assertRecoveryBit(recovery)
-               const h = bits2int_modN(abytes(messageHash, L)) // Truncate hash
-               const radj = recovery === 2 || recovery === 3 ? r + N : r
-               FpIsValidNot0(radj) // ensure q.x is still a field element
-               const head = getPrefix(big(recovery!)) // head is 0x02 or 0x03
-               const Rb = concatBytes(head, numTo32b(radj)) // concat head + r
-               const R = Point.fromBytes(Rb)
-               const ir = invert(radj, N) // r^-1
-               const u1 = modN(-h * ir) // -hr^-1
-               const u2 = modN(s * ir) // sr^-1
-               const point = doubleScalarMulUns(R, u1, u2) // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1)
+               this.assertRecoveryBit(recovery)
+               const h = this.bits2int_modN(this.abytes(messageHash, this.L)) // Truncate hash
+               const radj = recovery === 2 || recovery === 3 ? r + this.N : r
+               this.FpIsValidNot0(radj) // ensure q.x is still a field element
+               const head = this.getPrefix(this.big(recovery!)) // head is 0x02 or 0x03
+               const Rb = this.concatBytes(head, this.numTo32b(radj)) // concat head + r
+               const R = this.Point.fromBytes(Rb)
+               const ir = this.invert(radj, this.N) // r^-1
+               const u1 = this.modN(-h * ir) // -hr^-1
+               const u2 = this.modN(s * ir) // sr^-1
+               const point = this.doubleScalarMulUns(R, u1, u2) // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1)
                return point.toBytes()
        }
 
@@ -872,8 +869,8 @@ export class Secp256k1 {
         * Follows [SEC1](https://secg.org/sec1-v2.pdf) 4.1.6.
         */
        static recoverPublicKey = (signature: Bytes, message: Bytes, opts: ECDSARecoverOpts = {}): Bytes => {
-               message = prepMsg(message, setDefaults(opts), false) as Bytes
-               return _recover(signature, message)
+               message = this.prepMsg(message, this.setDefaults(opts), false) as Bytes
+               return this._recover(signature, message)
        }
 
        static recoverPublicKeyAsync = async (
@@ -881,8 +878,8 @@ export class Secp256k1 {
                message: Bytes,
                opts: ECDSARecoverOpts = {}
        ): Promise<Bytes> => {
-               message = await prepMsg(message, setDefaults(opts), true)
-               return _recover(signature, message)
+               message = await this.prepMsg(message, this.setDefaults(opts), true)
+               return this._recover(signature, message)
        }
 
        /**
@@ -892,23 +889,23 @@ export class Secp256k1 {
         * @returns public key C
         */
        static getSharedSecret = (secretKeyA: Bytes, publicKeyB: Bytes, isCompressed = true): Bytes => {
-               return Point.fromBytes(publicKeyB).multiply(secretKeyToScalar(secretKeyA)).toBytes(isCompressed)
+               return this.Point.fromBytes(publicKeyB).multiply(this.secretKeyToScalar(secretKeyA)).toBytes(isCompressed)
        }
 
        // FIPS 186 B.4.1 compliant key generation produces private keys
        // with modulo bias being neglible. takes >N+16 bytes, returns (hash mod n-1)+1
-       static randomSecretKey = (seed = randomBytes(lengths.seed)) => {
-               abytes(seed)
-               if (seed.length < lengths.seed || seed.length > 1024) err('expected 40-1024b')
-               const num = M(bytesToNumBE(seed), N - 1n)
-               return numTo32b(num + 1n)
+       static randomSecretKey = (seed = this.randomBytes(this.lengths.seed)) => {
+               this.abytes(seed)
+               if (seed.length < this.lengths.seed || seed.length > 1024) this.err('expected 40-1024b')
+               const num = this.M(this.bytesToNumBE(seed), this.N - 1n)
+               return this.numTo32b(num + 1n)
        }
 
        static createKeygen = (getPublicKey: (secretKey: Bytes) => Bytes) => (seed?: Bytes): KeysSecPub => {
                const secretKey = this.randomSecretKey(seed)
                return { secretKey, publicKey: getPublicKey(secretKey) }
        }
-       static keygen: KeygenFn = this.createKeygen(getPublicKey)
+       static keygen: KeygenFn = this.createKeygen(this.getPublicKey)
 
        /** Math, hex, byte helpers. Not in `utils` because utils share API with noble-curves. */
        static etc = {
@@ -919,7 +916,7 @@ export class Secp256k1 {
                numberToBytesBE: this.numTo32b as (n: bigint) => Bytes,
                mod: this.M as (a: bigint, md?: bigint) => bigint,
                invert: this.invert as (num: bigint, md?: bigint) => bigint, // math utilities
-               randomBytes: randomBytes as (len?: number) => Bytes,
+               randomBytes: this.randomBytes as (len?: number) => Bytes,
                secretKeyToScalar: this.secretKeyToScalar as typeof this.secretKeyToScalar,
                abytes: this.abytes as typeof this.abytes,
        }
@@ -938,57 +935,57 @@ export class Secp256k1 {
        static T_NONCE = 'nonce'
        static T_CHALLENGE = 'challenge'
        static taggedHash = (tag: string, ...messages: Bytes[]): Bytes => {
-               const fn = callHash('sha256')
-               const tagH = fn(getTag(tag))
-               return fn(concatBytes(tagH, tagH, ...messages))
+               const fn = this.callHash('sha256')
+               const tagH = fn(this.getTag(tag))
+               return fn(this.concatBytes(tagH, tagH, ...messages))
        }
        static taggedHashAsync = async (tag: string, ...messages: Bytes[]): Promise<Bytes> => {
-               const fn = hashes.sha256Async
-               const tagH = await fn(getTag(tag))
-               return await fn(concatBytes(tagH, tagH, ...messages))
+               const fn = this.hashes.sha256Async
+               const tagH = await fn(this.getTag(tag))
+               return await fn(this.concatBytes(tagH, tagH, ...messages))
        }
 
        // ECDSA compact points are 33-byte. Schnorr is 32: we strip first byte 0x02 or 0x03
        // Calculate point, scalar and bytes
        static extpubSchnorr = (priv: Bytes) => {
-               const d_ = secretKeyToScalar(priv)
-               const p = G.multiply(d_) // P = d'⋅G; 0 < d' < n check is done inside
+               const d_ = this.secretKeyToScalar(priv)
+               const p = this.G.multiply(d_) // P = d'⋅G; 0 < d' < n check is done inside
                const { x, y } = p.assertValidity().toAffine() // validate Point is not at infinity
-               const d = isEven(y) ? d_ : modN(-d_)
-               const px = numTo32b(x)
+               const d = this.isEven(y) ? d_ : this.modN(-d_)
+               const px = this.numTo32b(x)
                return { d, px }
        }
 
-       static bytesModN = (bytes: Bytes) => modN(bytesToNumBE(bytes))
-       static challenge = (...args: Bytes[]): bigint => bytesModN(taggedHash(T_CHALLENGE, ...args))
+       static bytesModN = (bytes: Bytes) => this.modN(this.bytesToNumBE(bytes))
+       static challenge = (...args: Bytes[]): bigint => this.bytesModN(this.taggedHash(this.T_CHALLENGE, ...args))
        static challengeAsync = async (...args: Bytes[]): Promise<bigint> =>
-               bytesModN(await taggedHashAsync(T_CHALLENGE, ...args))
+               this.bytesModN(await this.taggedHashAsync(this.T_CHALLENGE, ...args))
 
        /**
         * Schnorr public key is just `x` coordinate of Point as per BIP340.
         */
        static pubSchnorr = (secretKey: Bytes): Bytes => {
-               return extpubSchnorr(secretKey).px // d'=int(sk). Fail if d'=0 or d'≥n. Ret bytes(d'⋅G)
+               return this.extpubSchnorr(secretKey).px // d'=int(sk). Fail if d'=0 or d'≥n. Ret bytes(d'⋅G)
        }
 
-       static keygenSchnorr: KeygenFn = createKeygen(pubSchnorr)
+       static keygenSchnorr: KeygenFn = this.createKeygen(this.pubSchnorr)
 
        // Common preparation fn for both sync and async signing
        static prepSigSchnorr = (message: Bytes, secretKey: Bytes, auxRand: Bytes) => {
-               const { px, d } = extpubSchnorr(secretKey)
-               return { m: abytes(message), px, d, a: abytes(auxRand, L) }
+               const { px, d } = this.extpubSchnorr(secretKey)
+               return { m: this.abytes(message), px, d, a: this.abytes(auxRand, this.L) }
        }
 
        static extractK = (rand: Bytes) => {
-               const k_ = bytesModN(rand) // Let k' = int(rand) mod n
-               if (k_ === 0n) err('sign failed: k is zero') // Fail if k' = 0.
-               const { px, d } = extpubSchnorr(numTo32b(k_)) // Let R = k'⋅G.
+               const k_ = this.bytesModN(rand) // Let k' = int(rand) mod n
+               if (k_ === 0n) this.err('sign failed: k is zero') // Fail if k' = 0.
+               const { px, d } = this.extpubSchnorr(this.numTo32b(k_)) // Let R = k'⋅G.
                return { rx: px, k: d }
        }
 
        // Common signature creation helper
        static createSigSchnorr = (k: bigint, px: Bytes, e: bigint, d: bigint): Bytes => {
-               return concatBytes(px, numTo32b(modN(k + e * d)))
+               return this.concatBytes(px, this.numTo32b(this.modN(k + e * d)))
        }
 
        static E_INVSIG = 'invalid signature produced'
@@ -996,39 +993,39 @@ export class Secp256k1 {
         * Creates Schnorr signature as per BIP340. Verifies itself before returning anything.
         * auxRand is optional and is not the sole source of k generation: bad CSPRNG won't be dangerous.
         */
-       static signSchnorr = (message: Bytes, secretKey: Bytes, auxRand: Bytes = randomBytes(L)): Bytes => {
-               const { m, px, d, a } = prepSigSchnorr(message, secretKey, auxRand)
-               const aux = taggedHash(T_AUX, a)
+       static signSchnorr = (message: Bytes, secretKey: Bytes, auxRand: Bytes = this.randomBytes(this.L)): Bytes => {
+               const { m, px, d, a } = this.prepSigSchnorr(message, secretKey, auxRand)
+               const aux = this.taggedHash(this.T_AUX, a)
                // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
-               const t = numTo32b(d ^ bytesToNumBE(aux))
+               const t = this.numTo32b(d ^ this.bytesToNumBE(aux))
                // Let rand = hash/nonce(t || bytes(P) || m)
-               const rand = taggedHash(T_NONCE, t, px, m)
-               const { rx, k } = extractK(rand)
+               const rand = this.taggedHash(this.T_NONCE, t, px, m)
+               const { rx, k } = this.extractK(rand)
                // Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n.
-               const e = challenge(rx, px, m)
-               const sig = createSigSchnorr(k, rx, e, d)
+               const e = this.challenge(rx, px, m)
+               const sig = this.createSigSchnorr(k, rx, e, d)
                // If Verify(bytes(P), m, sig) (see below) returns failure, abort
-               if (!verifySchnorr(sig, m, px)) err(E_INVSIG)
+               if (!this.verifySchnorr(sig, m, px)) this.err(this.E_INVSIG)
                return sig
        }
 
        static signSchnorrAsync = async (
                message: Bytes,
                secretKey: Bytes,
-               auxRand: Bytes = randomBytes(L)
+               auxRand: Bytes = this.randomBytes(this.L)
        ): Promise<Bytes> => {
-               const { m, px, d, a } = prepSigSchnorr(message, secretKey, auxRand)
-               const aux = await taggedHashAsync(T_AUX, a)
+               const { m, px, d, a } = this.prepSigSchnorr(message, secretKey, auxRand)
+               const aux = await this.taggedHashAsync(this.T_AUX, a)
                // Let t be the byte-wise xor of bytes(d) and hash/aux(a)
-               const t = numTo32b(d ^ bytesToNumBE(aux))
+               const t = this.numTo32b(d ^ this.bytesToNumBE(aux))
                // Let rand = hash/nonce(t || bytes(P) || m)
-               const rand = await taggedHashAsync(T_NONCE, t, px, m)
-               const { rx, k } = extractK(rand)
+               const rand = await this.taggedHashAsync(this.T_NONCE, t, px, m)
+               const { rx, k } = this.extractK(rand)
                // Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n.
-               const e = await challengeAsync(rx, px, m)
-               const sig = createSigSchnorr(k, rx, e, d)
+               const e = await this.challengeAsync(rx, px, m)
+               const sig = this.createSigSchnorr(k, rx, e, d)
                // If Verify(bytes(P), m, sig) (see below) returns failure, abort
-               if (!(await verifySchnorrAsync(sig, m, px))) err(E_INVSIG)
+               if (!(await this.verifySchnorrAsync(sig, m, px))) this.err(this.E_INVSIG)
                return sig
        }
 
@@ -1055,7 +1052,7 @@ export class Secp256k1 {
                        const y_ = this.isEven(y) ? y : this.M(-y)
                        // Return the unique point P such that x(P) = x and
                        // y(P) = y if y mod 2 = 0 or y(P) = p-y otherwise.
-                       const P_ = new Point(x, y_, 1n).assertValidity()
+                       const P_ = new this.Point(x, y_, 1n).assertValidity()
                        const px = this.numTo32b(P_.toAffine().x)
                        // P = lift_x(int(pk)); fail if that fails
                        const r = this.sliceBytesNumBE(sig, 0, this.L) // Let r = int(sig[0:32]); fail if r ≥ p.
@@ -1107,7 +1104,7 @@ export class Secp256k1 {
        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: Point[] = []
+               const points: Secp256k1['Point'][] = []
                let p = this.G
                let b = p
                for (let w = 0; w < this.pwindows; w++) {
@@ -1121,9 +1118,9 @@ export class Secp256k1 {
                }
                return points
        }
-       static Gpows: Point[] | undefined = undefined // precomputes for base point G
+       static Gpows: Secp256k1['Point'][] | undefined = undefined // precomputes for base point G
        // const-time negate
-       static ctneg = (cnd: boolean, p: Point) => {
+       static ctneg = (cnd: boolean, p: Secp256k1['Point']) => {
                const n = p.negate()
                return cnd ? n : p
        }
@@ -1139,7 +1136,7 @@ export class Secp256k1 {
         *
         * !! Precomputes can be disabled by commenting-out call of the wNAF() inside Point#multiply().
         */
-       static wNAF = (n: bigint): { p: Point; f: Point } => {
+       static wNAF = (n: bigint): { p: Secp256k1['Point']; f: Secp256k1['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