|
1 | 1 | 'use strict';
|
2 | 2 | Object.defineProperty(exports, '__esModule', { value: true });
|
3 |
| -exports.oneOf = exports.Null = exports.BufferN = exports.Function = exports.UInt32 = exports.UInt8 = exports.tuple = exports.maybe = exports.Hex = exports.Buffer = exports.String = exports.Boolean = exports.Array = exports.Number = exports.Hash256bit = exports.Hash160bit = exports.Buffer256bit = exports.Network = exports.ECPoint = exports.Satoshi = exports.Signer = exports.BIP32Path = exports.UInt31 = exports.isPoint = exports.typeforce = void 0; |
| 3 | +exports.oneOf = exports.Null = exports.BufferN = exports.Function = exports.UInt32 = exports.UInt8 = exports.tuple = exports.maybe = exports.Hex = exports.Buffer = exports.String = exports.Boolean = exports.Array = exports.Number = exports.Hash256bit = exports.Hash160bit = exports.Buffer256bit = exports.Network = exports.ECPoint = exports.Satoshi = exports.Signer = exports.BIP32Path = exports.UInt31 = exports.isXOnlyPoint = exports.isPoint = exports.typeforce = void 0; |
4 | 4 | const buffer_1 = require('buffer');
|
5 | 5 | exports.typeforce = require('typeforce');
|
6 |
| -const ZERO32 = buffer_1.Buffer.alloc(32, 0); |
7 |
| -const EC_P = buffer_1.Buffer.from( |
8 |
| - 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f', |
9 |
| - 'hex', |
| 6 | +const BN_ZERO = BigInt(0); |
| 7 | +// Bitcoin uses the secp256k1 curve, whose parameters can be found on |
| 8 | +// page 13, section 2.4.1, of https://www.secg.org/sec2-v2.pdf |
| 9 | +const EC_P = BigInt( |
| 10 | + `0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f`, |
10 | 11 | );
|
| 12 | +// The short Weierstrass form curve equation simplifes to y^2 = x^3 + 7. |
| 13 | +function secp256k1Right(x) { |
| 14 | + const EC_B = BigInt(7); |
| 15 | + const x2 = (x * x) % EC_P; |
| 16 | + const x3 = (x2 * x) % EC_P; |
| 17 | + return (x3 + EC_B) % EC_P; |
| 18 | +} |
| 19 | +// For prime P, the Jacobi Symbol of 'a' is 1 if and only if 'a' is a quadratic |
| 20 | +// residue mod P, ie. there exists a value 'x' for whom x^2 = a. |
| 21 | +function jacobiSymbol(a) { |
| 22 | + // Idea from noble-secp256k1, to be nice to bad JS parsers |
| 23 | + const _1n = BigInt(1); |
| 24 | + const _2n = BigInt(2); |
| 25 | + const _3n = BigInt(3); |
| 26 | + const _5n = BigInt(5); |
| 27 | + const _7n = BigInt(7); |
| 28 | + if (a === BN_ZERO) return 0; |
| 29 | + let p = EC_P; |
| 30 | + let sign = 1; |
| 31 | + // This algorithm is fairly heavily optimized, so don't simplify it w/o benchmarking |
| 32 | + for (;;) { |
| 33 | + let and3; |
| 34 | + // Handle runs of zeros efficiently w/o flipping sign each time |
| 35 | + for (and3 = a & _3n; and3 === BN_ZERO; a >>= _2n, and3 = a & _3n); |
| 36 | + // If there's one more zero, shift it off and flip the sign |
| 37 | + if (and3 === _2n) { |
| 38 | + a >>= _1n; |
| 39 | + const pand7 = p & _7n; |
| 40 | + if (pand7 === _3n || pand7 === _5n) sign = -sign; |
| 41 | + } |
| 42 | + if (a === _1n) break; |
| 43 | + if ((_3n & a) === _3n && (_3n & p) === _3n) sign = -sign; |
| 44 | + [a, p] = [p % a, a]; |
| 45 | + } |
| 46 | + return sign > 0 ? 1 : -1; |
| 47 | +} |
11 | 48 | function isPoint(p) {
|
12 | 49 | if (!buffer_1.Buffer.isBuffer(p)) return false;
|
13 | 50 | if (p.length < 33) return false;
|
14 | 51 | const t = p[0];
|
15 |
| - const x = p.slice(1, 33); |
16 |
| - if (x.compare(ZERO32) === 0) return false; |
17 |
| - if (x.compare(EC_P) >= 0) return false; |
18 |
| - if ((t === 0x02 || t === 0x03) && p.length === 33) { |
19 |
| - return true; |
| 52 | + if (p.length === 33) { |
| 53 | + return (t === 0x02 || t === 0x03) && isXOnlyPoint(p.slice(1)); |
20 | 54 | }
|
21 |
| - const y = p.slice(33); |
22 |
| - if (y.compare(ZERO32) === 0) return false; |
23 |
| - if (y.compare(EC_P) >= 0) return false; |
24 |
| - if (t === 0x04 && p.length === 65) return true; |
25 |
| - return false; |
| 55 | + if (t !== 0x04 || p.length !== 65) return false; |
| 56 | + const x = BigInt(`0x${p.slice(1, 33).toString('hex')}`); |
| 57 | + if (x === BN_ZERO) return false; |
| 58 | + if (x >= EC_P) return false; |
| 59 | + const y = BigInt(`0x${p.slice(33).toString('hex')}`); |
| 60 | + if (y === BN_ZERO) return false; |
| 61 | + if (y >= EC_P) return false; |
| 62 | + const left = (y * y) % EC_P; |
| 63 | + const right = secp256k1Right(x); |
| 64 | + return left === right; |
26 | 65 | }
|
27 | 66 | exports.isPoint = isPoint;
|
| 67 | +function isXOnlyPoint(p) { |
| 68 | + if (!buffer_1.Buffer.isBuffer(p)) return false; |
| 69 | + if (p.length !== 32) return false; |
| 70 | + const x = BigInt(`0x${p.toString('hex')}`); |
| 71 | + if (x === BN_ZERO) return false; |
| 72 | + if (x >= EC_P) return false; |
| 73 | + const y2 = secp256k1Right(x); |
| 74 | + return jacobiSymbol(y2) === 1; // If sqrt(y^2) exists, x is on the curve. |
| 75 | +} |
| 76 | +exports.isXOnlyPoint = isXOnlyPoint; |
28 | 77 | const UINT31_MAX = Math.pow(2, 31) - 1;
|
29 | 78 | function UInt31(value) {
|
30 | 79 | return exports.typeforce.UInt32(value) && value <= UINT31_MAX;
|
|
0 commit comments