lotus-sdk
Version:
Central repository for several classes of tools for integrating with, and building for, the Lotusia ecosystem
128 lines (127 loc) • 4.29 kB
JavaScript
import elliptic from 'elliptic';
import { BN } from './bn.js';
const ecInstance = new elliptic.ec('secp256k1');
const ecPoint = ecInstance.curve.point.bind(ecInstance.curve);
const ecPointFromX = ecInstance.curve.pointFromX.bind(ecInstance.curve);
export class Point {
_point;
constructor(x, y, isRed) {
try {
const bnX = x instanceof BN ? x.bn : new BN(x);
const bnY = y instanceof BN ? y.bn : new BN(y);
this._point = ecPoint(bnX, bnY, isRed);
}
catch (e) {
throw new Error('Invalid Point');
}
this.validate();
}
static fromX(odd, x) {
try {
const xStr = typeof x === 'string' ? x : x.toString();
let point;
try {
const bnX = x instanceof BN ? x.bn : new BN(xStr);
point = ecInstance.curve.pointFromX(bnX, odd);
}
catch (e1) {
try {
const hexStr = typeof x === 'string' ? x : '0x' + x.toString(16);
const bnX = new BN(hexStr);
point = ecInstance.curve.pointFromX(bnX, odd);
}
catch (e2) {
const xBigInt = typeof x === 'string' ? new BN(xStr) : x.bn;
const y = ecInstance.curve.yFromX(xBigInt.toString(), odd);
point = ecInstance.curve.point(xBigInt.toString(), y);
}
}
const instance = new Point(new BN(0), new BN(0));
instance._point = point;
instance.validate();
return instance;
}
catch (e) {
throw new Error('Invalid X');
}
}
static getG() {
const instance = new Point(new BN(0), new BN(0));
instance._point = ecInstance.curve.g;
return instance;
}
static getN() {
return new BN(ecInstance.curve.n.toArray());
}
getX() {
return new BN(this._point.getX().toString());
}
getY() {
return new BN(this._point.getY().toString());
}
validate() {
if (this.isInfinity()) {
throw new Error('Point cannot be equal to Infinity');
}
try {
const x = this.getX();
const y = this.getY();
if (x === undefined || y === undefined) {
throw new Error('Point coordinates are undefined');
}
const n = ecInstance.curve.n;
const infinityPoint = this._point.mul(n);
if (!infinityPoint.isInfinity()) {
throw new Error('Point is not in the correct subgroup');
}
}
catch (e) {
throw new Error('Point does not lie on the curve');
}
return this;
}
isInfinity() {
return this._point.isInfinity();
}
add(other) {
const result = new Point(new BN(0), new BN(0));
result._point = this._point.add(other._point);
return result;
}
mul(scalar) {
const result = new Point(new BN(0), new BN(0));
result._point = this._point.mul(scalar.bn);
return result;
}
mulAdd(scalar1, other, scalar2) {
const result = new Point(new BN(0), new BN(0));
const point1 = this._point.mul(scalar1.bn);
const point2 = other._point.mul(scalar2.bn);
result._point = point1.add(point2);
return result;
}
eq(other) {
return this._point.eq(other._point);
}
static pointToCompressed(point) {
const xbuf = point.getX().toArrayLike(Buffer, 'be', 32);
const y = point.getY();
const odd = y.mod(new BN(2)).isOdd();
const prefix = odd ? Buffer.from([0x03]) : Buffer.from([0x02]);
return Buffer.concat([prefix, xbuf]);
}
hasSquare() {
if (this.isInfinity()) {
return false;
}
const y = this.getY();
return !y.isOdd();
}
isSquare(x) {
const p = new BN('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F', 16);
const x0 = new BN(x.toString());
const exponent = p.sub(new BN(1)).div(new BN(2));
const result = x0.modPow(exponent, p);
return result.eq(new BN(1));
}
}