xrpl
Version:
A TypeScript/JavaScript API for interacting with the XRP Ledger in Node.js and the browser
1,160 lines (1,137 loc) • 1.21 MB
JavaScript
var xrpl;
/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ "../../node_modules/@noble/curves/_shortw_utils.js":
/*!*********************************************************!*\
!*** ../../node_modules/@noble/curves/_shortw_utils.js ***!
\*********************************************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.getHash = getHash;
exports.createCurve = createCurve;
/**
* Utilities for short weierstrass curves, combined with noble-hashes.
* @module
*/
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
const hmac_1 = __webpack_require__(/*! @noble/hashes/hmac */ "../../node_modules/@noble/curves/node_modules/@noble/hashes/hmac.js");
const utils_1 = __webpack_require__(/*! @noble/hashes/utils */ "../../node_modules/@noble/curves/node_modules/@noble/hashes/utils.js");
const weierstrass_js_1 = __webpack_require__(/*! ./abstract/weierstrass.js */ "../../node_modules/@noble/curves/abstract/weierstrass.js");
/** connects noble-curves to noble-hashes */
function getHash(hash) {
return {
hash,
hmac: (key, ...msgs) => (0, hmac_1.hmac)(hash, key, (0, utils_1.concatBytes)(...msgs)),
randomBytes: utils_1.randomBytes,
};
}
function createCurve(curveDef, defHash) {
const create = (hash) => (0, weierstrass_js_1.weierstrass)({ ...curveDef, ...getHash(hash) });
return { ...create(defHash), create };
}
/***/ }),
/***/ "../../node_modules/@noble/curves/abstract/curve.js":
/*!**********************************************************!*\
!*** ../../node_modules/@noble/curves/abstract/curve.js ***!
\**********************************************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.wNAF = wNAF;
exports.pippenger = pippenger;
exports.precomputeMSMUnsafe = precomputeMSMUnsafe;
exports.validateBasic = validateBasic;
/**
* Methods for elliptic curve multiplication by scalars.
* Contains wNAF, pippenger
* @module
*/
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
const modular_js_1 = __webpack_require__(/*! ./modular.js */ "../../node_modules/@noble/curves/abstract/modular.js");
const utils_js_1 = __webpack_require__(/*! ./utils.js */ "../../node_modules/@noble/curves/abstract/utils.js");
const _0n = BigInt(0);
const _1n = BigInt(1);
function constTimeNegate(condition, item) {
const neg = item.negate();
return condition ? neg : item;
}
function validateW(W, bits) {
if (!Number.isSafeInteger(W) || W <= 0 || W > bits)
throw new Error('invalid window size, expected [1..' + bits + '], got W=' + W);
}
function calcWOpts(W, bits) {
validateW(W, bits);
const windows = Math.ceil(bits / W) + 1; // +1, because
const windowSize = 2 ** (W - 1); // -1 because we skip zero
return { windows, windowSize };
}
function validateMSMPoints(points, c) {
if (!Array.isArray(points))
throw new Error('array expected');
points.forEach((p, i) => {
if (!(p instanceof c))
throw new Error('invalid point at index ' + i);
});
}
function validateMSMScalars(scalars, field) {
if (!Array.isArray(scalars))
throw new Error('array of scalars expected');
scalars.forEach((s, i) => {
if (!field.isValid(s))
throw new Error('invalid scalar at index ' + i);
});
}
// Since points in different groups cannot be equal (different object constructor),
// we can have single place to store precomputes
const pointPrecomputes = new WeakMap();
const pointWindowSizes = new WeakMap(); // This allows use make points immutable (nothing changes inside)
function getW(P) {
return pointWindowSizes.get(P) || 1;
}
/**
* Elliptic curve multiplication of Point by scalar. Fragile.
* Scalars should always be less than curve order: this should be checked inside of a curve itself.
* Creates precomputation tables for fast multiplication:
* - private scalar is split by fixed size windows of W bits
* - every window point is collected from window's table & added to accumulator
* - since windows are different, same point inside tables won't be accessed more than once per calc
* - each multiplication is 'Math.ceil(CURVE_ORDER / 𝑊) + 1' point additions (fixed for any scalar)
* - +1 window is neccessary for wNAF
* - wNAF reduces table size: 2x less memory + 2x faster generation, but 10% slower multiplication
*
* @todo Research returning 2d JS array of windows, instead of a single window.
* This would allow windows to be in different memory locations
*/
function wNAF(c, bits) {
return {
constTimeNegate,
hasPrecomputes(elm) {
return getW(elm) !== 1;
},
// non-const time multiplication ladder
unsafeLadder(elm, n, p = c.ZERO) {
let d = elm;
while (n > _0n) {
if (n & _1n)
p = p.add(d);
d = d.double();
n >>= _1n;
}
return p;
},
/**
* Creates a wNAF precomputation window. Used for caching.
* Default window size is set by `utils.precompute()` and is equal to 8.
* Number of precomputed points depends on the curve size:
* 2^(𝑊−1) * (Math.ceil(𝑛 / 𝑊) + 1), where:
* - 𝑊 is the window size
* - 𝑛 is the bitlength of the curve order.
* For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224.
* @param elm Point instance
* @param W window size
* @returns precomputed point tables flattened to a single array
*/
precomputeWindow(elm, W) {
const { windows, windowSize } = calcWOpts(W, bits);
const points = [];
let p = elm;
let base = p;
for (let window = 0; window < windows; window++) {
base = p;
points.push(base);
// =1, because we skip zero
for (let i = 1; i < windowSize; i++) {
base = base.add(p);
points.push(base);
}
p = base.double();
}
return points;
},
/**
* Implements ec multiplication using precomputed tables and w-ary non-adjacent form.
* @param W window size
* @param precomputes precomputed tables
* @param n scalar (we don't check here, but should be less than curve order)
* @returns real and fake (for const-time) points
*/
wNAF(W, precomputes, n) {
// TODO: maybe check that scalar is less than group order? wNAF behavious is undefined otherwise
// But need to carefully remove other checks before wNAF. ORDER == bits here
const { windows, windowSize } = calcWOpts(W, bits);
let p = c.ZERO;
let f = c.BASE;
const mask = BigInt(2 ** W - 1); // Create mask with W ones: 0b1111 for W=4 etc.
const maxNumber = 2 ** W;
const shiftBy = BigInt(W);
for (let window = 0; window < windows; window++) {
const offset = window * windowSize;
// Extract W bits.
let wbits = Number(n & mask);
// Shift number by W bits.
n >>= shiftBy;
// If the bits are bigger than max size, we'll split those.
// +224 => 256 - 32
if (wbits > windowSize) {
wbits -= maxNumber;
n += _1n;
}
// This code was first written with assumption that 'f' and 'p' will never be infinity point:
// since each addition is multiplied by 2 ** W, it cannot cancel each other. However,
// there is negate now: it is possible that negated element from low value
// would be the same as high element, which will create carry into next window.
// It's not obvious how this can fail, but still worth investigating later.
// Check if we're onto Zero point.
// Add random point inside current window to f.
const offset1 = offset;
const offset2 = offset + Math.abs(wbits) - 1; // -1 because we skip zero
const cond1 = window % 2 !== 0;
const cond2 = wbits < 0;
if (wbits === 0) {
// The most important part for const-time getPublicKey
f = f.add(constTimeNegate(cond1, precomputes[offset1]));
}
else {
p = p.add(constTimeNegate(cond2, precomputes[offset2]));
}
}
// JIT-compiler should not eliminate f here, since it will later be used in normalizeZ()
// Even if the variable is still unused, there are some checks which will
// throw an exception, so compiler needs to prove they won't happen, which is hard.
// At this point there is a way to F be infinity-point even if p is not,
// which makes it less const-time: around 1 bigint multiply.
return { p, f };
},
/**
* Implements ec unsafe (non const-time) multiplication using precomputed tables and w-ary non-adjacent form.
* @param W window size
* @param precomputes precomputed tables
* @param n scalar (we don't check here, but should be less than curve order)
* @param acc accumulator point to add result of multiplication
* @returns point
*/
wNAFUnsafe(W, precomputes, n, acc = c.ZERO) {
const { windows, windowSize } = calcWOpts(W, bits);
const mask = BigInt(2 ** W - 1); // Create mask with W ones: 0b1111 for W=4 etc.
const maxNumber = 2 ** W;
const shiftBy = BigInt(W);
for (let window = 0; window < windows; window++) {
const offset = window * windowSize;
if (n === _0n)
break; // No need to go over empty scalar
// Extract W bits.
let wbits = Number(n & mask);
// Shift number by W bits.
n >>= shiftBy;
// If the bits are bigger than max size, we'll split those.
// +224 => 256 - 32
if (wbits > windowSize) {
wbits -= maxNumber;
n += _1n;
}
if (wbits === 0)
continue;
let curr = precomputes[offset + Math.abs(wbits) - 1]; // -1 because we skip zero
if (wbits < 0)
curr = curr.negate();
// NOTE: by re-using acc, we can save a lot of additions in case of MSM
acc = acc.add(curr);
}
return acc;
},
getPrecomputes(W, P, transform) {
// Calculate precomputes on a first run, reuse them after
let comp = pointPrecomputes.get(P);
if (!comp) {
comp = this.precomputeWindow(P, W);
if (W !== 1)
pointPrecomputes.set(P, transform(comp));
}
return comp;
},
wNAFCached(P, n, transform) {
const W = getW(P);
return this.wNAF(W, this.getPrecomputes(W, P, transform), n);
},
wNAFCachedUnsafe(P, n, transform, prev) {
const W = getW(P);
if (W === 1)
return this.unsafeLadder(P, n, prev); // For W=1 ladder is ~x2 faster
return this.wNAFUnsafe(W, this.getPrecomputes(W, P, transform), n, prev);
},
// We calculate precomputes for elliptic curve point multiplication
// using windowed method. This specifies window size and
// stores precomputed values. Usually only base point would be precomputed.
setWindowSize(P, W) {
validateW(W, bits);
pointWindowSizes.set(P, W);
pointPrecomputes.delete(P);
},
};
}
/**
* Pippenger algorithm for multi-scalar multiplication (MSM, Pa + Qb + Rc + ...).
* 30x faster vs naive addition on L=4096, 10x faster with precomputes.
* For N=254bit, L=1, it does: 1024 ADD + 254 DBL. For L=5: 1536 ADD + 254 DBL.
* Algorithmically constant-time (for same L), even when 1 point + scalar, or when scalar = 0.
* @param c Curve Point constructor
* @param fieldN field over CURVE.N - important that it's not over CURVE.P
* @param points array of L curve points
* @param scalars array of L scalars (aka private keys / bigints)
*/
function pippenger(c, fieldN, points, scalars) {
// If we split scalars by some window (let's say 8 bits), every chunk will only
// take 256 buckets even if there are 4096 scalars, also re-uses double.
// TODO:
// - https://eprint.iacr.org/2024/750.pdf
// - https://tches.iacr.org/index.php/TCHES/article/view/10287
// 0 is accepted in scalars
validateMSMPoints(points, c);
validateMSMScalars(scalars, fieldN);
if (points.length !== scalars.length)
throw new Error('arrays of points and scalars must have equal length');
const zero = c.ZERO;
const wbits = (0, utils_js_1.bitLen)(BigInt(points.length));
const windowSize = wbits > 12 ? wbits - 3 : wbits > 4 ? wbits - 2 : wbits ? 2 : 1; // in bits
const MASK = (1 << windowSize) - 1;
const buckets = new Array(MASK + 1).fill(zero); // +1 for zero array
const lastBits = Math.floor((fieldN.BITS - 1) / windowSize) * windowSize;
let sum = zero;
for (let i = lastBits; i >= 0; i -= windowSize) {
buckets.fill(zero);
for (let j = 0; j < scalars.length; j++) {
const scalar = scalars[j];
const wbits = Number((scalar >> BigInt(i)) & BigInt(MASK));
buckets[wbits] = buckets[wbits].add(points[j]);
}
let resI = zero; // not using this will do small speed-up, but will lose ct
// Skip first bucket, because it is zero
for (let j = buckets.length - 1, sumI = zero; j > 0; j--) {
sumI = sumI.add(buckets[j]);
resI = resI.add(sumI);
}
sum = sum.add(resI);
if (i !== 0)
for (let j = 0; j < windowSize; j++)
sum = sum.double();
}
return sum;
}
/**
* Precomputed multi-scalar multiplication (MSM, Pa + Qb + Rc + ...).
* @param c Curve Point constructor
* @param fieldN field over CURVE.N - important that it's not over CURVE.P
* @param points array of L curve points
* @returns function which multiplies points with scaars
*/
function precomputeMSMUnsafe(c, fieldN, points, windowSize) {
/**
* Performance Analysis of Window-based Precomputation
*
* Base Case (256-bit scalar, 8-bit window):
* - Standard precomputation requires:
* - 31 additions per scalar × 256 scalars = 7,936 ops
* - Plus 255 summary additions = 8,191 total ops
* Note: Summary additions can be optimized via accumulator
*
* Chunked Precomputation Analysis:
* - Using 32 chunks requires:
* - 255 additions per chunk
* - 256 doublings
* - Total: (255 × 32) + 256 = 8,416 ops
*
* Memory Usage Comparison:
* Window Size | Standard Points | Chunked Points
* ------------|-----------------|---------------
* 4-bit | 520 | 15
* 8-bit | 4,224 | 255
* 10-bit | 13,824 | 1,023
* 16-bit | 557,056 | 65,535
*
* Key Advantages:
* 1. Enables larger window sizes due to reduced memory overhead
* 2. More efficient for smaller scalar counts:
* - 16 chunks: (16 × 255) + 256 = 4,336 ops
* - ~2x faster than standard 8,191 ops
*
* Limitations:
* - Not suitable for plain precomputes (requires 256 constant doublings)
* - Performance degrades with larger scalar counts:
* - Optimal for ~256 scalars
* - Less efficient for 4096+ scalars (Pippenger preferred)
*/
validateW(windowSize, fieldN.BITS);
validateMSMPoints(points, c);
const zero = c.ZERO;
const tableSize = 2 ** windowSize - 1; // table size (without zero)
const chunks = Math.ceil(fieldN.BITS / windowSize); // chunks of item
const MASK = BigInt((1 << windowSize) - 1);
const tables = points.map((p) => {
const res = [];
for (let i = 0, acc = p; i < tableSize; i++) {
res.push(acc);
acc = acc.add(p);
}
return res;
});
return (scalars) => {
validateMSMScalars(scalars, fieldN);
if (scalars.length > points.length)
throw new Error('array of scalars must be smaller than array of points');
let res = zero;
for (let i = 0; i < chunks; i++) {
// No need to double if accumulator is still zero.
if (res !== zero)
for (let j = 0; j < windowSize; j++)
res = res.double();
const shiftBy = BigInt(chunks * windowSize - (i + 1) * windowSize);
for (let j = 0; j < scalars.length; j++) {
const n = scalars[j];
const curr = Number((n >> shiftBy) & MASK);
if (!curr)
continue; // skip zero scalars chunks
res = res.add(tables[j][curr - 1]);
}
}
return res;
};
}
function validateBasic(curve) {
(0, modular_js_1.validateField)(curve.Fp);
(0, utils_js_1.validateObject)(curve, {
n: 'bigint',
h: 'bigint',
Gx: 'field',
Gy: 'field',
}, {
nBitLength: 'isSafeInteger',
nByteLength: 'isSafeInteger',
});
// Set defaults
return Object.freeze({
...(0, modular_js_1.nLength)(curve.n, curve.nBitLength),
...curve,
...{ p: curve.Fp.ORDER },
});
}
/***/ }),
/***/ "../../node_modules/@noble/curves/abstract/edwards.js":
/*!************************************************************!*\
!*** ../../node_modules/@noble/curves/abstract/edwards.js ***!
\************************************************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.twistedEdwards = twistedEdwards;
/**
* Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y².
* For design rationale of types / exports, see weierstrass module documentation.
* @module
*/
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
const curve_js_1 = __webpack_require__(/*! ./curve.js */ "../../node_modules/@noble/curves/abstract/curve.js");
const modular_js_1 = __webpack_require__(/*! ./modular.js */ "../../node_modules/@noble/curves/abstract/modular.js");
const ut = __webpack_require__(/*! ./utils.js */ "../../node_modules/@noble/curves/abstract/utils.js");
const utils_js_1 = __webpack_require__(/*! ./utils.js */ "../../node_modules/@noble/curves/abstract/utils.js");
// Be friendly to bad ECMAScript parsers by not using bigint literals
// prettier-ignore
const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _8n = BigInt(8);
// verification rule is either zip215 or rfc8032 / nist186-5. Consult fromHex:
const VERIFY_DEFAULT = { zip215: true };
function validateOpts(curve) {
const opts = (0, curve_js_1.validateBasic)(curve);
ut.validateObject(curve, {
hash: 'function',
a: 'bigint',
d: 'bigint',
randomBytes: 'function',
}, {
adjustScalarBytes: 'function',
domain: 'function',
uvRatio: 'function',
mapToCurve: 'function',
});
// Set defaults
return Object.freeze({ ...opts });
}
/**
* Creates Twisted Edwards curve with EdDSA signatures.
* @example
* import { Field } from '@noble/curves/abstract/modular';
* // Before that, define BigInt-s: a, d, p, n, Gx, Gy, h
* const curve = twistedEdwards({ a, d, Fp: Field(p), n, Gx, Gy, h })
*/
function twistedEdwards(curveDef) {
const CURVE = validateOpts(curveDef);
const { Fp, n: CURVE_ORDER, prehash: prehash, hash: cHash, randomBytes, nByteLength, h: cofactor, } = CURVE;
// Important:
// There are some places where Fp.BYTES is used instead of nByteLength.
// So far, everything has been tested with curves of Fp.BYTES == nByteLength.
// TODO: test and find curves which behave otherwise.
const MASK = _2n << (BigInt(nByteLength * 8) - _1n);
const modP = Fp.create; // Function overrides
const Fn = (0, modular_js_1.Field)(CURVE.n, CURVE.nBitLength);
// sqrt(u/v)
const uvRatio = CURVE.uvRatio ||
((u, v) => {
try {
return { isValid: true, value: Fp.sqrt(u * Fp.inv(v)) };
}
catch (e) {
return { isValid: false, value: _0n };
}
});
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes); // NOOP
const domain = CURVE.domain ||
((data, ctx, phflag) => {
(0, utils_js_1.abool)('phflag', phflag);
if (ctx.length || phflag)
throw new Error('Contexts/pre-hash are not supported');
return data;
}); // NOOP
// 0 <= n < MASK
// Coordinates larger than Fp.ORDER are allowed for zip215
function aCoordinate(title, n) {
ut.aInRange('coordinate ' + title, n, _0n, MASK);
}
function assertPoint(other) {
if (!(other instanceof Point))
throw new Error('ExtendedPoint expected');
}
// Converts Extended point to default (x, y) coordinates.
// Can accept precomputed Z^-1 - for example, from invertBatch.
const toAffineMemo = (0, utils_js_1.memoized)((p, iz) => {
const { ex: x, ey: y, ez: z } = p;
const is0 = p.is0();
if (iz == null)
iz = is0 ? _8n : Fp.inv(z); // 8 was chosen arbitrarily
const ax = modP(x * iz);
const ay = modP(y * iz);
const zz = modP(z * iz);
if (is0)
return { x: _0n, y: _1n };
if (zz !== _1n)
throw new Error('invZ was invalid');
return { x: ax, y: ay };
});
const assertValidMemo = (0, utils_js_1.memoized)((p) => {
const { a, d } = CURVE;
if (p.is0())
throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
// Equation in affine coordinates: ax² + y² = 1 + dx²y²
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
const { ex: X, ey: Y, ez: Z, et: T } = p;
const X2 = modP(X * X); // X²
const Y2 = modP(Y * Y); // Y²
const Z2 = modP(Z * Z); // Z²
const Z4 = modP(Z2 * Z2); // Z⁴
const aX2 = modP(X2 * a); // aX²
const left = modP(Z2 * modP(aX2 + Y2)); // (aX² + Y²)Z²
const right = modP(Z4 + modP(d * modP(X2 * Y2))); // Z⁴ + dX²Y²
if (left !== right)
throw new Error('bad point: equation left != right (1)');
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
const XY = modP(X * Y);
const ZT = modP(Z * T);
if (XY !== ZT)
throw new Error('bad point: equation left != right (2)');
return true;
});
// Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).
// https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
class Point {
constructor(ex, ey, ez, et) {
this.ex = ex;
this.ey = ey;
this.ez = ez;
this.et = et;
aCoordinate('x', ex);
aCoordinate('y', ey);
aCoordinate('z', ez);
aCoordinate('t', et);
Object.freeze(this);
}
get x() {
return this.toAffine().x;
}
get y() {
return this.toAffine().y;
}
static fromAffine(p) {
if (p instanceof Point)
throw new Error('extended point not allowed');
const { x, y } = p || {};
aCoordinate('x', x);
aCoordinate('y', y);
return new Point(x, y, _1n, modP(x * y));
}
static normalizeZ(points) {
const toInv = Fp.invertBatch(points.map((p) => p.ez));
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
}
// Multiscalar Multiplication
static msm(points, scalars) {
return (0, curve_js_1.pippenger)(Point, Fn, points, scalars);
}
// "Private method", don't use it directly
_setWindowSize(windowSize) {
wnaf.setWindowSize(this, windowSize);
}
// Not required for fromHex(), which always creates valid points.
// Could be useful for fromAffine().
assertValidity() {
assertValidMemo(this);
}
// Compare one point to another.
equals(other) {
assertPoint(other);
const { ex: X1, ey: Y1, ez: Z1 } = this;
const { ex: X2, ey: Y2, ez: Z2 } = other;
const X1Z2 = modP(X1 * Z2);
const X2Z1 = modP(X2 * Z1);
const Y1Z2 = modP(Y1 * Z2);
const Y2Z1 = modP(Y2 * Z1);
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
}
is0() {
return this.equals(Point.ZERO);
}
negate() {
// Flips point sign to a negative one (-x, y in affine coords)
return new Point(modP(-this.ex), this.ey, this.ez, modP(-this.et));
}
// Fast algo for doubling Extended Point.
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
// Cost: 4M + 4S + 1*a + 6add + 1*2.
double() {
const { a } = CURVE;
const { ex: X1, ey: Y1, ez: Z1 } = this;
const A = modP(X1 * X1); // A = X12
const B = modP(Y1 * Y1); // B = Y12
const C = modP(_2n * modP(Z1 * Z1)); // C = 2*Z12
const D = modP(a * A); // D = a*A
const x1y1 = X1 + Y1;
const E = modP(modP(x1y1 * x1y1) - A - B); // E = (X1+Y1)2-A-B
const G = D + B; // G = D+B
const F = G - C; // F = G-C
const H = D - B; // H = D-B
const X3 = modP(E * F); // X3 = E*F
const Y3 = modP(G * H); // Y3 = G*H
const T3 = modP(E * H); // T3 = E*H
const Z3 = modP(F * G); // Z3 = F*G
return new Point(X3, Y3, Z3, T3);
}
// Fast algo for adding 2 Extended Points.
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
// Cost: 9M + 1*a + 1*d + 7add.
add(other) {
assertPoint(other);
const { a, d } = CURVE;
const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
const { ex: X2, ey: Y2, ez: Z2, et: T2 } = other;
// Faster algo for adding 2 Extended Points when curve's a=-1.
// http://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-4
// Cost: 8M + 8add + 2*2.
// Note: It does not check whether the `other` point is valid.
if (a === BigInt(-1)) {
const A = modP((Y1 - X1) * (Y2 + X2));
const B = modP((Y1 + X1) * (Y2 - X2));
const F = modP(B - A);
if (F === _0n)
return this.double(); // Same point. Tests say it doesn't affect timing
const C = modP(Z1 * _2n * T2);
const D = modP(T1 * _2n * Z2);
const E = D + C;
const G = B + A;
const H = D - C;
const X3 = modP(E * F);
const Y3 = modP(G * H);
const T3 = modP(E * H);
const Z3 = modP(F * G);
return new Point(X3, Y3, Z3, T3);
}
const A = modP(X1 * X2); // A = X1*X2
const B = modP(Y1 * Y2); // B = Y1*Y2
const C = modP(T1 * d * T2); // C = T1*d*T2
const D = modP(Z1 * Z2); // D = Z1*Z2
const E = modP((X1 + Y1) * (X2 + Y2) - A - B); // E = (X1+Y1)*(X2+Y2)-A-B
const F = D - C; // F = D-C
const G = D + C; // G = D+C
const H = modP(B - a * A); // H = B-a*A
const X3 = modP(E * F); // X3 = E*F
const Y3 = modP(G * H); // Y3 = G*H
const T3 = modP(E * H); // T3 = E*H
const Z3 = modP(F * G); // Z3 = F*G
return new Point(X3, Y3, Z3, T3);
}
subtract(other) {
return this.add(other.negate());
}
wNAF(n) {
return wnaf.wNAFCached(this, n, Point.normalizeZ);
}
// Constant-time multiplication.
multiply(scalar) {
const n = scalar;
ut.aInRange('scalar', n, _1n, CURVE_ORDER); // 1 <= scalar < L
const { p, f } = this.wNAF(n);
return Point.normalizeZ([p, f])[0];
}
// Non-constant-time multiplication. Uses double-and-add algorithm.
// It's faster, but should only be used when you don't care about
// an exposed private key e.g. sig verification.
// Does NOT allow scalars higher than CURVE.n.
// Accepts optional accumulator to merge with multiply (important for sparse scalars)
multiplyUnsafe(scalar, acc = Point.ZERO) {
const n = scalar;
ut.aInRange('scalar', n, _0n, CURVE_ORDER); // 0 <= scalar < L
if (n === _0n)
return I;
if (this.is0() || n === _1n)
return this;
return wnaf.wNAFCachedUnsafe(this, n, Point.normalizeZ, acc);
}
// Checks if point is of small order.
// If you add something to small order point, you will have "dirty"
// point with torsion component.
// Multiplies point by cofactor and checks if the result is 0.
isSmallOrder() {
return this.multiplyUnsafe(cofactor).is0();
}
// Multiplies point by curve order and checks if the result is 0.
// Returns `false` is the point is dirty.
isTorsionFree() {
return wnaf.unsafeLadder(this, CURVE_ORDER).is0();
}
// Converts Extended point to default (x, y) coordinates.
// Can accept precomputed Z^-1 - for example, from invertBatch.
toAffine(iz) {
return toAffineMemo(this, iz);
}
clearCofactor() {
const { h: cofactor } = CURVE;
if (cofactor === _1n)
return this;
return this.multiplyUnsafe(cofactor);
}
// Converts hash string or Uint8Array to Point.
// Uses algo from RFC8032 5.1.3.
static fromHex(hex, zip215 = false) {
const { d, a } = CURVE;
const len = Fp.BYTES;
hex = (0, utils_js_1.ensureBytes)('pointHex', hex, len); // copy hex to a new array
(0, utils_js_1.abool)('zip215', zip215);
const normed = hex.slice(); // copy again, we'll manipulate it
const lastByte = hex[len - 1]; // select last byte
normed[len - 1] = lastByte & ~0x80; // clear last bit
const y = ut.bytesToNumberLE(normed);
// zip215=true is good for consensus-critical apps. =false follows RFC8032 / NIST186-5.
// RFC8032 prohibits >= p, but ZIP215 doesn't
// zip215=true: 0 <= y < MASK (2^256 for ed25519)
// zip215=false: 0 <= y < P (2^255-19 for ed25519)
const max = zip215 ? MASK : Fp.ORDER;
ut.aInRange('pointHex.y', y, _0n, max);
// Ed25519: x² = (y²-1)/(dy²+1) mod p. Ed448: x² = (y²-1)/(dy²-1) mod p. Generic case:
// ax²+y²=1+dx²y² => y²-1=dx²y²-ax² => y²-1=x²(dy²-a) => x²=(y²-1)/(dy²-a)
const y2 = modP(y * y); // denominator is always non-0 mod p.
const u = modP(y2 - _1n); // u = y² - 1
const v = modP(d * y2 - a); // v = d y² + 1.
let { isValid, value: x } = uvRatio(u, v); // √(u/v)
if (!isValid)
throw new Error('Point.fromHex: invalid y coordinate');
const isXOdd = (x & _1n) === _1n; // There are 2 square roots. Use x_0 bit to select proper
const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit
if (!zip215 && x === _0n && isLastByteOdd)
// if x=0 and x_0 = 1, fail
throw new Error('Point.fromHex: x=0 and x_0=1');
if (isLastByteOdd !== isXOdd)
x = modP(-x); // if x_0 != x mod 2, set x = p-x
return Point.fromAffine({ x, y });
}
static fromPrivateKey(privKey) {
return getExtendedPublicKey(privKey).point;
}
toRawBytes() {
const { x, y } = this.toAffine();
const bytes = ut.numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y)
bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0; // when compressing, it's enough to store y
return bytes; // and use the last byte to encode sign of x
}
toHex() {
return ut.bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
}
}
Point.BASE = new Point(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
Point.ZERO = new Point(_0n, _1n, _1n, _0n); // 0, 1, 1, 0
const { BASE: G, ZERO: I } = Point;
const wnaf = (0, curve_js_1.wNAF)(Point, nByteLength * 8);
function modN(a) {
return (0, modular_js_1.mod)(a, CURVE_ORDER);
}
// Little-endian SHA512 with modulo n
function modN_LE(hash) {
return modN(ut.bytesToNumberLE(hash));
}
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */
function getExtendedPublicKey(key) {
const len = Fp.BYTES;
key = (0, utils_js_1.ensureBytes)('private key', key, len);
// Hash private key with curve's hash function to produce uniformingly random input
// Check byte lengths: ensure(64, h(ensure(32, key)))
const hashed = (0, utils_js_1.ensureBytes)('hashed private key', cHash(key), 2 * len);
const head = adjustScalarBytes(hashed.slice(0, len)); // clear first half bits, produce FE
const prefix = hashed.slice(len, 2 * len); // second half is called key prefix (5.1.6)
const scalar = modN_LE(head); // The actual private scalar
const point = G.multiply(scalar); // Point on Edwards curve aka public key
const pointBytes = point.toRawBytes(); // Uint8Array representation
return { head, prefix, scalar, point, pointBytes };
}
// Calculates EdDSA pub key. RFC8032 5.1.5. Privkey is hashed. Use first half with 3 bits cleared
function getPublicKey(privKey) {
return getExtendedPublicKey(privKey).pointBytes;
}
// int('LE', SHA512(dom2(F, C) || msgs)) mod N
function hashDomainToScalar(context = new Uint8Array(), ...msgs) {
const msg = ut.concatBytes(...msgs);
return modN_LE(cHash(domain(msg, (0, utils_js_1.ensureBytes)('context', context), !!prehash)));
}
/** Signs message with privateKey. RFC8032 5.1.6 */
function sign(msg, privKey, options = {}) {
msg = (0, utils_js_1.ensureBytes)('message', msg);
if (prehash)
msg = prehash(msg); // for ed25519ph etc.
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privKey);
const r = hashDomainToScalar(options.context, prefix, msg); // r = dom2(F, C) || prefix || PH(M)
const R = G.multiply(r).toRawBytes(); // R = rG
const k = hashDomainToScalar(options.context, R, pointBytes, msg); // R || A || PH(M)
const s = modN(r + k * scalar); // S = (r + k * s) mod L
ut.aInRange('signature.s', s, _0n, CURVE_ORDER); // 0 <= s < l
const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES));
return (0, utils_js_1.ensureBytes)('result', res, Fp.BYTES * 2); // 64-byte signature
}
const verifyOpts = VERIFY_DEFAULT;
/**
* Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
* An extended group equation is checked.
*/
function verify(sig, msg, publicKey, options = verifyOpts) {
const { context, zip215 } = options;
const len = Fp.BYTES; // Verifies EdDSA signature against message and public key. RFC8032 5.1.7.
sig = (0, utils_js_1.ensureBytes)('signature', sig, 2 * len); // An extended group equation is checked.
msg = (0, utils_js_1.ensureBytes)('message', msg);
publicKey = (0, utils_js_1.ensureBytes)('publicKey', publicKey, len);
if (zip215 !== undefined)
(0, utils_js_1.abool)('zip215', zip215);
if (prehash)
msg = prehash(msg); // for ed25519ph, etc
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len));
let A, R, SB;
try {
// zip215=true is good for consensus-critical apps. =false follows RFC8032 / NIST186-5.
// zip215=true: 0 <= y < MASK (2^256 for ed25519)
// zip215=false: 0 <= y < P (2^255-19 for ed25519)
A = Point.fromHex(publicKey, zip215);
R = Point.fromHex(sig.slice(0, len), zip215);
SB = G.multiplyUnsafe(s); // 0 <= s < l is done inside
}
catch (error) {
return false;
}
if (!zip215 && A.isSmallOrder())
return false;
const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg);
const RkA = R.add(A.multiplyUnsafe(k));
// Extended group equation
// [8][S]B = [8]R + [8][k]A'
return RkA.subtract(SB).clearCofactor().equals(Point.ZERO);
}
G._setWindowSize(8); // Enable precomputes. Slows down first publicKey computation by 20ms.
const utils = {
getExtendedPublicKey,
// ed25519 private keys are uniform 32b. No need to check for modulo bias, like in secp256k1.
randomPrivateKey: () => randomBytes(Fp.BYTES),
/**
* We're doing scalar multiplication (used in getPublicKey etc) with precomputed BASE_POINT
* values. This slows down first getPublicKey() by milliseconds (see Speed section),
* but allows to speed-up subsequent getPublicKey() calls up to 20x.
* @param windowSize 2, 4, 8, 16
*/
precompute(windowSize = 8, point = Point.BASE) {
point._setWindowSize(windowSize);
point.multiply(BigInt(3));
return point;
},
};
return {
CURVE,
getPublicKey,
sign,
verify,
ExtendedPoint: Point,
utils,
};
}
/***/ }),
/***/ "../../node_modules/@noble/curves/abstract/hash-to-curve.js":
/*!******************************************************************!*\
!*** ../../node_modules/@noble/curves/abstract/hash-to-curve.js ***!
\******************************************************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.expand_message_xmd = expand_message_xmd;
exports.expand_message_xof = expand_message_xof;
exports.hash_to_field = hash_to_field;
exports.isogenyMap = isogenyMap;
exports.createHasher = createHasher;
const modular_js_1 = __webpack_require__(/*! ./modular.js */ "../../node_modules/@noble/curves/abstract/modular.js");
const utils_js_1 = __webpack_require__(/*! ./utils.js */ "../../node_modules/@noble/curves/abstract/utils.js");
// Octet Stream to Integer. "spec" implementation of os2ip is 2.5x slower vs bytesToNumberBE.
const os2ip = utils_js_1.bytesToNumberBE;
// Integer to Octet Stream (numberToBytesBE)
function i2osp(value, length) {
anum(value);
anum(length);
if (value < 0 || value >= 1 << (8 * length))
throw new Error('invalid I2OSP input: ' + value);
const res = Array.from({ length }).fill(0);
for (let i = length - 1; i >= 0; i--) {
res[i] = value & 0xff;
value >>>= 8;
}
return new Uint8Array(res);
}
function strxor(a, b) {
const arr = new Uint8Array(a.length);
for (let i = 0; i < a.length; i++) {
arr[i] = a[i] ^ b[i];
}
return arr;
}
function anum(item) {
if (!Number.isSafeInteger(item))
throw new Error('number expected');
}
/**
* Produces a uniformly random byte string using a cryptographic hash function H that outputs b bits.
* [RFC 9380 5.3.1](https://www.rfc-editor.org/rfc/rfc9380#section-5.3.1).
*/
function expand_message_xmd(msg, DST, lenInBytes, H) {
(0, utils_js_1.abytes)(msg);
(0, utils_js_1.abytes)(DST);
anum(lenInBytes);
// https://www.rfc-editor.org/rfc/rfc9380#section-5.3.3
if (DST.length > 255)
DST = H((0, utils_js_1.concatBytes)((0, utils_js_1.utf8ToBytes)('H2C-OVERSIZE-DST-'), DST));
const { outputLen: b_in_bytes, blockLen: r_in_bytes } = H;
const ell = Math.ceil(lenInBytes / b_in_bytes);
if (lenInBytes > 65535 || ell > 255)
throw new Error('expand_message_xmd: invalid lenInBytes');
const DST_prime = (0, utils_js_1.concatBytes)(DST, i2osp(DST.length, 1));
const Z_pad = i2osp(0, r_in_bytes);
const l_i_b_str = i2osp(lenInBytes, 2); // len_in_bytes_str
const b = new Array(ell);
const b_0 = H((0, utils_js_1.concatBytes)(Z_pad, msg, l_i_b_str, i2osp(0, 1), DST_prime));
b[0] = H((0, utils_js_1.concatBytes)(b_0, i2osp(1, 1), DST_prime));
for (let i = 1; i <= ell; i++) {
const args = [strxor(b_0, b[i - 1]), i2osp(i + 1, 1), DST_prime];
b[i] = H((0, utils_js_1.concatBytes)(...args));
}
const pseudo_random_bytes = (0, utils_js_1.concatBytes)(...b);
return pseudo_random_bytes.slice(0, lenInBytes);
}
/**
* Produces a uniformly random byte string using an extendable-output function (XOF) H.
* 1. The collision resistance of H MUST be at least k bits.
* 2. H MUST be an XOF that has been proved indifferentiable from
* a random oracle under a reasonable cryptographic assumption.
* [RFC 9380 5.3.2](https://www.rfc-editor.org/rfc/rfc9380#section-5.3.2).
*/
function expand_message_xof(msg, DST, lenInBytes, k, H) {
(0, utils_js_1.abytes)(msg);
(0, utils_js_1.abytes)(DST);
anum(lenInBytes);
// https://www.rfc-editor.org/rfc/rfc9380#section-5.3.3
// DST = H('H2C-OVERSIZE-DST-' || a_very_long_DST, Math.ceil((lenInBytes * k) / 8));
if (DST.length > 255) {
const dkLen = Math.ceil((2 * k) / 8);
DST = H.create({ dkLen }).update((0, utils_js_1.utf8ToBytes)('H2C-OVERSIZE-DST-')).update(DST).digest();
}
if (lenInBytes > 65535 || DST.length > 255)
throw new Error('expand_message_xof: invalid lenInBytes');
return (H.create({ dkLen: lenInBytes })
.update(msg)
.update(i2osp(lenInBytes, 2))
// 2. DST_prime = DST || I2OSP(len(DST), 1)
.update(DST)
.update(i2osp(DST.length, 1))
.digest());
}
/**
* Hashes arbitrary-length byte strings to a list of one or more elements of a finite field F.
* [RFC 9380 5.2](https://www.rfc-editor.org/rfc/rfc9380#section-5.2).
* @param msg a byte string containing the message to hash
* @param count the number of elements of F to output
* @param options `{DST: string, p: bigint, m: number, k: number, expand: 'xmd' | 'xof', hash: H}`, see above
* @returns [u_0, ..., u_(count - 1)], a list of field elements.
*/
function hash_to_field(msg, count, options) {
(0, utils_js_1.validateObject)(options, {
DST: 'stringOrUint8Array',
p: 'bigint',
m: 'isSafeInteger',
k: 'isSafeInteger',
hash: 'hash',
});
const { p, k, m, hash, expand, DST: _DST } = options;
(0, utils_js_1.abytes)(msg);
anum(count);
const DST = typeof _DST === 'string' ? (0, utils_js_1.utf8ToBytes)(_DST) : _DST;
const log2p = p.toString(2).length;
const L = Math.ceil((log2p + k) / 8); // section 5.1 of ietf draft link above
const len_in_bytes = count * m * L;
let prb; // pseudo_random_bytes
if (expand === 'xmd') {
prb = expand_message_xmd(msg, DST, len_in_bytes, hash);
}
else if (expand === 'xof') {
prb = expand_message_xof(msg, DST, len_in_bytes, k, hash);
}
else if (expand === '_internal_pass') {
// for internal tests only
prb = msg;
}
else {
throw new Error('expand must be "xmd" or "xof"');
}
const u = new Array(count);
for (let i = 0; i < count; i++) {
const e = new Array(m);
for (let j = 0; j < m; j++) {
const elm_offset = L * (j + i * m);
const tv = prb.subarray(elm_offset, elm_offset + L);
e[j] = (0, modular_js_1.mod)(os2ip(tv), p);
}
u[i] = e;
}
return u;
}
function isogenyMap(field, map) {
// Make same order as in spec
const COEFF = map.map((i) => Array.from(i).reverse());
return (x, y) => {
const [xNum, xDen, yNum, yDen] = COEFF.map((val) => val.reduce((acc, i) => field.add(field.mul(acc, x), i)));
x = field.div(xNum, xDen); // xNum / xDen
y = field.mul(y, field.div(yNum, yDen)); // y * (yNum / yDev)
return { x: x, y: y };
};
}
/** Creates hash-to-curve methods from EC Point and mapToCurve function. */
function createHasher(Point, mapToCurve, def) {
if (typeof mapToCurve !== 'function')
throw new Error('mapToCurve() must be defined');
return {
// Encodes byte string to elliptic curve.
// hash_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
hashToCurve(msg, options) {
const u = hash_to_field(msg, 2, { ...def, DST: def.DST, ...options });
const u0 = Point.fromAffine(mapToCurve(u[0]));
const u1 = Point.fromAffine(mapToCurve(u[1]));
const P = u0.add(u1).clearCofactor();
P.assertValidity();
return P;
},
// Encodes byte string to elliptic curve.
// encode_to_curve from https://www.rfc-editor.org/rfc/rfc9380#section-3
encodeToCurve(msg, options) {
const u = hash_to_field(msg, 1, { ...def, DST: def.encodeDST, ...options });
const P = Point.fromAffine(mapToCurve(u[0])).clearCofactor();
P.assertValidity();
return P;
},
// Same as encodeToCurve, but without hash
mapToCurve(scalars) {
if (!Array.isArray(scalars))
throw new Error('mapToCurve: expected array of bigints');
for (const i of scalars)
if (typeof i !== 'bigint')
throw new Error('mapToCurve: expected array of bigints');
const P = Point.fromAffine(mapToCurve(scalars)).clearCofactor();
P.assertValidity();
return P;
},
};
}
/***/ }),
/***/ "../../node_modules/@noble/curves/abstract/modular.js":
/*!************************************************************!*\
!*** ../../node_modules/@noble/curves/abstract/modular.js ***!
\************************************************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.isNegativeLE = void 0;
exports.mod = mod;
exports.pow = pow;
exports.pow2 = pow2;
exports.invert = invert;
exports.tonelliShanks = tonelliShanks;
exports.FpSqrt = FpSqrt;
exports.validateField = validateField;
exports.FpPow = FpPow;
exports.FpInvertBatch = FpInvertBatch;
exports.FpDiv = FpDiv;
exports.FpLegendre = FpLegendre;
exports.FpIsSquare = FpIsSquare;
exports.nLength = nLength;
exports.Field = Field;
exports.FpSqrtOdd = FpSqrtOdd;
exports.FpSqrtEven = FpSqrtEven;
exports.hashToPrivateScalar = hashToPrivateScalar;
exports.getFieldBytesLength = getFieldBytesLength;
exports.getMinHashLength = getMinHashLength;
exports.mapHashToField = mapHashToField;
/**
* Utils for modular division and finite fields.
* A finite field over 11 is integer number operations `mod 11`.
* There is no division: it is replaced by modular multiplicative inverse.
* @module
*/
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
const utils_js_1 = __webpack_require__(/*! ./utils.js */ "../../node_modules/@noble/curves/abstract/utils.js");
// prettier-ignore
const _0n = BigInt(0), _1n = BigInt(1), _2n = /* @__PURE__ */ BigInt(2), _3n = /* @__PURE__ */ BigInt(3);
// prettier-ignore
const _4n = /* @__PURE__ */ BigInt(4), _5n = /* @__PURE__ */ BigInt(5), _8n = /* @__PURE__ */ BigInt(8);
// prettier-ignore
const _9n = /* @__PURE__ */ BigInt(9), _16n = /* @__PURE__ */ BigInt(16);
// Calculates a modulo b
function mod(a, b) {
const result = a % b;
return result >= _0n ? result : b + result;
}
/**
* Efficiently raise num to power and do modular division.
* Unsafe in some contexts: uses ladder, so can expose bigint bits.
* @todo use field version && remove
* @example
* pow(2n, 6n, 11n) // 64n % 11n == 9n
*/
function pow(num, power, modulo) {
if (power < _0n)
throw new Error('invalid exponent, negatives unsupported');
if (modulo <= _0n)
throw new Error('invalid modulus');
if (modulo === _1n)
return _0n;
let res = _1n;
while (power > _0n) {
if (power & _1n)
res = (res * num) % modulo;
num = (num * num) % modulo;
power >>= _1n;
}
return res;
}
/