@toruslabs/metadata-helpers
Version:
Helper methods for metadata
240 lines (236 loc) • 8.47 kB
JavaScript
;
var _defineProperty = require('@babel/runtime/helpers/defineProperty');
var modular_js = require('@noble/curves/abstract/modular.js');
var ed25519_js = require('@noble/curves/ed25519.js');
var utils_js = require('@noble/curves/utils.js');
var bytes = require('./bytes.js');
var crypto = require('./crypto.js');
var number = require('./number.js');
// ---------------------------------------------------------------------------
// Point
// ---------------------------------------------------------------------------
class Point {
constructor(x, y, keyType) {
_defineProperty(this, "x", void 0);
_defineProperty(this, "y", void 0);
_defineProperty(this, "keyType", void 0);
this.x = x;
this.y = y;
this.keyType = keyType;
}
encode(enc) {
switch (enc) {
case "arr":
return utils_js.concatBytes(bytes.hexToBytes("04"), utils_js.numberToBytesBE(this.x, 32), utils_js.numberToBytesBE(this.y, 32));
case "elliptic-compressed":
{
if (this.keyType === "secp256k1") {
const point = crypto.getSecp256k1().Point.fromAffine({
x: this.x,
y: this.y
});
return point.toBytes();
}
const point = ed25519_js.ed25519.Point.fromAffine({
x: this.x,
y: this.y
});
return point.toBytes();
}
default:
throw new Error("encoding doesn't exist in Point");
}
}
}
// ---------------------------------------------------------------------------
// Share
// ---------------------------------------------------------------------------
class Share {
constructor(shareIndex, share) {
_defineProperty(this, "share", void 0);
_defineProperty(this, "shareIndex", void 0);
this.share = share;
this.shareIndex = shareIndex;
}
static fromJSON(value) {
const {
share,
shareIndex
} = value;
return new Share(number.hexToBigInt(shareIndex), number.hexToBigInt(share));
}
toJSON() {
return {
share: number.bigintToHex(this.share),
shareIndex: number.bigintToHex(this.shareIndex)
};
}
}
// ---------------------------------------------------------------------------
// Polynomial
// ---------------------------------------------------------------------------
class Polynomial {
constructor(polynomial, ecCurve) {
_defineProperty(this, "polynomial", void 0);
_defineProperty(this, "ecCurve", void 0);
this.polynomial = polynomial;
this.ecCurve = ecCurve;
}
getThreshold() {
return this.polynomial.length;
}
polyEval(x) {
const n = this.ecCurve.Point.CURVE().n;
let xi = x;
let sum = this.polynomial[0];
for (let i = 1; i < this.polynomial.length; i += 1) {
const tmp = xi * this.polynomial[i];
sum = modular_js.mod(sum + tmp, n);
xi = modular_js.mod(xi * x, n);
}
return sum;
}
generateShares(shareIndexes) {
const shares = {};
for (let x = 0; x < shareIndexes.length; x += 1) {
const idx = shareIndexes[x];
shares[number.bigintToHex(idx)] = new Share(idx, this.polyEval(idx));
}
return shares;
}
}
// ---------------------------------------------------------------------------
// Lagrange interpolation
// ---------------------------------------------------------------------------
function generatePrivateExcludingIndexes(shareIndexes, keyType) {
const key = utils_js.bytesToNumberBE(crypto.generatePrivateKey(keyType));
if (shareIndexes.find(el => el === key)) {
return generatePrivateExcludingIndexes(shareIndexes, keyType);
}
return key;
}
const generateEmptyBigIntArray = length => Array.from({
length
}, () => 0n);
const denominator = (ecCurve, i, innerPoints) => {
const n = ecCurve.Point.CURVE().n;
let result = 1n;
const xi = innerPoints[i].x;
for (let j = innerPoints.length - 1; j >= 0; j -= 1) {
if (i !== j) {
let tmp = xi - innerPoints[j].x;
tmp = modular_js.mod(tmp, n);
result = modular_js.mod(result * tmp, n);
}
}
return result;
};
const interpolationPoly = (ecCurve, i, innerPoints) => {
const n = ecCurve.Point.CURVE().n;
let coefficients = generateEmptyBigIntArray(innerPoints.length);
const d = denominator(ecCurve, i, innerPoints);
if (d === 0n) {
throw new Error("Denominator for interpolationPoly is 0");
}
coefficients[0] = modular_js.invert(d, n);
for (let k = 0; k < innerPoints.length; k += 1) {
const newCoefficients = generateEmptyBigIntArray(innerPoints.length);
if (k !== i) {
let j;
if (k < i) {
j = k + 1;
} else {
j = k;
}
j -= 1;
for (; j >= 0; j -= 1) {
newCoefficients[j + 1] = modular_js.mod(newCoefficients[j + 1] + coefficients[j], n);
const tmp = modular_js.mod(innerPoints[k].x * coefficients[j], n);
newCoefficients[j] = modular_js.mod(newCoefficients[j] - tmp, n);
}
coefficients = newCoefficients;
}
}
return coefficients;
};
const pointSort = innerPoints => {
const pointArrClone = [...innerPoints];
pointArrClone.sort((a, b) => a.x < b.x ? -1 : a.x > b.x ? 1 : 0);
return pointArrClone;
};
const lagrange = (ecCurve, unsortedPoints) => {
const n = ecCurve.Point.CURVE().n;
const sortedPoints = pointSort(unsortedPoints);
const polynomial = generateEmptyBigIntArray(sortedPoints.length);
for (let i = 0; i < sortedPoints.length; i += 1) {
const coefficients = interpolationPoly(ecCurve, i, sortedPoints);
for (let k = 0; k < sortedPoints.length; k += 1) {
const tmp = sortedPoints[i].y * coefficients[k];
polynomial[k] = modular_js.mod(polynomial[k] + tmp, n);
}
}
return new Polynomial(polynomial, ecCurve);
};
function lagrangeInterpolatePolynomial(ecCurve, points) {
return lagrange(ecCurve, points);
}
function lagrangeInterpolation(ecCurve, shares, nodeIndex) {
if (shares.length !== nodeIndex.length) {
throw new Error("shares not equal to nodeIndex length in lagrangeInterpolation");
}
const n = ecCurve.Point.CURVE().n;
let secret = 0n;
for (let i = 0; i < shares.length; i += 1) {
let upper = 1n;
let lower = 1n;
for (let j = 0; j < shares.length; j += 1) {
if (i !== j) {
upper = modular_js.mod(upper * -nodeIndex[j], n);
let temp = nodeIndex[i] - nodeIndex[j];
temp = modular_js.mod(temp, n);
lower = modular_js.mod(lower * temp, n);
}
}
let delta = modular_js.mod(upper * modular_js.invert(lower, n), n);
delta = modular_js.mod(delta * shares[i], n);
secret = secret + delta;
}
return modular_js.mod(secret, n);
}
/** Generate a random polynomial for Shamir's Secret Sharing. */
function generateRandomPolynomial(ecCurve, keyType, degree, secret, deterministicShares) {
const actualS = secret !== undefined ? secret : generatePrivateExcludingIndexes([0n], keyType);
if (!deterministicShares) {
const poly = [actualS];
for (let i = 0; i < degree; i += 1) {
const share = generatePrivateExcludingIndexes(poly, keyType);
poly.push(share);
}
return new Polynomial(poly, ecCurve);
}
if (!Array.isArray(deterministicShares)) {
throw new Error("deterministic shares in generateRandomPolynomial should be an array");
}
if (deterministicShares.length > degree) {
throw new Error("deterministicShares in generateRandomPolynomial should be less or equal than degree to ensure an element of randomness");
}
const points = {};
deterministicShares.forEach(share => {
points[number.bigintToHex(share.shareIndex)] = new Point(share.shareIndex, share.share, keyType);
});
for (let i = 0; i < degree - deterministicShares.length; i += 1) {
let shareIndex = generatePrivateExcludingIndexes([0n], keyType);
while (points[number.bigintToHex(shareIndex)] !== undefined) {
shareIndex = generatePrivateExcludingIndexes([0n], keyType);
}
points[number.bigintToHex(shareIndex)] = new Point(shareIndex, utils_js.bytesToNumberBE(crypto.generatePrivateKey(keyType)), keyType);
}
points["0"] = new Point(0n, actualS, keyType);
return lagrangeInterpolatePolynomial(ecCurve, Object.values(points));
}
exports.Point = Point;
exports.Polynomial = Polynomial;
exports.Share = Share;
exports.generateRandomPolynomial = generateRandomPolynomial;
exports.lagrangeInterpolatePolynomial = lagrangeInterpolatePolynomial;
exports.lagrangeInterpolation = lagrangeInterpolation;