7041 lines
235 KiB
JavaScript
7041 lines
235 KiB
JavaScript
|
|
var __create = Object.create;
|
|||
|
|
var __defProp = Object.defineProperty;
|
|||
|
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|||
|
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|||
|
|
var __getProtoOf = Object.getPrototypeOf;
|
|||
|
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|||
|
|
var __esm = (fn, res, err) => function __init() {
|
|||
|
|
if (err) throw err[0];
|
|||
|
|
try {
|
|||
|
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|||
|
|
} catch (e) {
|
|||
|
|
throw err = [e], e;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
var __commonJS = (cb, mod2) => function __require() {
|
|||
|
|
try {
|
|||
|
|
return mod2 || (0, cb[__getOwnPropNames(cb)[0]])((mod2 = { exports: {} }).exports, mod2), mod2.exports;
|
|||
|
|
} catch (e) {
|
|||
|
|
throw mod2 = 0, e;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
var __export = (target, all) => {
|
|||
|
|
for (var name in all)
|
|||
|
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|||
|
|
};
|
|||
|
|
var __copyProps = (to, from, except, desc) => {
|
|||
|
|
if (from && typeof from === "object" || typeof from === "function") {
|
|||
|
|
for (let key of __getOwnPropNames(from))
|
|||
|
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|||
|
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|||
|
|
}
|
|||
|
|
return to;
|
|||
|
|
};
|
|||
|
|
var __toESM = (mod2, isNodeMode, target) => (target = mod2 != null ? __create(__getProtoOf(mod2)) : {}, __copyProps(
|
|||
|
|
// If the importer is in node compatibility mode or this is not an ESM
|
|||
|
|
// file that has been converted to a CommonJS file using a Babel-
|
|||
|
|
// compatible transform (i.e. "__esModule" has not been set), then set
|
|||
|
|
// "default" to the CommonJS "module.exports" for node compatibility.
|
|||
|
|
isNodeMode || !mod2 || !mod2.__esModule ? __defProp(target, "default", { value: mod2, enumerable: true }) : target,
|
|||
|
|
mod2
|
|||
|
|
));
|
|||
|
|
|
|||
|
|
// node_modules/@wizardconnect/core/dist/primitives.js
|
|||
|
|
function unwrap(value) {
|
|||
|
|
if (typeof value === "string") {
|
|||
|
|
throw new Error(`unwrap: ${value}`);
|
|||
|
|
}
|
|||
|
|
if (value instanceof Error) {
|
|||
|
|
throw value;
|
|||
|
|
}
|
|||
|
|
return value;
|
|||
|
|
}
|
|||
|
|
var throwUnless;
|
|||
|
|
var init_primitives = __esm({
|
|||
|
|
"node_modules/@wizardconnect/core/dist/primitives.js"() {
|
|||
|
|
throwUnless = (x, what) => {
|
|||
|
|
if (!x) {
|
|||
|
|
throw Error(`Internal application error: ${what}`);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@noble/hashes/utils.js
|
|||
|
|
function isBytes(a) {
|
|||
|
|
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
|
|||
|
|
}
|
|||
|
|
function anumber(n, title = "") {
|
|||
|
|
if (!Number.isSafeInteger(n) || n < 0) {
|
|||
|
|
const prefix = title && `"${title}" `;
|
|||
|
|
throw new Error(`${prefix}expected integer >= 0, got ${n}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function abytes(value, length, title = "") {
|
|||
|
|
const bytes = isBytes(value);
|
|||
|
|
const len = value?.length;
|
|||
|
|
const needsLen = length !== void 0;
|
|||
|
|
if (!bytes || needsLen && len !== length) {
|
|||
|
|
const prefix = title && `"${title}" `;
|
|||
|
|
const ofLen = needsLen ? ` of length ${length}` : "";
|
|||
|
|
const got = bytes ? `length=${len}` : `type=${typeof value}`;
|
|||
|
|
throw new Error(prefix + "expected Uint8Array" + ofLen + ", got " + got);
|
|||
|
|
}
|
|||
|
|
return value;
|
|||
|
|
}
|
|||
|
|
function ahash(h) {
|
|||
|
|
if (typeof h !== "function" || typeof h.create !== "function")
|
|||
|
|
throw new Error("Hash must wrapped by utils.createHasher");
|
|||
|
|
anumber(h.outputLen);
|
|||
|
|
anumber(h.blockLen);
|
|||
|
|
}
|
|||
|
|
function aexists(instance, checkFinished = true) {
|
|||
|
|
if (instance.destroyed)
|
|||
|
|
throw new Error("Hash instance has been destroyed");
|
|||
|
|
if (checkFinished && instance.finished)
|
|||
|
|
throw new Error("Hash#digest() has already been called");
|
|||
|
|
}
|
|||
|
|
function aoutput(out, instance) {
|
|||
|
|
abytes(out, void 0, "digestInto() output");
|
|||
|
|
const min = instance.outputLen;
|
|||
|
|
if (out.length < min) {
|
|||
|
|
throw new Error('"digestInto() output" expected to be of length >=' + min);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function clean(...arrays) {
|
|||
|
|
for (let i3 = 0; i3 < arrays.length; i3++) {
|
|||
|
|
arrays[i3].fill(0);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function createView(arr) {
|
|||
|
|
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|||
|
|
}
|
|||
|
|
function rotr(word, shift) {
|
|||
|
|
return word << 32 - shift | word >>> shift;
|
|||
|
|
}
|
|||
|
|
function bytesToHex(bytes) {
|
|||
|
|
abytes(bytes);
|
|||
|
|
if (hasHexBuiltin)
|
|||
|
|
return bytes.toHex();
|
|||
|
|
let hex = "";
|
|||
|
|
for (let i3 = 0; i3 < bytes.length; i3++) {
|
|||
|
|
hex += hexes[bytes[i3]];
|
|||
|
|
}
|
|||
|
|
return hex;
|
|||
|
|
}
|
|||
|
|
function asciiToBase16(ch) {
|
|||
|
|
if (ch >= asciis._0 && ch <= asciis._9)
|
|||
|
|
return ch - asciis._0;
|
|||
|
|
if (ch >= asciis.A && ch <= asciis.F)
|
|||
|
|
return ch - (asciis.A - 10);
|
|||
|
|
if (ch >= asciis.a && ch <= asciis.f)
|
|||
|
|
return ch - (asciis.a - 10);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
function hexToBytes(hex) {
|
|||
|
|
if (typeof hex !== "string")
|
|||
|
|
throw new Error("hex string expected, got " + typeof hex);
|
|||
|
|
if (hasHexBuiltin)
|
|||
|
|
return Uint8Array.fromHex(hex);
|
|||
|
|
const hl = hex.length;
|
|||
|
|
const al = hl / 2;
|
|||
|
|
if (hl % 2)
|
|||
|
|
throw new Error("hex string expected, got unpadded hex of length " + hl);
|
|||
|
|
const array = new Uint8Array(al);
|
|||
|
|
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
|
|||
|
|
const n1 = asciiToBase16(hex.charCodeAt(hi));
|
|||
|
|
const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
|
|||
|
|
if (n1 === void 0 || n2 === void 0) {
|
|||
|
|
const char = hex[hi] + hex[hi + 1];
|
|||
|
|
throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi);
|
|||
|
|
}
|
|||
|
|
array[ai] = n1 * 16 + n2;
|
|||
|
|
}
|
|||
|
|
return array;
|
|||
|
|
}
|
|||
|
|
function concatBytes(...arrays) {
|
|||
|
|
let sum = 0;
|
|||
|
|
for (let i3 = 0; i3 < arrays.length; i3++) {
|
|||
|
|
const a = arrays[i3];
|
|||
|
|
abytes(a);
|
|||
|
|
sum += a.length;
|
|||
|
|
}
|
|||
|
|
const res = new Uint8Array(sum);
|
|||
|
|
for (let i3 = 0, pad2 = 0; i3 < arrays.length; i3++) {
|
|||
|
|
const a = arrays[i3];
|
|||
|
|
res.set(a, pad2);
|
|||
|
|
pad2 += a.length;
|
|||
|
|
}
|
|||
|
|
return res;
|
|||
|
|
}
|
|||
|
|
function createHasher(hashCons, info = {}) {
|
|||
|
|
const hashC = (msg, opts) => hashCons(opts).update(msg).digest();
|
|||
|
|
const tmp = hashCons(void 0);
|
|||
|
|
hashC.outputLen = tmp.outputLen;
|
|||
|
|
hashC.blockLen = tmp.blockLen;
|
|||
|
|
hashC.create = (opts) => hashCons(opts);
|
|||
|
|
Object.assign(hashC, info);
|
|||
|
|
return Object.freeze(hashC);
|
|||
|
|
}
|
|||
|
|
function randomBytes(bytesLength = 32) {
|
|||
|
|
const cr = typeof globalThis === "object" ? globalThis.crypto : null;
|
|||
|
|
if (typeof cr?.getRandomValues !== "function")
|
|||
|
|
throw new Error("crypto.getRandomValues must be defined");
|
|||
|
|
return cr.getRandomValues(new Uint8Array(bytesLength));
|
|||
|
|
}
|
|||
|
|
var hasHexBuiltin, hexes, asciis, oidNist;
|
|||
|
|
var init_utils = __esm({
|
|||
|
|
"node_modules/@noble/hashes/utils.js"() {
|
|||
|
|
hasHexBuiltin = /* @__PURE__ */ (() => (
|
|||
|
|
// @ts-ignore
|
|||
|
|
typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function"
|
|||
|
|
))();
|
|||
|
|
hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i3) => i3.toString(16).padStart(2, "0"));
|
|||
|
|
asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 };
|
|||
|
|
oidNist = (suffix) => ({
|
|||
|
|
oid: Uint8Array.from([6, 9, 96, 134, 72, 1, 101, 3, 4, 2, suffix])
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@noble/hashes/_md.js
|
|||
|
|
function Chi(a, b, c) {
|
|||
|
|
return a & b ^ ~a & c;
|
|||
|
|
}
|
|||
|
|
function Maj(a, b, c) {
|
|||
|
|
return a & b ^ a & c ^ b & c;
|
|||
|
|
}
|
|||
|
|
var HashMD, SHA256_IV;
|
|||
|
|
var init_md = __esm({
|
|||
|
|
"node_modules/@noble/hashes/_md.js"() {
|
|||
|
|
init_utils();
|
|||
|
|
HashMD = class {
|
|||
|
|
blockLen;
|
|||
|
|
outputLen;
|
|||
|
|
padOffset;
|
|||
|
|
isLE;
|
|||
|
|
// For partial updates less than block size
|
|||
|
|
buffer;
|
|||
|
|
view;
|
|||
|
|
finished = false;
|
|||
|
|
length = 0;
|
|||
|
|
pos = 0;
|
|||
|
|
destroyed = false;
|
|||
|
|
constructor(blockLen, outputLen, padOffset, isLE2) {
|
|||
|
|
this.blockLen = blockLen;
|
|||
|
|
this.outputLen = outputLen;
|
|||
|
|
this.padOffset = padOffset;
|
|||
|
|
this.isLE = isLE2;
|
|||
|
|
this.buffer = new Uint8Array(blockLen);
|
|||
|
|
this.view = createView(this.buffer);
|
|||
|
|
}
|
|||
|
|
update(data) {
|
|||
|
|
aexists(this);
|
|||
|
|
abytes(data);
|
|||
|
|
const { view, buffer, blockLen } = this;
|
|||
|
|
const len = data.length;
|
|||
|
|
for (let pos = 0; pos < len; ) {
|
|||
|
|
const take = Math.min(blockLen - this.pos, len - pos);
|
|||
|
|
if (take === blockLen) {
|
|||
|
|
const dataView = createView(data);
|
|||
|
|
for (; blockLen <= len - pos; pos += blockLen)
|
|||
|
|
this.process(dataView, pos);
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
buffer.set(data.subarray(pos, pos + take), this.pos);
|
|||
|
|
this.pos += take;
|
|||
|
|
pos += take;
|
|||
|
|
if (this.pos === blockLen) {
|
|||
|
|
this.process(view, 0);
|
|||
|
|
this.pos = 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
this.length += data.length;
|
|||
|
|
this.roundClean();
|
|||
|
|
return this;
|
|||
|
|
}
|
|||
|
|
digestInto(out) {
|
|||
|
|
aexists(this);
|
|||
|
|
aoutput(out, this);
|
|||
|
|
this.finished = true;
|
|||
|
|
const { buffer, view, blockLen, isLE: isLE2 } = this;
|
|||
|
|
let { pos } = this;
|
|||
|
|
buffer[pos++] = 128;
|
|||
|
|
clean(this.buffer.subarray(pos));
|
|||
|
|
if (this.padOffset > blockLen - pos) {
|
|||
|
|
this.process(view, 0);
|
|||
|
|
pos = 0;
|
|||
|
|
}
|
|||
|
|
for (let i3 = pos; i3 < blockLen; i3++)
|
|||
|
|
buffer[i3] = 0;
|
|||
|
|
view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE2);
|
|||
|
|
this.process(view, 0);
|
|||
|
|
const oview = createView(out);
|
|||
|
|
const len = this.outputLen;
|
|||
|
|
if (len % 4)
|
|||
|
|
throw new Error("_sha2: outputLen must be aligned to 32bit");
|
|||
|
|
const outLen = len / 4;
|
|||
|
|
const state = this.get();
|
|||
|
|
if (outLen > state.length)
|
|||
|
|
throw new Error("_sha2: outputLen bigger than state");
|
|||
|
|
for (let i3 = 0; i3 < outLen; i3++)
|
|||
|
|
oview.setUint32(4 * i3, state[i3], isLE2);
|
|||
|
|
}
|
|||
|
|
digest() {
|
|||
|
|
const { buffer, outputLen } = this;
|
|||
|
|
this.digestInto(buffer);
|
|||
|
|
const res = buffer.slice(0, outputLen);
|
|||
|
|
this.destroy();
|
|||
|
|
return res;
|
|||
|
|
}
|
|||
|
|
_cloneInto(to) {
|
|||
|
|
to ||= new this.constructor();
|
|||
|
|
to.set(...this.get());
|
|||
|
|
const { blockLen, buffer, length, finished, destroyed, pos } = this;
|
|||
|
|
to.destroyed = destroyed;
|
|||
|
|
to.finished = finished;
|
|||
|
|
to.length = length;
|
|||
|
|
to.pos = pos;
|
|||
|
|
if (length % blockLen)
|
|||
|
|
to.buffer.set(buffer);
|
|||
|
|
return to;
|
|||
|
|
}
|
|||
|
|
clone() {
|
|||
|
|
return this._cloneInto();
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
SHA256_IV = /* @__PURE__ */ Uint32Array.from([
|
|||
|
|
1779033703,
|
|||
|
|
3144134277,
|
|||
|
|
1013904242,
|
|||
|
|
2773480762,
|
|||
|
|
1359893119,
|
|||
|
|
2600822924,
|
|||
|
|
528734635,
|
|||
|
|
1541459225
|
|||
|
|
]);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@noble/hashes/sha2.js
|
|||
|
|
var SHA256_K, SHA256_W, SHA2_32B, _SHA256, sha256;
|
|||
|
|
var init_sha2 = __esm({
|
|||
|
|
"node_modules/@noble/hashes/sha2.js"() {
|
|||
|
|
init_md();
|
|||
|
|
init_utils();
|
|||
|
|
SHA256_K = /* @__PURE__ */ Uint32Array.from([
|
|||
|
|
1116352408,
|
|||
|
|
1899447441,
|
|||
|
|
3049323471,
|
|||
|
|
3921009573,
|
|||
|
|
961987163,
|
|||
|
|
1508970993,
|
|||
|
|
2453635748,
|
|||
|
|
2870763221,
|
|||
|
|
3624381080,
|
|||
|
|
310598401,
|
|||
|
|
607225278,
|
|||
|
|
1426881987,
|
|||
|
|
1925078388,
|
|||
|
|
2162078206,
|
|||
|
|
2614888103,
|
|||
|
|
3248222580,
|
|||
|
|
3835390401,
|
|||
|
|
4022224774,
|
|||
|
|
264347078,
|
|||
|
|
604807628,
|
|||
|
|
770255983,
|
|||
|
|
1249150122,
|
|||
|
|
1555081692,
|
|||
|
|
1996064986,
|
|||
|
|
2554220882,
|
|||
|
|
2821834349,
|
|||
|
|
2952996808,
|
|||
|
|
3210313671,
|
|||
|
|
3336571891,
|
|||
|
|
3584528711,
|
|||
|
|
113926993,
|
|||
|
|
338241895,
|
|||
|
|
666307205,
|
|||
|
|
773529912,
|
|||
|
|
1294757372,
|
|||
|
|
1396182291,
|
|||
|
|
1695183700,
|
|||
|
|
1986661051,
|
|||
|
|
2177026350,
|
|||
|
|
2456956037,
|
|||
|
|
2730485921,
|
|||
|
|
2820302411,
|
|||
|
|
3259730800,
|
|||
|
|
3345764771,
|
|||
|
|
3516065817,
|
|||
|
|
3600352804,
|
|||
|
|
4094571909,
|
|||
|
|
275423344,
|
|||
|
|
430227734,
|
|||
|
|
506948616,
|
|||
|
|
659060556,
|
|||
|
|
883997877,
|
|||
|
|
958139571,
|
|||
|
|
1322822218,
|
|||
|
|
1537002063,
|
|||
|
|
1747873779,
|
|||
|
|
1955562222,
|
|||
|
|
2024104815,
|
|||
|
|
2227730452,
|
|||
|
|
2361852424,
|
|||
|
|
2428436474,
|
|||
|
|
2756734187,
|
|||
|
|
3204031479,
|
|||
|
|
3329325298
|
|||
|
|
]);
|
|||
|
|
SHA256_W = /* @__PURE__ */ new Uint32Array(64);
|
|||
|
|
SHA2_32B = class extends HashMD {
|
|||
|
|
constructor(outputLen) {
|
|||
|
|
super(64, outputLen, 8, false);
|
|||
|
|
}
|
|||
|
|
get() {
|
|||
|
|
const { A, B, C, D, E, F, G, H } = this;
|
|||
|
|
return [A, B, C, D, E, F, G, H];
|
|||
|
|
}
|
|||
|
|
// prettier-ignore
|
|||
|
|
set(A, B, C, D, E, F, G, H) {
|
|||
|
|
this.A = A | 0;
|
|||
|
|
this.B = B | 0;
|
|||
|
|
this.C = C | 0;
|
|||
|
|
this.D = D | 0;
|
|||
|
|
this.E = E | 0;
|
|||
|
|
this.F = F | 0;
|
|||
|
|
this.G = G | 0;
|
|||
|
|
this.H = H | 0;
|
|||
|
|
}
|
|||
|
|
process(view, offset) {
|
|||
|
|
for (let i3 = 0; i3 < 16; i3++, offset += 4)
|
|||
|
|
SHA256_W[i3] = view.getUint32(offset, false);
|
|||
|
|
for (let i3 = 16; i3 < 64; i3++) {
|
|||
|
|
const W15 = SHA256_W[i3 - 15];
|
|||
|
|
const W2 = SHA256_W[i3 - 2];
|
|||
|
|
const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3;
|
|||
|
|
const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10;
|
|||
|
|
SHA256_W[i3] = s1 + SHA256_W[i3 - 7] + s0 + SHA256_W[i3 - 16] | 0;
|
|||
|
|
}
|
|||
|
|
let { A, B, C, D, E, F, G, H } = this;
|
|||
|
|
for (let i3 = 0; i3 < 64; i3++) {
|
|||
|
|
const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
|
|||
|
|
const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i3] + SHA256_W[i3] | 0;
|
|||
|
|
const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
|
|||
|
|
const T2 = sigma0 + Maj(A, B, C) | 0;
|
|||
|
|
H = G;
|
|||
|
|
G = F;
|
|||
|
|
F = E;
|
|||
|
|
E = D + T1 | 0;
|
|||
|
|
D = C;
|
|||
|
|
C = B;
|
|||
|
|
B = A;
|
|||
|
|
A = T1 + T2 | 0;
|
|||
|
|
}
|
|||
|
|
A = A + this.A | 0;
|
|||
|
|
B = B + this.B | 0;
|
|||
|
|
C = C + this.C | 0;
|
|||
|
|
D = D + this.D | 0;
|
|||
|
|
E = E + this.E | 0;
|
|||
|
|
F = F + this.F | 0;
|
|||
|
|
G = G + this.G | 0;
|
|||
|
|
H = H + this.H | 0;
|
|||
|
|
this.set(A, B, C, D, E, F, G, H);
|
|||
|
|
}
|
|||
|
|
roundClean() {
|
|||
|
|
clean(SHA256_W);
|
|||
|
|
}
|
|||
|
|
destroy() {
|
|||
|
|
this.set(0, 0, 0, 0, 0, 0, 0, 0);
|
|||
|
|
clean(this.buffer);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
_SHA256 = class extends SHA2_32B {
|
|||
|
|
// We cannot use array here since array allows indexing by variable
|
|||
|
|
// which means optimizer/compiler cannot use registers.
|
|||
|
|
A = SHA256_IV[0] | 0;
|
|||
|
|
B = SHA256_IV[1] | 0;
|
|||
|
|
C = SHA256_IV[2] | 0;
|
|||
|
|
D = SHA256_IV[3] | 0;
|
|||
|
|
E = SHA256_IV[4] | 0;
|
|||
|
|
F = SHA256_IV[5] | 0;
|
|||
|
|
G = SHA256_IV[6] | 0;
|
|||
|
|
H = SHA256_IV[7] | 0;
|
|||
|
|
constructor() {
|
|||
|
|
super(32);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
sha256 = /* @__PURE__ */ createHasher(
|
|||
|
|
() => new _SHA256(),
|
|||
|
|
/* @__PURE__ */ oidNist(1)
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@noble/curves/utils.js
|
|||
|
|
function abool(value, title = "") {
|
|||
|
|
if (typeof value !== "boolean") {
|
|||
|
|
const prefix = title && `"${title}" `;
|
|||
|
|
throw new Error(prefix + "expected boolean, got type=" + typeof value);
|
|||
|
|
}
|
|||
|
|
return value;
|
|||
|
|
}
|
|||
|
|
function abignumber(n) {
|
|||
|
|
if (typeof n === "bigint") {
|
|||
|
|
if (!isPosBig(n))
|
|||
|
|
throw new Error("positive bigint expected, got " + n);
|
|||
|
|
} else
|
|||
|
|
anumber(n);
|
|||
|
|
return n;
|
|||
|
|
}
|
|||
|
|
function numberToHexUnpadded(num2) {
|
|||
|
|
const hex = abignumber(num2).toString(16);
|
|||
|
|
return hex.length & 1 ? "0" + hex : hex;
|
|||
|
|
}
|
|||
|
|
function hexToNumber(hex) {
|
|||
|
|
if (typeof hex !== "string")
|
|||
|
|
throw new Error("hex string expected, got " + typeof hex);
|
|||
|
|
return hex === "" ? _0n : BigInt("0x" + hex);
|
|||
|
|
}
|
|||
|
|
function bytesToNumberBE(bytes) {
|
|||
|
|
return hexToNumber(bytesToHex(bytes));
|
|||
|
|
}
|
|||
|
|
function bytesToNumberLE(bytes) {
|
|||
|
|
return hexToNumber(bytesToHex(copyBytes(abytes(bytes)).reverse()));
|
|||
|
|
}
|
|||
|
|
function numberToBytesBE(n, len) {
|
|||
|
|
anumber(len);
|
|||
|
|
n = abignumber(n);
|
|||
|
|
const res = hexToBytes(n.toString(16).padStart(len * 2, "0"));
|
|||
|
|
if (res.length !== len)
|
|||
|
|
throw new Error("number too large");
|
|||
|
|
return res;
|
|||
|
|
}
|
|||
|
|
function numberToBytesLE(n, len) {
|
|||
|
|
return numberToBytesBE(n, len).reverse();
|
|||
|
|
}
|
|||
|
|
function copyBytes(bytes) {
|
|||
|
|
return Uint8Array.from(bytes);
|
|||
|
|
}
|
|||
|
|
function asciiToBytes(ascii) {
|
|||
|
|
return Uint8Array.from(ascii, (c, i3) => {
|
|||
|
|
const charCode = c.charCodeAt(0);
|
|||
|
|
if (c.length !== 1 || charCode > 127) {
|
|||
|
|
throw new Error(`string contains non-ASCII character "${ascii[i3]}" with code ${charCode} at position ${i3}`);
|
|||
|
|
}
|
|||
|
|
return charCode;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
function inRange(n, min, max) {
|
|||
|
|
return isPosBig(n) && isPosBig(min) && isPosBig(max) && min <= n && n < max;
|
|||
|
|
}
|
|||
|
|
function aInRange(title, n, min, max) {
|
|||
|
|
if (!inRange(n, min, max))
|
|||
|
|
throw new Error("expected valid " + title + ": " + min + " <= n < " + max + ", got " + n);
|
|||
|
|
}
|
|||
|
|
function bitLen(n) {
|
|||
|
|
let len;
|
|||
|
|
for (len = 0; n > _0n; n >>= _1n, len += 1)
|
|||
|
|
;
|
|||
|
|
return len;
|
|||
|
|
}
|
|||
|
|
function createHmacDrbg(hashLen, qByteLen, hmacFn) {
|
|||
|
|
anumber(hashLen, "hashLen");
|
|||
|
|
anumber(qByteLen, "qByteLen");
|
|||
|
|
if (typeof hmacFn !== "function")
|
|||
|
|
throw new Error("hmacFn must be a function");
|
|||
|
|
const u8n = (len) => new Uint8Array(len);
|
|||
|
|
const NULL = Uint8Array.of();
|
|||
|
|
const byte0 = Uint8Array.of(0);
|
|||
|
|
const byte1 = Uint8Array.of(1);
|
|||
|
|
const _maxDrbgIters = 1e3;
|
|||
|
|
let v = u8n(hashLen);
|
|||
|
|
let k = u8n(hashLen);
|
|||
|
|
let i3 = 0;
|
|||
|
|
const reset = () => {
|
|||
|
|
v.fill(1);
|
|||
|
|
k.fill(0);
|
|||
|
|
i3 = 0;
|
|||
|
|
};
|
|||
|
|
const h = (...msgs) => hmacFn(k, concatBytes(v, ...msgs));
|
|||
|
|
const reseed = (seed = NULL) => {
|
|||
|
|
k = h(byte0, seed);
|
|||
|
|
v = h();
|
|||
|
|
if (seed.length === 0)
|
|||
|
|
return;
|
|||
|
|
k = h(byte1, seed);
|
|||
|
|
v = h();
|
|||
|
|
};
|
|||
|
|
const gen = () => {
|
|||
|
|
if (i3++ >= _maxDrbgIters)
|
|||
|
|
throw new Error("drbg: tried max amount of iterations");
|
|||
|
|
let len = 0;
|
|||
|
|
const out = [];
|
|||
|
|
while (len < qByteLen) {
|
|||
|
|
v = h();
|
|||
|
|
const sl = v.slice();
|
|||
|
|
out.push(sl);
|
|||
|
|
len += v.length;
|
|||
|
|
}
|
|||
|
|
return concatBytes(...out);
|
|||
|
|
};
|
|||
|
|
const genUntil = (seed, pred) => {
|
|||
|
|
reset();
|
|||
|
|
reseed(seed);
|
|||
|
|
let res = void 0;
|
|||
|
|
while (!(res = pred(gen())))
|
|||
|
|
reseed();
|
|||
|
|
reset();
|
|||
|
|
return res;
|
|||
|
|
};
|
|||
|
|
return genUntil;
|
|||
|
|
}
|
|||
|
|
function validateObject(object, fields = {}, optFields = {}) {
|
|||
|
|
if (!object || typeof object !== "object")
|
|||
|
|
throw new Error("expected valid options object");
|
|||
|
|
function checkField(fieldName, expectedType, isOpt) {
|
|||
|
|
const val = object[fieldName];
|
|||
|
|
if (isOpt && val === void 0)
|
|||
|
|
return;
|
|||
|
|
const current = typeof val;
|
|||
|
|
if (current !== expectedType || val === null)
|
|||
|
|
throw new Error(`param "${fieldName}" is invalid: expected ${expectedType}, got ${current}`);
|
|||
|
|
}
|
|||
|
|
const iter = (f, isOpt) => Object.entries(f).forEach(([k, v]) => checkField(k, v, isOpt));
|
|||
|
|
iter(fields, false);
|
|||
|
|
iter(optFields, true);
|
|||
|
|
}
|
|||
|
|
function memoized(fn) {
|
|||
|
|
const map = /* @__PURE__ */ new WeakMap();
|
|||
|
|
return (arg, ...args) => {
|
|||
|
|
const val = map.get(arg);
|
|||
|
|
if (val !== void 0)
|
|||
|
|
return val;
|
|||
|
|
const computed = fn(arg, ...args);
|
|||
|
|
map.set(arg, computed);
|
|||
|
|
return computed;
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
var _0n, _1n, isPosBig, bitMask;
|
|||
|
|
var init_utils2 = __esm({
|
|||
|
|
"node_modules/@noble/curves/utils.js"() {
|
|||
|
|
init_utils();
|
|||
|
|
init_utils();
|
|||
|
|
_0n = /* @__PURE__ */ BigInt(0);
|
|||
|
|
_1n = /* @__PURE__ */ BigInt(1);
|
|||
|
|
isPosBig = (n) => typeof n === "bigint" && _0n <= n;
|
|||
|
|
bitMask = (n) => (_1n << BigInt(n)) - _1n;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@noble/curves/abstract/modular.js
|
|||
|
|
function mod(a, b) {
|
|||
|
|
const result = a % b;
|
|||
|
|
return result >= _0n2 ? result : b + result;
|
|||
|
|
}
|
|||
|
|
function pow2(x, power, modulo) {
|
|||
|
|
let res = x;
|
|||
|
|
while (power-- > _0n2) {
|
|||
|
|
res *= res;
|
|||
|
|
res %= modulo;
|
|||
|
|
}
|
|||
|
|
return res;
|
|||
|
|
}
|
|||
|
|
function invert(number, modulo) {
|
|||
|
|
if (number === _0n2)
|
|||
|
|
throw new Error("invert: expected non-zero number");
|
|||
|
|
if (modulo <= _0n2)
|
|||
|
|
throw new Error("invert: expected positive modulus, got " + modulo);
|
|||
|
|
let a = mod(number, modulo);
|
|||
|
|
let b = modulo;
|
|||
|
|
let x = _0n2, y = _1n2, u = _1n2, v = _0n2;
|
|||
|
|
while (a !== _0n2) {
|
|||
|
|
const q = b / a;
|
|||
|
|
const r = b % a;
|
|||
|
|
const m = x - u * q;
|
|||
|
|
const n = y - v * q;
|
|||
|
|
b = a, a = r, x = u, y = v, u = m, v = n;
|
|||
|
|
}
|
|||
|
|
const gcd2 = b;
|
|||
|
|
if (gcd2 !== _1n2)
|
|||
|
|
throw new Error("invert: does not exist");
|
|||
|
|
return mod(x, modulo);
|
|||
|
|
}
|
|||
|
|
function assertIsSquare(Fp, root, n) {
|
|||
|
|
if (!Fp.eql(Fp.sqr(root), n))
|
|||
|
|
throw new Error("Cannot find square root");
|
|||
|
|
}
|
|||
|
|
function sqrt3mod4(Fp, n) {
|
|||
|
|
const p1div4 = (Fp.ORDER + _1n2) / _4n;
|
|||
|
|
const root = Fp.pow(n, p1div4);
|
|||
|
|
assertIsSquare(Fp, root, n);
|
|||
|
|
return root;
|
|||
|
|
}
|
|||
|
|
function sqrt5mod8(Fp, n) {
|
|||
|
|
const p5div8 = (Fp.ORDER - _5n) / _8n;
|
|||
|
|
const n2 = Fp.mul(n, _2n);
|
|||
|
|
const v = Fp.pow(n2, p5div8);
|
|||
|
|
const nv = Fp.mul(n, v);
|
|||
|
|
const i3 = Fp.mul(Fp.mul(nv, _2n), v);
|
|||
|
|
const root = Fp.mul(nv, Fp.sub(i3, Fp.ONE));
|
|||
|
|
assertIsSquare(Fp, root, n);
|
|||
|
|
return root;
|
|||
|
|
}
|
|||
|
|
function sqrt9mod16(P) {
|
|||
|
|
const Fp_ = Field(P);
|
|||
|
|
const tn = tonelliShanks(P);
|
|||
|
|
const c1 = tn(Fp_, Fp_.neg(Fp_.ONE));
|
|||
|
|
const c2 = tn(Fp_, c1);
|
|||
|
|
const c3 = tn(Fp_, Fp_.neg(c1));
|
|||
|
|
const c4 = (P + _7n) / _16n;
|
|||
|
|
return (Fp, n) => {
|
|||
|
|
let tv1 = Fp.pow(n, c4);
|
|||
|
|
let tv2 = Fp.mul(tv1, c1);
|
|||
|
|
const tv3 = Fp.mul(tv1, c2);
|
|||
|
|
const tv4 = Fp.mul(tv1, c3);
|
|||
|
|
const e1 = Fp.eql(Fp.sqr(tv2), n);
|
|||
|
|
const e2 = Fp.eql(Fp.sqr(tv3), n);
|
|||
|
|
tv1 = Fp.cmov(tv1, tv2, e1);
|
|||
|
|
tv2 = Fp.cmov(tv4, tv3, e2);
|
|||
|
|
const e3 = Fp.eql(Fp.sqr(tv2), n);
|
|||
|
|
const root = Fp.cmov(tv1, tv2, e3);
|
|||
|
|
assertIsSquare(Fp, root, n);
|
|||
|
|
return root;
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
function tonelliShanks(P) {
|
|||
|
|
if (P < _3n)
|
|||
|
|
throw new Error("sqrt is not defined for small field");
|
|||
|
|
let Q = P - _1n2;
|
|||
|
|
let S = 0;
|
|||
|
|
while (Q % _2n === _0n2) {
|
|||
|
|
Q /= _2n;
|
|||
|
|
S++;
|
|||
|
|
}
|
|||
|
|
let Z = _2n;
|
|||
|
|
const _Fp = Field(P);
|
|||
|
|
while (FpLegendre(_Fp, Z) === 1) {
|
|||
|
|
if (Z++ > 1e3)
|
|||
|
|
throw new Error("Cannot find square root: probably non-prime P");
|
|||
|
|
}
|
|||
|
|
if (S === 1)
|
|||
|
|
return sqrt3mod4;
|
|||
|
|
let cc = _Fp.pow(Z, Q);
|
|||
|
|
const Q1div2 = (Q + _1n2) / _2n;
|
|||
|
|
return function tonelliSlow(Fp, n) {
|
|||
|
|
if (Fp.is0(n))
|
|||
|
|
return n;
|
|||
|
|
if (FpLegendre(Fp, n) !== 1)
|
|||
|
|
throw new Error("Cannot find square root");
|
|||
|
|
let M2 = S;
|
|||
|
|
let c = Fp.mul(Fp.ONE, cc);
|
|||
|
|
let t = Fp.pow(n, Q);
|
|||
|
|
let R = Fp.pow(n, Q1div2);
|
|||
|
|
while (!Fp.eql(t, Fp.ONE)) {
|
|||
|
|
if (Fp.is0(t))
|
|||
|
|
return Fp.ZERO;
|
|||
|
|
let i3 = 1;
|
|||
|
|
let t_tmp = Fp.sqr(t);
|
|||
|
|
while (!Fp.eql(t_tmp, Fp.ONE)) {
|
|||
|
|
i3++;
|
|||
|
|
t_tmp = Fp.sqr(t_tmp);
|
|||
|
|
if (i3 === M2)
|
|||
|
|
throw new Error("Cannot find square root");
|
|||
|
|
}
|
|||
|
|
const exponent = _1n2 << BigInt(M2 - i3 - 1);
|
|||
|
|
const b = Fp.pow(c, exponent);
|
|||
|
|
M2 = i3;
|
|||
|
|
c = Fp.sqr(b);
|
|||
|
|
t = Fp.mul(t, c);
|
|||
|
|
R = Fp.mul(R, b);
|
|||
|
|
}
|
|||
|
|
return R;
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
function FpSqrt(P) {
|
|||
|
|
if (P % _4n === _3n)
|
|||
|
|
return sqrt3mod4;
|
|||
|
|
if (P % _8n === _5n)
|
|||
|
|
return sqrt5mod8;
|
|||
|
|
if (P % _16n === _9n)
|
|||
|
|
return sqrt9mod16(P);
|
|||
|
|
return tonelliShanks(P);
|
|||
|
|
}
|
|||
|
|
function validateField(field) {
|
|||
|
|
const initial = {
|
|||
|
|
ORDER: "bigint",
|
|||
|
|
BYTES: "number",
|
|||
|
|
BITS: "number"
|
|||
|
|
};
|
|||
|
|
const opts = FIELD_FIELDS.reduce((map, val) => {
|
|||
|
|
map[val] = "function";
|
|||
|
|
return map;
|
|||
|
|
}, initial);
|
|||
|
|
validateObject(field, opts);
|
|||
|
|
return field;
|
|||
|
|
}
|
|||
|
|
function FpPow(Fp, num2, power) {
|
|||
|
|
if (power < _0n2)
|
|||
|
|
throw new Error("invalid exponent, negatives unsupported");
|
|||
|
|
if (power === _0n2)
|
|||
|
|
return Fp.ONE;
|
|||
|
|
if (power === _1n2)
|
|||
|
|
return num2;
|
|||
|
|
let p = Fp.ONE;
|
|||
|
|
let d = num2;
|
|||
|
|
while (power > _0n2) {
|
|||
|
|
if (power & _1n2)
|
|||
|
|
p = Fp.mul(p, d);
|
|||
|
|
d = Fp.sqr(d);
|
|||
|
|
power >>= _1n2;
|
|||
|
|
}
|
|||
|
|
return p;
|
|||
|
|
}
|
|||
|
|
function FpInvertBatch(Fp, nums, passZero = false) {
|
|||
|
|
const inverted = new Array(nums.length).fill(passZero ? Fp.ZERO : void 0);
|
|||
|
|
const multipliedAcc = nums.reduce((acc, num2, i3) => {
|
|||
|
|
if (Fp.is0(num2))
|
|||
|
|
return acc;
|
|||
|
|
inverted[i3] = acc;
|
|||
|
|
return Fp.mul(acc, num2);
|
|||
|
|
}, Fp.ONE);
|
|||
|
|
const invertedAcc = Fp.inv(multipliedAcc);
|
|||
|
|
nums.reduceRight((acc, num2, i3) => {
|
|||
|
|
if (Fp.is0(num2))
|
|||
|
|
return acc;
|
|||
|
|
inverted[i3] = Fp.mul(acc, inverted[i3]);
|
|||
|
|
return Fp.mul(acc, num2);
|
|||
|
|
}, invertedAcc);
|
|||
|
|
return inverted;
|
|||
|
|
}
|
|||
|
|
function FpLegendre(Fp, n) {
|
|||
|
|
const p1mod2 = (Fp.ORDER - _1n2) / _2n;
|
|||
|
|
const powered = Fp.pow(n, p1mod2);
|
|||
|
|
const yes = Fp.eql(powered, Fp.ONE);
|
|||
|
|
const zero = Fp.eql(powered, Fp.ZERO);
|
|||
|
|
const no = Fp.eql(powered, Fp.neg(Fp.ONE));
|
|||
|
|
if (!yes && !zero && !no)
|
|||
|
|
throw new Error("invalid Legendre symbol result");
|
|||
|
|
return yes ? 1 : zero ? 0 : -1;
|
|||
|
|
}
|
|||
|
|
function nLength(n, nBitLength) {
|
|||
|
|
if (nBitLength !== void 0)
|
|||
|
|
anumber(nBitLength);
|
|||
|
|
const _nBitLength = nBitLength !== void 0 ? nBitLength : n.toString(2).length;
|
|||
|
|
const nByteLength = Math.ceil(_nBitLength / 8);
|
|||
|
|
return { nBitLength: _nBitLength, nByteLength };
|
|||
|
|
}
|
|||
|
|
function Field(ORDER, opts = {}) {
|
|||
|
|
return new _Field(ORDER, opts);
|
|||
|
|
}
|
|||
|
|
function getFieldBytesLength(fieldOrder) {
|
|||
|
|
if (typeof fieldOrder !== "bigint")
|
|||
|
|
throw new Error("field order must be bigint");
|
|||
|
|
const bitLength = fieldOrder.toString(2).length;
|
|||
|
|
return Math.ceil(bitLength / 8);
|
|||
|
|
}
|
|||
|
|
function getMinHashLength(fieldOrder) {
|
|||
|
|
const length = getFieldBytesLength(fieldOrder);
|
|||
|
|
return length + Math.ceil(length / 2);
|
|||
|
|
}
|
|||
|
|
function mapHashToField(key, fieldOrder, isLE2 = false) {
|
|||
|
|
abytes(key);
|
|||
|
|
const len = key.length;
|
|||
|
|
const fieldLen = getFieldBytesLength(fieldOrder);
|
|||
|
|
const minLen = getMinHashLength(fieldOrder);
|
|||
|
|
if (len < 16 || len < minLen || len > 1024)
|
|||
|
|
throw new Error("expected " + minLen + "-1024 bytes of input, got " + len);
|
|||
|
|
const num2 = isLE2 ? bytesToNumberLE(key) : bytesToNumberBE(key);
|
|||
|
|
const reduced = mod(num2, fieldOrder - _1n2) + _1n2;
|
|||
|
|
return isLE2 ? numberToBytesLE(reduced, fieldLen) : numberToBytesBE(reduced, fieldLen);
|
|||
|
|
}
|
|||
|
|
var _0n2, _1n2, _2n, _3n, _4n, _5n, _7n, _8n, _9n, _16n, FIELD_FIELDS, _Field;
|
|||
|
|
var init_modular = __esm({
|
|||
|
|
"node_modules/@noble/curves/abstract/modular.js"() {
|
|||
|
|
init_utils2();
|
|||
|
|
_0n2 = /* @__PURE__ */ BigInt(0);
|
|||
|
|
_1n2 = /* @__PURE__ */ BigInt(1);
|
|||
|
|
_2n = /* @__PURE__ */ BigInt(2);
|
|||
|
|
_3n = /* @__PURE__ */ BigInt(3);
|
|||
|
|
_4n = /* @__PURE__ */ BigInt(4);
|
|||
|
|
_5n = /* @__PURE__ */ BigInt(5);
|
|||
|
|
_7n = /* @__PURE__ */ BigInt(7);
|
|||
|
|
_8n = /* @__PURE__ */ BigInt(8);
|
|||
|
|
_9n = /* @__PURE__ */ BigInt(9);
|
|||
|
|
_16n = /* @__PURE__ */ BigInt(16);
|
|||
|
|
FIELD_FIELDS = [
|
|||
|
|
"create",
|
|||
|
|
"isValid",
|
|||
|
|
"is0",
|
|||
|
|
"neg",
|
|||
|
|
"inv",
|
|||
|
|
"sqrt",
|
|||
|
|
"sqr",
|
|||
|
|
"eql",
|
|||
|
|
"add",
|
|||
|
|
"sub",
|
|||
|
|
"mul",
|
|||
|
|
"pow",
|
|||
|
|
"div",
|
|||
|
|
"addN",
|
|||
|
|
"subN",
|
|||
|
|
"mulN",
|
|||
|
|
"sqrN"
|
|||
|
|
];
|
|||
|
|
_Field = class {
|
|||
|
|
ORDER;
|
|||
|
|
BITS;
|
|||
|
|
BYTES;
|
|||
|
|
isLE;
|
|||
|
|
ZERO = _0n2;
|
|||
|
|
ONE = _1n2;
|
|||
|
|
_lengths;
|
|||
|
|
_sqrt;
|
|||
|
|
// cached sqrt
|
|||
|
|
_mod;
|
|||
|
|
constructor(ORDER, opts = {}) {
|
|||
|
|
if (ORDER <= _0n2)
|
|||
|
|
throw new Error("invalid field: expected ORDER > 0, got " + ORDER);
|
|||
|
|
let _nbitLength = void 0;
|
|||
|
|
this.isLE = false;
|
|||
|
|
if (opts != null && typeof opts === "object") {
|
|||
|
|
if (typeof opts.BITS === "number")
|
|||
|
|
_nbitLength = opts.BITS;
|
|||
|
|
if (typeof opts.sqrt === "function")
|
|||
|
|
this.sqrt = opts.sqrt;
|
|||
|
|
if (typeof opts.isLE === "boolean")
|
|||
|
|
this.isLE = opts.isLE;
|
|||
|
|
if (opts.allowedLengths)
|
|||
|
|
this._lengths = opts.allowedLengths?.slice();
|
|||
|
|
if (typeof opts.modFromBytes === "boolean")
|
|||
|
|
this._mod = opts.modFromBytes;
|
|||
|
|
}
|
|||
|
|
const { nBitLength, nByteLength } = nLength(ORDER, _nbitLength);
|
|||
|
|
if (nByteLength > 2048)
|
|||
|
|
throw new Error("invalid field: expected ORDER of <= 2048 bytes");
|
|||
|
|
this.ORDER = ORDER;
|
|||
|
|
this.BITS = nBitLength;
|
|||
|
|
this.BYTES = nByteLength;
|
|||
|
|
this._sqrt = void 0;
|
|||
|
|
Object.preventExtensions(this);
|
|||
|
|
}
|
|||
|
|
create(num2) {
|
|||
|
|
return mod(num2, this.ORDER);
|
|||
|
|
}
|
|||
|
|
isValid(num2) {
|
|||
|
|
if (typeof num2 !== "bigint")
|
|||
|
|
throw new Error("invalid field element: expected bigint, got " + typeof num2);
|
|||
|
|
return _0n2 <= num2 && num2 < this.ORDER;
|
|||
|
|
}
|
|||
|
|
is0(num2) {
|
|||
|
|
return num2 === _0n2;
|
|||
|
|
}
|
|||
|
|
// is valid and invertible
|
|||
|
|
isValidNot0(num2) {
|
|||
|
|
return !this.is0(num2) && this.isValid(num2);
|
|||
|
|
}
|
|||
|
|
isOdd(num2) {
|
|||
|
|
return (num2 & _1n2) === _1n2;
|
|||
|
|
}
|
|||
|
|
neg(num2) {
|
|||
|
|
return mod(-num2, this.ORDER);
|
|||
|
|
}
|
|||
|
|
eql(lhs, rhs) {
|
|||
|
|
return lhs === rhs;
|
|||
|
|
}
|
|||
|
|
sqr(num2) {
|
|||
|
|
return mod(num2 * num2, this.ORDER);
|
|||
|
|
}
|
|||
|
|
add(lhs, rhs) {
|
|||
|
|
return mod(lhs + rhs, this.ORDER);
|
|||
|
|
}
|
|||
|
|
sub(lhs, rhs) {
|
|||
|
|
return mod(lhs - rhs, this.ORDER);
|
|||
|
|
}
|
|||
|
|
mul(lhs, rhs) {
|
|||
|
|
return mod(lhs * rhs, this.ORDER);
|
|||
|
|
}
|
|||
|
|
pow(num2, power) {
|
|||
|
|
return FpPow(this, num2, power);
|
|||
|
|
}
|
|||
|
|
div(lhs, rhs) {
|
|||
|
|
return mod(lhs * invert(rhs, this.ORDER), this.ORDER);
|
|||
|
|
}
|
|||
|
|
// Same as above, but doesn't normalize
|
|||
|
|
sqrN(num2) {
|
|||
|
|
return num2 * num2;
|
|||
|
|
}
|
|||
|
|
addN(lhs, rhs) {
|
|||
|
|
return lhs + rhs;
|
|||
|
|
}
|
|||
|
|
subN(lhs, rhs) {
|
|||
|
|
return lhs - rhs;
|
|||
|
|
}
|
|||
|
|
mulN(lhs, rhs) {
|
|||
|
|
return lhs * rhs;
|
|||
|
|
}
|
|||
|
|
inv(num2) {
|
|||
|
|
return invert(num2, this.ORDER);
|
|||
|
|
}
|
|||
|
|
sqrt(num2) {
|
|||
|
|
if (!this._sqrt)
|
|||
|
|
this._sqrt = FpSqrt(this.ORDER);
|
|||
|
|
return this._sqrt(this, num2);
|
|||
|
|
}
|
|||
|
|
toBytes(num2) {
|
|||
|
|
return this.isLE ? numberToBytesLE(num2, this.BYTES) : numberToBytesBE(num2, this.BYTES);
|
|||
|
|
}
|
|||
|
|
fromBytes(bytes, skipValidation = false) {
|
|||
|
|
abytes(bytes);
|
|||
|
|
const { _lengths: allowedLengths, BYTES, isLE: isLE2, ORDER, _mod: modFromBytes } = this;
|
|||
|
|
if (allowedLengths) {
|
|||
|
|
if (!allowedLengths.includes(bytes.length) || bytes.length > BYTES) {
|
|||
|
|
throw new Error("Field.fromBytes: expected " + allowedLengths + " bytes, got " + bytes.length);
|
|||
|
|
}
|
|||
|
|
const padded = new Uint8Array(BYTES);
|
|||
|
|
padded.set(bytes, isLE2 ? 0 : padded.length - bytes.length);
|
|||
|
|
bytes = padded;
|
|||
|
|
}
|
|||
|
|
if (bytes.length !== BYTES)
|
|||
|
|
throw new Error("Field.fromBytes: expected " + BYTES + " bytes, got " + bytes.length);
|
|||
|
|
let scalar = isLE2 ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
|
|||
|
|
if (modFromBytes)
|
|||
|
|
scalar = mod(scalar, ORDER);
|
|||
|
|
if (!skipValidation) {
|
|||
|
|
if (!this.isValid(scalar))
|
|||
|
|
throw new Error("invalid field element: outside of range 0..ORDER");
|
|||
|
|
}
|
|||
|
|
return scalar;
|
|||
|
|
}
|
|||
|
|
// TODO: we don't need it here, move out to separate fn
|
|||
|
|
invertBatch(lst) {
|
|||
|
|
return FpInvertBatch(this, lst);
|
|||
|
|
}
|
|||
|
|
// We can't move this out because Fp6, Fp12 implement it
|
|||
|
|
// and it's unclear what to return in there.
|
|||
|
|
cmov(a, b, condition) {
|
|||
|
|
return condition ? b : a;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@noble/curves/abstract/curve.js
|
|||
|
|
function negateCt(condition, item) {
|
|||
|
|
const neg = item.negate();
|
|||
|
|
return condition ? neg : item;
|
|||
|
|
}
|
|||
|
|
function normalizeZ(c, points) {
|
|||
|
|
const invertedZs = FpInvertBatch(c.Fp, points.map((p) => p.Z));
|
|||
|
|
return points.map((p, i3) => c.fromAffine(p.toAffine(invertedZs[i3])));
|
|||
|
|
}
|
|||
|
|
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, scalarBits) {
|
|||
|
|
validateW(W, scalarBits);
|
|||
|
|
const windows = Math.ceil(scalarBits / W) + 1;
|
|||
|
|
const windowSize = 2 ** (W - 1);
|
|||
|
|
const maxNumber = 2 ** W;
|
|||
|
|
const mask = bitMask(W);
|
|||
|
|
const shiftBy = BigInt(W);
|
|||
|
|
return { windows, windowSize, mask, maxNumber, shiftBy };
|
|||
|
|
}
|
|||
|
|
function calcOffsets(n, window2, wOpts) {
|
|||
|
|
const { windowSize, mask, maxNumber, shiftBy } = wOpts;
|
|||
|
|
let wbits = Number(n & mask);
|
|||
|
|
let nextN = n >> shiftBy;
|
|||
|
|
if (wbits > windowSize) {
|
|||
|
|
wbits -= maxNumber;
|
|||
|
|
nextN += _1n3;
|
|||
|
|
}
|
|||
|
|
const offsetStart = window2 * windowSize;
|
|||
|
|
const offset = offsetStart + Math.abs(wbits) - 1;
|
|||
|
|
const isZero = wbits === 0;
|
|||
|
|
const isNeg = wbits < 0;
|
|||
|
|
const isNegF = window2 % 2 !== 0;
|
|||
|
|
const offsetF = offsetStart;
|
|||
|
|
return { nextN, offset, isZero, isNeg, isNegF, offsetF };
|
|||
|
|
}
|
|||
|
|
function getW(P) {
|
|||
|
|
return pointWindowSizes.get(P) || 1;
|
|||
|
|
}
|
|||
|
|
function assert0(n) {
|
|||
|
|
if (n !== _0n3)
|
|||
|
|
throw new Error("invalid wNAF");
|
|||
|
|
}
|
|||
|
|
function mulEndoUnsafe(Point, point, k1, k2) {
|
|||
|
|
let acc = point;
|
|||
|
|
let p1 = Point.ZERO;
|
|||
|
|
let p2 = Point.ZERO;
|
|||
|
|
while (k1 > _0n3 || k2 > _0n3) {
|
|||
|
|
if (k1 & _1n3)
|
|||
|
|
p1 = p1.add(acc);
|
|||
|
|
if (k2 & _1n3)
|
|||
|
|
p2 = p2.add(acc);
|
|||
|
|
acc = acc.double();
|
|||
|
|
k1 >>= _1n3;
|
|||
|
|
k2 >>= _1n3;
|
|||
|
|
}
|
|||
|
|
return { p1, p2 };
|
|||
|
|
}
|
|||
|
|
function createField(order, field, isLE2) {
|
|||
|
|
if (field) {
|
|||
|
|
if (field.ORDER !== order)
|
|||
|
|
throw new Error("Field.ORDER must match order: Fp == p, Fn == n");
|
|||
|
|
validateField(field);
|
|||
|
|
return field;
|
|||
|
|
} else {
|
|||
|
|
return Field(order, { isLE: isLE2 });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function createCurveFields(type, CURVE, curveOpts = {}, FpFnLE) {
|
|||
|
|
if (FpFnLE === void 0)
|
|||
|
|
FpFnLE = type === "edwards";
|
|||
|
|
if (!CURVE || typeof CURVE !== "object")
|
|||
|
|
throw new Error(`expected valid ${type} CURVE object`);
|
|||
|
|
for (const p of ["p", "n", "h"]) {
|
|||
|
|
const val = CURVE[p];
|
|||
|
|
if (!(typeof val === "bigint" && val > _0n3))
|
|||
|
|
throw new Error(`CURVE.${p} must be positive bigint`);
|
|||
|
|
}
|
|||
|
|
const Fp = createField(CURVE.p, curveOpts.Fp, FpFnLE);
|
|||
|
|
const Fn = createField(CURVE.n, curveOpts.Fn, FpFnLE);
|
|||
|
|
const _b = type === "weierstrass" ? "b" : "d";
|
|||
|
|
const params = ["Gx", "Gy", "a", _b];
|
|||
|
|
for (const p of params) {
|
|||
|
|
if (!Fp.isValid(CURVE[p]))
|
|||
|
|
throw new Error(`CURVE.${p} must be valid field element of CURVE.Fp`);
|
|||
|
|
}
|
|||
|
|
CURVE = Object.freeze(Object.assign({}, CURVE));
|
|||
|
|
return { CURVE, Fp, Fn };
|
|||
|
|
}
|
|||
|
|
function createKeygen(randomSecretKey, getPublicKey3) {
|
|||
|
|
return function keygen(seed) {
|
|||
|
|
const secretKey = randomSecretKey(seed);
|
|||
|
|
return { secretKey, publicKey: getPublicKey3(secretKey) };
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
var _0n3, _1n3, pointPrecomputes, pointWindowSizes, wNAF;
|
|||
|
|
var init_curve = __esm({
|
|||
|
|
"node_modules/@noble/curves/abstract/curve.js"() {
|
|||
|
|
init_utils2();
|
|||
|
|
init_modular();
|
|||
|
|
_0n3 = /* @__PURE__ */ BigInt(0);
|
|||
|
|
_1n3 = /* @__PURE__ */ BigInt(1);
|
|||
|
|
pointPrecomputes = /* @__PURE__ */ new WeakMap();
|
|||
|
|
pointWindowSizes = /* @__PURE__ */ new WeakMap();
|
|||
|
|
wNAF = class {
|
|||
|
|
BASE;
|
|||
|
|
ZERO;
|
|||
|
|
Fn;
|
|||
|
|
bits;
|
|||
|
|
// Parametrized with a given Point class (not individual point)
|
|||
|
|
constructor(Point, bits) {
|
|||
|
|
this.BASE = Point.BASE;
|
|||
|
|
this.ZERO = Point.ZERO;
|
|||
|
|
this.Fn = Point.Fn;
|
|||
|
|
this.bits = bits;
|
|||
|
|
}
|
|||
|
|
// non-const time multiplication ladder
|
|||
|
|
_unsafeLadder(elm, n, p = this.ZERO) {
|
|||
|
|
let d = elm;
|
|||
|
|
while (n > _0n3) {
|
|||
|
|
if (n & _1n3)
|
|||
|
|
p = p.add(d);
|
|||
|
|
d = d.double();
|
|||
|
|
n >>= _1n3;
|
|||
|
|
}
|
|||
|
|
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 point Point instance
|
|||
|
|
* @param W window size
|
|||
|
|
* @returns precomputed point tables flattened to a single array
|
|||
|
|
*/
|
|||
|
|
precomputeWindow(point, W) {
|
|||
|
|
const { windows, windowSize } = calcWOpts(W, this.bits);
|
|||
|
|
const points = [];
|
|||
|
|
let p = point;
|
|||
|
|
let base = p;
|
|||
|
|
for (let window2 = 0; window2 < windows; window2++) {
|
|||
|
|
base = p;
|
|||
|
|
points.push(base);
|
|||
|
|
for (let i3 = 1; i3 < windowSize; i3++) {
|
|||
|
|
base = base.add(p);
|
|||
|
|
points.push(base);
|
|||
|
|
}
|
|||
|
|
p = base.double();
|
|||
|
|
}
|
|||
|
|
return points;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Implements ec multiplication using precomputed tables and w-ary non-adjacent form.
|
|||
|
|
* More compact implementation:
|
|||
|
|
* https://github.com/paulmillr/noble-secp256k1/blob/47cb1669b6e506ad66b35fe7d76132ae97465da2/index.ts#L502-L541
|
|||
|
|
* @returns real and fake (for const-time) points
|
|||
|
|
*/
|
|||
|
|
wNAF(W, precomputes, n) {
|
|||
|
|
if (!this.Fn.isValid(n))
|
|||
|
|
throw new Error("invalid scalar");
|
|||
|
|
let p = this.ZERO;
|
|||
|
|
let f = this.BASE;
|
|||
|
|
const wo = calcWOpts(W, this.bits);
|
|||
|
|
for (let window2 = 0; window2 < wo.windows; window2++) {
|
|||
|
|
const { nextN, offset, isZero, isNeg, isNegF, offsetF } = calcOffsets(n, window2, wo);
|
|||
|
|
n = nextN;
|
|||
|
|
if (isZero) {
|
|||
|
|
f = f.add(negateCt(isNegF, precomputes[offsetF]));
|
|||
|
|
} else {
|
|||
|
|
p = p.add(negateCt(isNeg, precomputes[offset]));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
assert0(n);
|
|||
|
|
return { p, f };
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Implements ec unsafe (non const-time) multiplication using precomputed tables and w-ary non-adjacent form.
|
|||
|
|
* @param acc accumulator point to add result of multiplication
|
|||
|
|
* @returns point
|
|||
|
|
*/
|
|||
|
|
wNAFUnsafe(W, precomputes, n, acc = this.ZERO) {
|
|||
|
|
const wo = calcWOpts(W, this.bits);
|
|||
|
|
for (let window2 = 0; window2 < wo.windows; window2++) {
|
|||
|
|
if (n === _0n3)
|
|||
|
|
break;
|
|||
|
|
const { nextN, offset, isZero, isNeg } = calcOffsets(n, window2, wo);
|
|||
|
|
n = nextN;
|
|||
|
|
if (isZero) {
|
|||
|
|
continue;
|
|||
|
|
} else {
|
|||
|
|
const item = precomputes[offset];
|
|||
|
|
acc = acc.add(isNeg ? item.negate() : item);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
assert0(n);
|
|||
|
|
return acc;
|
|||
|
|
}
|
|||
|
|
getPrecomputes(W, point, transform) {
|
|||
|
|
let comp = pointPrecomputes.get(point);
|
|||
|
|
if (!comp) {
|
|||
|
|
comp = this.precomputeWindow(point, W);
|
|||
|
|
if (W !== 1) {
|
|||
|
|
if (typeof transform === "function")
|
|||
|
|
comp = transform(comp);
|
|||
|
|
pointPrecomputes.set(point, comp);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return comp;
|
|||
|
|
}
|
|||
|
|
cached(point, scalar, transform) {
|
|||
|
|
const W = getW(point);
|
|||
|
|
return this.wNAF(W, this.getPrecomputes(W, point, transform), scalar);
|
|||
|
|
}
|
|||
|
|
unsafe(point, scalar, transform, prev) {
|
|||
|
|
const W = getW(point);
|
|||
|
|
if (W === 1)
|
|||
|
|
return this._unsafeLadder(point, scalar, prev);
|
|||
|
|
return this.wNAFUnsafe(W, this.getPrecomputes(W, point, transform), scalar, 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.
|
|||
|
|
createCache(P, W) {
|
|||
|
|
validateW(W, this.bits);
|
|||
|
|
pointWindowSizes.set(P, W);
|
|||
|
|
pointPrecomputes.delete(P);
|
|||
|
|
}
|
|||
|
|
hasCache(elm) {
|
|||
|
|
return getW(elm) !== 1;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@noble/hashes/hmac.js
|
|||
|
|
var _HMAC, hmac;
|
|||
|
|
var init_hmac = __esm({
|
|||
|
|
"node_modules/@noble/hashes/hmac.js"() {
|
|||
|
|
init_utils();
|
|||
|
|
_HMAC = class {
|
|||
|
|
oHash;
|
|||
|
|
iHash;
|
|||
|
|
blockLen;
|
|||
|
|
outputLen;
|
|||
|
|
finished = false;
|
|||
|
|
destroyed = false;
|
|||
|
|
constructor(hash, key) {
|
|||
|
|
ahash(hash);
|
|||
|
|
abytes(key, void 0, "key");
|
|||
|
|
this.iHash = hash.create();
|
|||
|
|
if (typeof this.iHash.update !== "function")
|
|||
|
|
throw new Error("Expected instance of class which extends utils.Hash");
|
|||
|
|
this.blockLen = this.iHash.blockLen;
|
|||
|
|
this.outputLen = this.iHash.outputLen;
|
|||
|
|
const blockLen = this.blockLen;
|
|||
|
|
const pad2 = new Uint8Array(blockLen);
|
|||
|
|
pad2.set(key.length > blockLen ? hash.create().update(key).digest() : key);
|
|||
|
|
for (let i3 = 0; i3 < pad2.length; i3++)
|
|||
|
|
pad2[i3] ^= 54;
|
|||
|
|
this.iHash.update(pad2);
|
|||
|
|
this.oHash = hash.create();
|
|||
|
|
for (let i3 = 0; i3 < pad2.length; i3++)
|
|||
|
|
pad2[i3] ^= 54 ^ 92;
|
|||
|
|
this.oHash.update(pad2);
|
|||
|
|
clean(pad2);
|
|||
|
|
}
|
|||
|
|
update(buf) {
|
|||
|
|
aexists(this);
|
|||
|
|
this.iHash.update(buf);
|
|||
|
|
return this;
|
|||
|
|
}
|
|||
|
|
digestInto(out) {
|
|||
|
|
aexists(this);
|
|||
|
|
abytes(out, this.outputLen, "output");
|
|||
|
|
this.finished = true;
|
|||
|
|
this.iHash.digestInto(out);
|
|||
|
|
this.oHash.update(out);
|
|||
|
|
this.oHash.digestInto(out);
|
|||
|
|
this.destroy();
|
|||
|
|
}
|
|||
|
|
digest() {
|
|||
|
|
const out = new Uint8Array(this.oHash.outputLen);
|
|||
|
|
this.digestInto(out);
|
|||
|
|
return out;
|
|||
|
|
}
|
|||
|
|
_cloneInto(to) {
|
|||
|
|
to ||= Object.create(Object.getPrototypeOf(this), {});
|
|||
|
|
const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;
|
|||
|
|
to = to;
|
|||
|
|
to.finished = finished;
|
|||
|
|
to.destroyed = destroyed;
|
|||
|
|
to.blockLen = blockLen;
|
|||
|
|
to.outputLen = outputLen;
|
|||
|
|
to.oHash = oHash._cloneInto(to.oHash);
|
|||
|
|
to.iHash = iHash._cloneInto(to.iHash);
|
|||
|
|
return to;
|
|||
|
|
}
|
|||
|
|
clone() {
|
|||
|
|
return this._cloneInto();
|
|||
|
|
}
|
|||
|
|
destroy() {
|
|||
|
|
this.destroyed = true;
|
|||
|
|
this.oHash.destroy();
|
|||
|
|
this.iHash.destroy();
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
hmac = (hash, key, message) => new _HMAC(hash, key).update(message).digest();
|
|||
|
|
hmac.create = (hash, key) => new _HMAC(hash, key);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@noble/curves/abstract/weierstrass.js
|
|||
|
|
function _splitEndoScalar(k, basis, n) {
|
|||
|
|
const [[a1, b1], [a2, b2]] = basis;
|
|||
|
|
const c1 = divNearest(b2 * k, n);
|
|||
|
|
const c2 = divNearest(-b1 * k, n);
|
|||
|
|
let k1 = k - c1 * a1 - c2 * a2;
|
|||
|
|
let k2 = -c1 * b1 - c2 * b2;
|
|||
|
|
const k1neg = k1 < _0n4;
|
|||
|
|
const k2neg = k2 < _0n4;
|
|||
|
|
if (k1neg)
|
|||
|
|
k1 = -k1;
|
|||
|
|
if (k2neg)
|
|||
|
|
k2 = -k2;
|
|||
|
|
const MAX_NUM = bitMask(Math.ceil(bitLen(n) / 2)) + _1n4;
|
|||
|
|
if (k1 < _0n4 || k1 >= MAX_NUM || k2 < _0n4 || k2 >= MAX_NUM) {
|
|||
|
|
throw new Error("splitScalar (endomorphism): failed, k=" + k);
|
|||
|
|
}
|
|||
|
|
return { k1neg, k1, k2neg, k2 };
|
|||
|
|
}
|
|||
|
|
function validateSigFormat(format) {
|
|||
|
|
if (!["compact", "recovered", "der"].includes(format))
|
|||
|
|
throw new Error('Signature format must be "compact", "recovered", or "der"');
|
|||
|
|
return format;
|
|||
|
|
}
|
|||
|
|
function validateSigOpts(opts, def) {
|
|||
|
|
const optsn = {};
|
|||
|
|
for (let optName of Object.keys(def)) {
|
|||
|
|
optsn[optName] = opts[optName] === void 0 ? def[optName] : opts[optName];
|
|||
|
|
}
|
|||
|
|
abool(optsn.lowS, "lowS");
|
|||
|
|
abool(optsn.prehash, "prehash");
|
|||
|
|
if (optsn.format !== void 0)
|
|||
|
|
validateSigFormat(optsn.format);
|
|||
|
|
return optsn;
|
|||
|
|
}
|
|||
|
|
function weierstrass(params, extraOpts = {}) {
|
|||
|
|
const validated = createCurveFields("weierstrass", params, extraOpts);
|
|||
|
|
const { Fp, Fn } = validated;
|
|||
|
|
let CURVE = validated.CURVE;
|
|||
|
|
const { h: cofactor, n: CURVE_ORDER } = CURVE;
|
|||
|
|
validateObject(extraOpts, {}, {
|
|||
|
|
allowInfinityPoint: "boolean",
|
|||
|
|
clearCofactor: "function",
|
|||
|
|
isTorsionFree: "function",
|
|||
|
|
fromBytes: "function",
|
|||
|
|
toBytes: "function",
|
|||
|
|
endo: "object"
|
|||
|
|
});
|
|||
|
|
const { endo } = extraOpts;
|
|||
|
|
if (endo) {
|
|||
|
|
if (!Fp.is0(CURVE.a) || typeof endo.beta !== "bigint" || !Array.isArray(endo.basises)) {
|
|||
|
|
throw new Error('invalid endo: expected "beta": bigint and "basises": array');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
const lengths = getWLengths(Fp, Fn);
|
|||
|
|
function assertCompressionIsSupported() {
|
|||
|
|
if (!Fp.isOdd)
|
|||
|
|
throw new Error("compression is not supported: Field does not have .isOdd()");
|
|||
|
|
}
|
|||
|
|
function pointToBytes2(_c, point, isCompressed) {
|
|||
|
|
const { x, y } = point.toAffine();
|
|||
|
|
const bx = Fp.toBytes(x);
|
|||
|
|
abool(isCompressed, "isCompressed");
|
|||
|
|
if (isCompressed) {
|
|||
|
|
assertCompressionIsSupported();
|
|||
|
|
const hasEvenY = !Fp.isOdd(y);
|
|||
|
|
return concatBytes(pprefix(hasEvenY), bx);
|
|||
|
|
} else {
|
|||
|
|
return concatBytes(Uint8Array.of(4), bx, Fp.toBytes(y));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function pointFromBytes(bytes) {
|
|||
|
|
abytes(bytes, void 0, "Point");
|
|||
|
|
const { publicKey: comp, publicKeyUncompressed: uncomp } = lengths;
|
|||
|
|
const length = bytes.length;
|
|||
|
|
const head = bytes[0];
|
|||
|
|
const tail = bytes.subarray(1);
|
|||
|
|
if (length === comp && (head === 2 || head === 3)) {
|
|||
|
|
const x = Fp.fromBytes(tail);
|
|||
|
|
if (!Fp.isValid(x))
|
|||
|
|
throw new Error("bad point: is not on curve, wrong x");
|
|||
|
|
const y2 = weierstrassEquation(x);
|
|||
|
|
let y;
|
|||
|
|
try {
|
|||
|
|
y = Fp.sqrt(y2);
|
|||
|
|
} catch (sqrtError) {
|
|||
|
|
const err = sqrtError instanceof Error ? ": " + sqrtError.message : "";
|
|||
|
|
throw new Error("bad point: is not on curve, sqrt error" + err);
|
|||
|
|
}
|
|||
|
|
assertCompressionIsSupported();
|
|||
|
|
const evenY = Fp.isOdd(y);
|
|||
|
|
const evenH = (head & 1) === 1;
|
|||
|
|
if (evenH !== evenY)
|
|||
|
|
y = Fp.neg(y);
|
|||
|
|
return { x, y };
|
|||
|
|
} else if (length === uncomp && head === 4) {
|
|||
|
|
const L = Fp.BYTES;
|
|||
|
|
const x = Fp.fromBytes(tail.subarray(0, L));
|
|||
|
|
const y = Fp.fromBytes(tail.subarray(L, L * 2));
|
|||
|
|
if (!isValidXY(x, y))
|
|||
|
|
throw new Error("bad point: is not on curve");
|
|||
|
|
return { x, y };
|
|||
|
|
} else {
|
|||
|
|
throw new Error(`bad point: got length ${length}, expected compressed=${comp} or uncompressed=${uncomp}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
const encodePoint = extraOpts.toBytes || pointToBytes2;
|
|||
|
|
const decodePoint = extraOpts.fromBytes || pointFromBytes;
|
|||
|
|
function weierstrassEquation(x) {
|
|||
|
|
const x2 = Fp.sqr(x);
|
|||
|
|
const x3 = Fp.mul(x2, x);
|
|||
|
|
return Fp.add(Fp.add(x3, Fp.mul(x, CURVE.a)), CURVE.b);
|
|||
|
|
}
|
|||
|
|
function isValidXY(x, y) {
|
|||
|
|
const left = Fp.sqr(y);
|
|||
|
|
const right = weierstrassEquation(x);
|
|||
|
|
return Fp.eql(left, right);
|
|||
|
|
}
|
|||
|
|
if (!isValidXY(CURVE.Gx, CURVE.Gy))
|
|||
|
|
throw new Error("bad curve params: generator point");
|
|||
|
|
const _4a3 = Fp.mul(Fp.pow(CURVE.a, _3n2), _4n2);
|
|||
|
|
const _27b2 = Fp.mul(Fp.sqr(CURVE.b), BigInt(27));
|
|||
|
|
if (Fp.is0(Fp.add(_4a3, _27b2)))
|
|||
|
|
throw new Error("bad curve params: a or b");
|
|||
|
|
function acoord(title, n, banZero = false) {
|
|||
|
|
if (!Fp.isValid(n) || banZero && Fp.is0(n))
|
|||
|
|
throw new Error(`bad point coordinate ${title}`);
|
|||
|
|
return n;
|
|||
|
|
}
|
|||
|
|
function aprjpoint(other) {
|
|||
|
|
if (!(other instanceof Point))
|
|||
|
|
throw new Error("Weierstrass Point expected");
|
|||
|
|
}
|
|||
|
|
function splitEndoScalarN(k) {
|
|||
|
|
if (!endo || !endo.basises)
|
|||
|
|
throw new Error("no endo");
|
|||
|
|
return _splitEndoScalar(k, endo.basises, Fn.ORDER);
|
|||
|
|
}
|
|||
|
|
const toAffineMemo = memoized((p, iz) => {
|
|||
|
|
const { X, Y, Z } = p;
|
|||
|
|
if (Fp.eql(Z, Fp.ONE))
|
|||
|
|
return { x: X, y: Y };
|
|||
|
|
const is0 = p.is0();
|
|||
|
|
if (iz == null)
|
|||
|
|
iz = is0 ? Fp.ONE : Fp.inv(Z);
|
|||
|
|
const x = Fp.mul(X, iz);
|
|||
|
|
const y = Fp.mul(Y, iz);
|
|||
|
|
const zz = Fp.mul(Z, iz);
|
|||
|
|
if (is0)
|
|||
|
|
return { x: Fp.ZERO, y: Fp.ZERO };
|
|||
|
|
if (!Fp.eql(zz, Fp.ONE))
|
|||
|
|
throw new Error("invZ was invalid");
|
|||
|
|
return { x, y };
|
|||
|
|
});
|
|||
|
|
const assertValidMemo = memoized((p) => {
|
|||
|
|
if (p.is0()) {
|
|||
|
|
if (extraOpts.allowInfinityPoint && !Fp.is0(p.Y))
|
|||
|
|
return;
|
|||
|
|
throw new Error("bad point: ZERO");
|
|||
|
|
}
|
|||
|
|
const { x, y } = p.toAffine();
|
|||
|
|
if (!Fp.isValid(x) || !Fp.isValid(y))
|
|||
|
|
throw new Error("bad point: x or y not field elements");
|
|||
|
|
if (!isValidXY(x, y))
|
|||
|
|
throw new Error("bad point: equation left != right");
|
|||
|
|
if (!p.isTorsionFree())
|
|||
|
|
throw new Error("bad point: not in prime-order subgroup");
|
|||
|
|
return true;
|
|||
|
|
});
|
|||
|
|
function finishEndo(endoBeta, k1p, k2p, k1neg, k2neg) {
|
|||
|
|
k2p = new Point(Fp.mul(k2p.X, endoBeta), k2p.Y, k2p.Z);
|
|||
|
|
k1p = negateCt(k1neg, k1p);
|
|||
|
|
k2p = negateCt(k2neg, k2p);
|
|||
|
|
return k1p.add(k2p);
|
|||
|
|
}
|
|||
|
|
class Point {
|
|||
|
|
// base / generator point
|
|||
|
|
static BASE = new Point(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
|||
|
|
// zero / infinity / identity point
|
|||
|
|
static ZERO = new Point(Fp.ZERO, Fp.ONE, Fp.ZERO);
|
|||
|
|
// 0, 1, 0
|
|||
|
|
// math field
|
|||
|
|
static Fp = Fp;
|
|||
|
|
// scalar field
|
|||
|
|
static Fn = Fn;
|
|||
|
|
X;
|
|||
|
|
Y;
|
|||
|
|
Z;
|
|||
|
|
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|||
|
|
constructor(X, Y, Z) {
|
|||
|
|
this.X = acoord("x", X);
|
|||
|
|
this.Y = acoord("y", Y, true);
|
|||
|
|
this.Z = acoord("z", Z);
|
|||
|
|
Object.freeze(this);
|
|||
|
|
}
|
|||
|
|
static CURVE() {
|
|||
|
|
return CURVE;
|
|||
|
|
}
|
|||
|
|
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
|
|||
|
|
static fromAffine(p) {
|
|||
|
|
const { x, y } = p || {};
|
|||
|
|
if (!p || !Fp.isValid(x) || !Fp.isValid(y))
|
|||
|
|
throw new Error("invalid affine point");
|
|||
|
|
if (p instanceof Point)
|
|||
|
|
throw new Error("projective point not allowed");
|
|||
|
|
if (Fp.is0(x) && Fp.is0(y))
|
|||
|
|
return Point.ZERO;
|
|||
|
|
return new Point(x, y, Fp.ONE);
|
|||
|
|
}
|
|||
|
|
static fromBytes(bytes) {
|
|||
|
|
const P = Point.fromAffine(decodePoint(abytes(bytes, void 0, "point")));
|
|||
|
|
P.assertValidity();
|
|||
|
|
return P;
|
|||
|
|
}
|
|||
|
|
static fromHex(hex) {
|
|||
|
|
return Point.fromBytes(hexToBytes(hex));
|
|||
|
|
}
|
|||
|
|
get x() {
|
|||
|
|
return this.toAffine().x;
|
|||
|
|
}
|
|||
|
|
get y() {
|
|||
|
|
return this.toAffine().y;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
*
|
|||
|
|
* @param windowSize
|
|||
|
|
* @param isLazy true will defer table computation until the first multiplication
|
|||
|
|
* @returns
|
|||
|
|
*/
|
|||
|
|
precompute(windowSize = 8, isLazy = true) {
|
|||
|
|
wnaf.createCache(this, windowSize);
|
|||
|
|
if (!isLazy)
|
|||
|
|
this.multiply(_3n2);
|
|||
|
|
return this;
|
|||
|
|
}
|
|||
|
|
// TODO: return `this`
|
|||
|
|
/** A point on curve is valid if it conforms to equation. */
|
|||
|
|
assertValidity() {
|
|||
|
|
assertValidMemo(this);
|
|||
|
|
}
|
|||
|
|
hasEvenY() {
|
|||
|
|
const { y } = this.toAffine();
|
|||
|
|
if (!Fp.isOdd)
|
|||
|
|
throw new Error("Field doesn't support isOdd");
|
|||
|
|
return !Fp.isOdd(y);
|
|||
|
|
}
|
|||
|
|
/** Compare one point to another. */
|
|||
|
|
equals(other) {
|
|||
|
|
aprjpoint(other);
|
|||
|
|
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|||
|
|
const { X: X2, Y: Y2, Z: Z2 } = other;
|
|||
|
|
const U1 = Fp.eql(Fp.mul(X1, Z2), Fp.mul(X2, Z1));
|
|||
|
|
const U2 = Fp.eql(Fp.mul(Y1, Z2), Fp.mul(Y2, Z1));
|
|||
|
|
return U1 && U2;
|
|||
|
|
}
|
|||
|
|
/** Flips point to one corresponding to (x, -y) in Affine coordinates. */
|
|||
|
|
negate() {
|
|||
|
|
return new Point(this.X, Fp.neg(this.Y), this.Z);
|
|||
|
|
}
|
|||
|
|
// Renes-Costello-Batina exception-free doubling formula.
|
|||
|
|
// There is 30% faster Jacobian formula, but it is not complete.
|
|||
|
|
// https://eprint.iacr.org/2015/1060, algorithm 3
|
|||
|
|
// Cost: 8M + 3S + 3*a + 2*b3 + 15add.
|
|||
|
|
double() {
|
|||
|
|
const { a, b } = CURVE;
|
|||
|
|
const b3 = Fp.mul(b, _3n2);
|
|||
|
|
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|||
|
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO;
|
|||
|
|
let t0 = Fp.mul(X1, X1);
|
|||
|
|
let t1 = Fp.mul(Y1, Y1);
|
|||
|
|
let t2 = Fp.mul(Z1, Z1);
|
|||
|
|
let t3 = Fp.mul(X1, Y1);
|
|||
|
|
t3 = Fp.add(t3, t3);
|
|||
|
|
Z3 = Fp.mul(X1, Z1);
|
|||
|
|
Z3 = Fp.add(Z3, Z3);
|
|||
|
|
X3 = Fp.mul(a, Z3);
|
|||
|
|
Y3 = Fp.mul(b3, t2);
|
|||
|
|
Y3 = Fp.add(X3, Y3);
|
|||
|
|
X3 = Fp.sub(t1, Y3);
|
|||
|
|
Y3 = Fp.add(t1, Y3);
|
|||
|
|
Y3 = Fp.mul(X3, Y3);
|
|||
|
|
X3 = Fp.mul(t3, X3);
|
|||
|
|
Z3 = Fp.mul(b3, Z3);
|
|||
|
|
t2 = Fp.mul(a, t2);
|
|||
|
|
t3 = Fp.sub(t0, t2);
|
|||
|
|
t3 = Fp.mul(a, t3);
|
|||
|
|
t3 = Fp.add(t3, Z3);
|
|||
|
|
Z3 = Fp.add(t0, t0);
|
|||
|
|
t0 = Fp.add(Z3, t0);
|
|||
|
|
t0 = Fp.add(t0, t2);
|
|||
|
|
t0 = Fp.mul(t0, t3);
|
|||
|
|
Y3 = Fp.add(Y3, t0);
|
|||
|
|
t2 = Fp.mul(Y1, Z1);
|
|||
|
|
t2 = Fp.add(t2, t2);
|
|||
|
|
t0 = Fp.mul(t2, t3);
|
|||
|
|
X3 = Fp.sub(X3, t0);
|
|||
|
|
Z3 = Fp.mul(t2, t1);
|
|||
|
|
Z3 = Fp.add(Z3, Z3);
|
|||
|
|
Z3 = Fp.add(Z3, Z3);
|
|||
|
|
return new Point(X3, Y3, Z3);
|
|||
|
|
}
|
|||
|
|
// Renes-Costello-Batina exception-free addition formula.
|
|||
|
|
// There is 30% faster Jacobian formula, but it is not complete.
|
|||
|
|
// https://eprint.iacr.org/2015/1060, algorithm 1
|
|||
|
|
// Cost: 12M + 0S + 3*a + 3*b3 + 23add.
|
|||
|
|
add(other) {
|
|||
|
|
aprjpoint(other);
|
|||
|
|
const { X: X1, Y: Y1, Z: Z1 } = this;
|
|||
|
|
const { X: X2, Y: Y2, Z: Z2 } = other;
|
|||
|
|
let X3 = Fp.ZERO, Y3 = Fp.ZERO, Z3 = Fp.ZERO;
|
|||
|
|
const a = CURVE.a;
|
|||
|
|
const b3 = Fp.mul(CURVE.b, _3n2);
|
|||
|
|
let t0 = Fp.mul(X1, X2);
|
|||
|
|
let t1 = Fp.mul(Y1, Y2);
|
|||
|
|
let t2 = Fp.mul(Z1, Z2);
|
|||
|
|
let t3 = Fp.add(X1, Y1);
|
|||
|
|
let t4 = Fp.add(X2, Y2);
|
|||
|
|
t3 = Fp.mul(t3, t4);
|
|||
|
|
t4 = Fp.add(t0, t1);
|
|||
|
|
t3 = Fp.sub(t3, t4);
|
|||
|
|
t4 = Fp.add(X1, Z1);
|
|||
|
|
let t5 = Fp.add(X2, Z2);
|
|||
|
|
t4 = Fp.mul(t4, t5);
|
|||
|
|
t5 = Fp.add(t0, t2);
|
|||
|
|
t4 = Fp.sub(t4, t5);
|
|||
|
|
t5 = Fp.add(Y1, Z1);
|
|||
|
|
X3 = Fp.add(Y2, Z2);
|
|||
|
|
t5 = Fp.mul(t5, X3);
|
|||
|
|
X3 = Fp.add(t1, t2);
|
|||
|
|
t5 = Fp.sub(t5, X3);
|
|||
|
|
Z3 = Fp.mul(a, t4);
|
|||
|
|
X3 = Fp.mul(b3, t2);
|
|||
|
|
Z3 = Fp.add(X3, Z3);
|
|||
|
|
X3 = Fp.sub(t1, Z3);
|
|||
|
|
Z3 = Fp.add(t1, Z3);
|
|||
|
|
Y3 = Fp.mul(X3, Z3);
|
|||
|
|
t1 = Fp.add(t0, t0);
|
|||
|
|
t1 = Fp.add(t1, t0);
|
|||
|
|
t2 = Fp.mul(a, t2);
|
|||
|
|
t4 = Fp.mul(b3, t4);
|
|||
|
|
t1 = Fp.add(t1, t2);
|
|||
|
|
t2 = Fp.sub(t0, t2);
|
|||
|
|
t2 = Fp.mul(a, t2);
|
|||
|
|
t4 = Fp.add(t4, t2);
|
|||
|
|
t0 = Fp.mul(t1, t4);
|
|||
|
|
Y3 = Fp.add(Y3, t0);
|
|||
|
|
t0 = Fp.mul(t5, t4);
|
|||
|
|
X3 = Fp.mul(t3, X3);
|
|||
|
|
X3 = Fp.sub(X3, t0);
|
|||
|
|
t0 = Fp.mul(t3, t1);
|
|||
|
|
Z3 = Fp.mul(t5, Z3);
|
|||
|
|
Z3 = Fp.add(Z3, t0);
|
|||
|
|
return new Point(X3, Y3, Z3);
|
|||
|
|
}
|
|||
|
|
subtract(other) {
|
|||
|
|
return this.add(other.negate());
|
|||
|
|
}
|
|||
|
|
is0() {
|
|||
|
|
return this.equals(Point.ZERO);
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Constant time multiplication.
|
|||
|
|
* Uses wNAF method. Windowed method may be 10% faster,
|
|||
|
|
* but takes 2x longer to generate and consumes 2x memory.
|
|||
|
|
* Uses precomputes when available.
|
|||
|
|
* Uses endomorphism for Koblitz curves.
|
|||
|
|
* @param scalar by which the point would be multiplied
|
|||
|
|
* @returns New point
|
|||
|
|
*/
|
|||
|
|
multiply(scalar) {
|
|||
|
|
const { endo: endo2 } = extraOpts;
|
|||
|
|
if (!Fn.isValidNot0(scalar))
|
|||
|
|
throw new Error("invalid scalar: out of range");
|
|||
|
|
let point, fake;
|
|||
|
|
const mul = (n) => wnaf.cached(this, n, (p) => normalizeZ(Point, p));
|
|||
|
|
if (endo2) {
|
|||
|
|
const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(scalar);
|
|||
|
|
const { p: k1p, f: k1f } = mul(k1);
|
|||
|
|
const { p: k2p, f: k2f } = mul(k2);
|
|||
|
|
fake = k1f.add(k2f);
|
|||
|
|
point = finishEndo(endo2.beta, k1p, k2p, k1neg, k2neg);
|
|||
|
|
} else {
|
|||
|
|
const { p, f } = mul(scalar);
|
|||
|
|
point = p;
|
|||
|
|
fake = f;
|
|||
|
|
}
|
|||
|
|
return normalizeZ(Point, [point, fake])[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 secret key e.g. sig verification, which works over *public* keys.
|
|||
|
|
*/
|
|||
|
|
multiplyUnsafe(sc) {
|
|||
|
|
const { endo: endo2 } = extraOpts;
|
|||
|
|
const p = this;
|
|||
|
|
if (!Fn.isValid(sc))
|
|||
|
|
throw new Error("invalid scalar: out of range");
|
|||
|
|
if (sc === _0n4 || p.is0())
|
|||
|
|
return Point.ZERO;
|
|||
|
|
if (sc === _1n4)
|
|||
|
|
return p;
|
|||
|
|
if (wnaf.hasCache(this))
|
|||
|
|
return this.multiply(sc);
|
|||
|
|
if (endo2) {
|
|||
|
|
const { k1neg, k1, k2neg, k2 } = splitEndoScalarN(sc);
|
|||
|
|
const { p1, p2 } = mulEndoUnsafe(Point, p, k1, k2);
|
|||
|
|
return finishEndo(endo2.beta, p1, p2, k1neg, k2neg);
|
|||
|
|
} else {
|
|||
|
|
return wnaf.unsafe(p, sc);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Converts Projective point to affine (x, y) coordinates.
|
|||
|
|
* @param invertedZ Z^-1 (inverted zero) - optional, precomputation is useful for invertBatch
|
|||
|
|
*/
|
|||
|
|
toAffine(invertedZ) {
|
|||
|
|
return toAffineMemo(this, invertedZ);
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Checks whether Point is free of torsion elements (is in prime subgroup).
|
|||
|
|
* Always torsion-free for cofactor=1 curves.
|
|||
|
|
*/
|
|||
|
|
isTorsionFree() {
|
|||
|
|
const { isTorsionFree } = extraOpts;
|
|||
|
|
if (cofactor === _1n4)
|
|||
|
|
return true;
|
|||
|
|
if (isTorsionFree)
|
|||
|
|
return isTorsionFree(Point, this);
|
|||
|
|
return wnaf.unsafe(this, CURVE_ORDER).is0();
|
|||
|
|
}
|
|||
|
|
clearCofactor() {
|
|||
|
|
const { clearCofactor } = extraOpts;
|
|||
|
|
if (cofactor === _1n4)
|
|||
|
|
return this;
|
|||
|
|
if (clearCofactor)
|
|||
|
|
return clearCofactor(Point, this);
|
|||
|
|
return this.multiplyUnsafe(cofactor);
|
|||
|
|
}
|
|||
|
|
isSmallOrder() {
|
|||
|
|
return this.multiplyUnsafe(cofactor).is0();
|
|||
|
|
}
|
|||
|
|
toBytes(isCompressed = true) {
|
|||
|
|
abool(isCompressed, "isCompressed");
|
|||
|
|
this.assertValidity();
|
|||
|
|
return encodePoint(Point, this, isCompressed);
|
|||
|
|
}
|
|||
|
|
toHex(isCompressed = true) {
|
|||
|
|
return bytesToHex(this.toBytes(isCompressed));
|
|||
|
|
}
|
|||
|
|
toString() {
|
|||
|
|
return `<Point ${this.is0() ? "ZERO" : this.toHex()}>`;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
const bits = Fn.BITS;
|
|||
|
|
const wnaf = new wNAF(Point, extraOpts.endo ? Math.ceil(bits / 2) : bits);
|
|||
|
|
Point.BASE.precompute(8);
|
|||
|
|
return Point;
|
|||
|
|
}
|
|||
|
|
function pprefix(hasEvenY) {
|
|||
|
|
return Uint8Array.of(hasEvenY ? 2 : 3);
|
|||
|
|
}
|
|||
|
|
function getWLengths(Fp, Fn) {
|
|||
|
|
return {
|
|||
|
|
secretKey: Fn.BYTES,
|
|||
|
|
publicKey: 1 + Fp.BYTES,
|
|||
|
|
publicKeyUncompressed: 1 + 2 * Fp.BYTES,
|
|||
|
|
publicKeyHasPrefix: true,
|
|||
|
|
signature: 2 * Fn.BYTES
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
function ecdh(Point, ecdhOpts = {}) {
|
|||
|
|
const { Fn } = Point;
|
|||
|
|
const randomBytes_ = ecdhOpts.randomBytes || randomBytes;
|
|||
|
|
const lengths = Object.assign(getWLengths(Point.Fp, Fn), { seed: getMinHashLength(Fn.ORDER) });
|
|||
|
|
function isValidSecretKey(secretKey) {
|
|||
|
|
try {
|
|||
|
|
const num2 = Fn.fromBytes(secretKey);
|
|||
|
|
return Fn.isValidNot0(num2);
|
|||
|
|
} catch (error2) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function isValidPublicKey(publicKey, isCompressed) {
|
|||
|
|
const { publicKey: comp, publicKeyUncompressed } = lengths;
|
|||
|
|
try {
|
|||
|
|
const l = publicKey.length;
|
|||
|
|
if (isCompressed === true && l !== comp)
|
|||
|
|
return false;
|
|||
|
|
if (isCompressed === false && l !== publicKeyUncompressed)
|
|||
|
|
return false;
|
|||
|
|
return !!Point.fromBytes(publicKey);
|
|||
|
|
} catch (error2) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function randomSecretKey(seed = randomBytes_(lengths.seed)) {
|
|||
|
|
return mapHashToField(abytes(seed, lengths.seed, "seed"), Fn.ORDER);
|
|||
|
|
}
|
|||
|
|
function getPublicKey3(secretKey, isCompressed = true) {
|
|||
|
|
return Point.BASE.multiply(Fn.fromBytes(secretKey)).toBytes(isCompressed);
|
|||
|
|
}
|
|||
|
|
function isProbPub(item) {
|
|||
|
|
const { secretKey, publicKey, publicKeyUncompressed } = lengths;
|
|||
|
|
if (!isBytes(item))
|
|||
|
|
return void 0;
|
|||
|
|
if ("_lengths" in Fn && Fn._lengths || secretKey === publicKey)
|
|||
|
|
return void 0;
|
|||
|
|
const l = abytes(item, void 0, "key").length;
|
|||
|
|
return l === publicKey || l === publicKeyUncompressed;
|
|||
|
|
}
|
|||
|
|
function getSharedSecret(secretKeyA, publicKeyB, isCompressed = true) {
|
|||
|
|
if (isProbPub(secretKeyA) === true)
|
|||
|
|
throw new Error("first arg must be private key");
|
|||
|
|
if (isProbPub(publicKeyB) === false)
|
|||
|
|
throw new Error("second arg must be public key");
|
|||
|
|
const s = Fn.fromBytes(secretKeyA);
|
|||
|
|
const b = Point.fromBytes(publicKeyB);
|
|||
|
|
return b.multiply(s).toBytes(isCompressed);
|
|||
|
|
}
|
|||
|
|
const utils = {
|
|||
|
|
isValidSecretKey,
|
|||
|
|
isValidPublicKey,
|
|||
|
|
randomSecretKey
|
|||
|
|
};
|
|||
|
|
const keygen = createKeygen(randomSecretKey, getPublicKey3);
|
|||
|
|
return Object.freeze({ getPublicKey: getPublicKey3, getSharedSecret, keygen, Point, utils, lengths });
|
|||
|
|
}
|
|||
|
|
function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|||
|
|
ahash(hash);
|
|||
|
|
validateObject(ecdsaOpts, {}, {
|
|||
|
|
hmac: "function",
|
|||
|
|
lowS: "boolean",
|
|||
|
|
randomBytes: "function",
|
|||
|
|
bits2int: "function",
|
|||
|
|
bits2int_modN: "function"
|
|||
|
|
});
|
|||
|
|
ecdsaOpts = Object.assign({}, ecdsaOpts);
|
|||
|
|
const randomBytes3 = ecdsaOpts.randomBytes || randomBytes;
|
|||
|
|
const hmac2 = ecdsaOpts.hmac || ((key, msg) => hmac(hash, key, msg));
|
|||
|
|
const { Fp, Fn } = Point;
|
|||
|
|
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
|
|||
|
|
const { keygen, getPublicKey: getPublicKey3, getSharedSecret, utils, lengths } = ecdh(Point, ecdsaOpts);
|
|||
|
|
const defaultSigOpts = {
|
|||
|
|
prehash: true,
|
|||
|
|
lowS: typeof ecdsaOpts.lowS === "boolean" ? ecdsaOpts.lowS : true,
|
|||
|
|
format: "compact",
|
|||
|
|
extraEntropy: false
|
|||
|
|
};
|
|||
|
|
const hasLargeCofactor = CURVE_ORDER * _2n2 < Fp.ORDER;
|
|||
|
|
function isBiggerThanHalfOrder(number) {
|
|||
|
|
const HALF = CURVE_ORDER >> _1n4;
|
|||
|
|
return number > HALF;
|
|||
|
|
}
|
|||
|
|
function validateRS(title, num2) {
|
|||
|
|
if (!Fn.isValidNot0(num2))
|
|||
|
|
throw new Error(`invalid signature ${title}: out of range 1..Point.Fn.ORDER`);
|
|||
|
|
return num2;
|
|||
|
|
}
|
|||
|
|
function assertSmallCofactor() {
|
|||
|
|
if (hasLargeCofactor)
|
|||
|
|
throw new Error('"recovered" sig type is not supported for cofactor >2 curves');
|
|||
|
|
}
|
|||
|
|
function validateSigLength(bytes, format) {
|
|||
|
|
validateSigFormat(format);
|
|||
|
|
const size = lengths.signature;
|
|||
|
|
const sizer = format === "compact" ? size : format === "recovered" ? size + 1 : void 0;
|
|||
|
|
return abytes(bytes, sizer);
|
|||
|
|
}
|
|||
|
|
class Signature {
|
|||
|
|
r;
|
|||
|
|
s;
|
|||
|
|
recovery;
|
|||
|
|
constructor(r, s, recovery) {
|
|||
|
|
this.r = validateRS("r", r);
|
|||
|
|
this.s = validateRS("s", s);
|
|||
|
|
if (recovery != null) {
|
|||
|
|
assertSmallCofactor();
|
|||
|
|
if (![0, 1, 2, 3].includes(recovery))
|
|||
|
|
throw new Error("invalid recovery id");
|
|||
|
|
this.recovery = recovery;
|
|||
|
|
}
|
|||
|
|
Object.freeze(this);
|
|||
|
|
}
|
|||
|
|
static fromBytes(bytes, format = defaultSigOpts.format) {
|
|||
|
|
validateSigLength(bytes, format);
|
|||
|
|
let recid;
|
|||
|
|
if (format === "der") {
|
|||
|
|
const { r: r2, s: s2 } = DER.toSig(abytes(bytes));
|
|||
|
|
return new Signature(r2, s2);
|
|||
|
|
}
|
|||
|
|
if (format === "recovered") {
|
|||
|
|
recid = bytes[0];
|
|||
|
|
format = "compact";
|
|||
|
|
bytes = bytes.subarray(1);
|
|||
|
|
}
|
|||
|
|
const L = lengths.signature / 2;
|
|||
|
|
const r = bytes.subarray(0, L);
|
|||
|
|
const s = bytes.subarray(L, L * 2);
|
|||
|
|
return new Signature(Fn.fromBytes(r), Fn.fromBytes(s), recid);
|
|||
|
|
}
|
|||
|
|
static fromHex(hex, format) {
|
|||
|
|
return this.fromBytes(hexToBytes(hex), format);
|
|||
|
|
}
|
|||
|
|
assertRecovery() {
|
|||
|
|
const { recovery } = this;
|
|||
|
|
if (recovery == null)
|
|||
|
|
throw new Error("invalid recovery id: must be present");
|
|||
|
|
return recovery;
|
|||
|
|
}
|
|||
|
|
addRecoveryBit(recovery) {
|
|||
|
|
return new Signature(this.r, this.s, recovery);
|
|||
|
|
}
|
|||
|
|
recoverPublicKey(messageHash) {
|
|||
|
|
const { r, s } = this;
|
|||
|
|
const recovery = this.assertRecovery();
|
|||
|
|
const radj = recovery === 2 || recovery === 3 ? r + CURVE_ORDER : r;
|
|||
|
|
if (!Fp.isValid(radj))
|
|||
|
|
throw new Error("invalid recovery id: sig.r+curve.n != R.x");
|
|||
|
|
const x = Fp.toBytes(radj);
|
|||
|
|
const R = Point.fromBytes(concatBytes(pprefix((recovery & 1) === 0), x));
|
|||
|
|
const ir = Fn.inv(radj);
|
|||
|
|
const h = bits2int_modN(abytes(messageHash, void 0, "msgHash"));
|
|||
|
|
const u1 = Fn.create(-h * ir);
|
|||
|
|
const u2 = Fn.create(s * ir);
|
|||
|
|
const Q = Point.BASE.multiplyUnsafe(u1).add(R.multiplyUnsafe(u2));
|
|||
|
|
if (Q.is0())
|
|||
|
|
throw new Error("invalid recovery: point at infinify");
|
|||
|
|
Q.assertValidity();
|
|||
|
|
return Q;
|
|||
|
|
}
|
|||
|
|
// Signatures should be low-s, to prevent malleability.
|
|||
|
|
hasHighS() {
|
|||
|
|
return isBiggerThanHalfOrder(this.s);
|
|||
|
|
}
|
|||
|
|
toBytes(format = defaultSigOpts.format) {
|
|||
|
|
validateSigFormat(format);
|
|||
|
|
if (format === "der")
|
|||
|
|
return hexToBytes(DER.hexFromSig(this));
|
|||
|
|
const { r, s } = this;
|
|||
|
|
const rb = Fn.toBytes(r);
|
|||
|
|
const sb = Fn.toBytes(s);
|
|||
|
|
if (format === "recovered") {
|
|||
|
|
assertSmallCofactor();
|
|||
|
|
return concatBytes(Uint8Array.of(this.assertRecovery()), rb, sb);
|
|||
|
|
}
|
|||
|
|
return concatBytes(rb, sb);
|
|||
|
|
}
|
|||
|
|
toHex(format) {
|
|||
|
|
return bytesToHex(this.toBytes(format));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
const bits2int = ecdsaOpts.bits2int || function bits2int_def(bytes) {
|
|||
|
|
if (bytes.length > 8192)
|
|||
|
|
throw new Error("input is too large");
|
|||
|
|
const num2 = bytesToNumberBE(bytes);
|
|||
|
|
const delta = bytes.length * 8 - fnBits;
|
|||
|
|
return delta > 0 ? num2 >> BigInt(delta) : num2;
|
|||
|
|
};
|
|||
|
|
const bits2int_modN = ecdsaOpts.bits2int_modN || function bits2int_modN_def(bytes) {
|
|||
|
|
return Fn.create(bits2int(bytes));
|
|||
|
|
};
|
|||
|
|
const ORDER_MASK = bitMask(fnBits);
|
|||
|
|
function int2octets(num2) {
|
|||
|
|
aInRange("num < 2^" + fnBits, num2, _0n4, ORDER_MASK);
|
|||
|
|
return Fn.toBytes(num2);
|
|||
|
|
}
|
|||
|
|
function validateMsgAndHash(message, prehash) {
|
|||
|
|
abytes(message, void 0, "message");
|
|||
|
|
return prehash ? abytes(hash(message), void 0, "prehashed message") : message;
|
|||
|
|
}
|
|||
|
|
function prepSig(message, secretKey, opts) {
|
|||
|
|
const { lowS, prehash, extraEntropy } = validateSigOpts(opts, defaultSigOpts);
|
|||
|
|
message = validateMsgAndHash(message, prehash);
|
|||
|
|
const h1int = bits2int_modN(message);
|
|||
|
|
const d = Fn.fromBytes(secretKey);
|
|||
|
|
if (!Fn.isValidNot0(d))
|
|||
|
|
throw new Error("invalid private key");
|
|||
|
|
const seedArgs = [int2octets(d), int2octets(h1int)];
|
|||
|
|
if (extraEntropy != null && extraEntropy !== false) {
|
|||
|
|
const e = extraEntropy === true ? randomBytes3(lengths.secretKey) : extraEntropy;
|
|||
|
|
seedArgs.push(abytes(e, void 0, "extraEntropy"));
|
|||
|
|
}
|
|||
|
|
const seed = concatBytes(...seedArgs);
|
|||
|
|
const m = h1int;
|
|||
|
|
function k2sig(kBytes) {
|
|||
|
|
const k = bits2int(kBytes);
|
|||
|
|
if (!Fn.isValidNot0(k))
|
|||
|
|
return;
|
|||
|
|
const ik = Fn.inv(k);
|
|||
|
|
const q = Point.BASE.multiply(k).toAffine();
|
|||
|
|
const r = Fn.create(q.x);
|
|||
|
|
if (r === _0n4)
|
|||
|
|
return;
|
|||
|
|
const s = Fn.create(ik * Fn.create(m + r * d));
|
|||
|
|
if (s === _0n4)
|
|||
|
|
return;
|
|||
|
|
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n4);
|
|||
|
|
let normS = s;
|
|||
|
|
if (lowS && isBiggerThanHalfOrder(s)) {
|
|||
|
|
normS = Fn.neg(s);
|
|||
|
|
recovery ^= 1;
|
|||
|
|
}
|
|||
|
|
return new Signature(r, normS, hasLargeCofactor ? void 0 : recovery);
|
|||
|
|
}
|
|||
|
|
return { seed, k2sig };
|
|||
|
|
}
|
|||
|
|
function sign(message, secretKey, opts = {}) {
|
|||
|
|
const { seed, k2sig } = prepSig(message, secretKey, opts);
|
|||
|
|
const drbg = createHmacDrbg(hash.outputLen, Fn.BYTES, hmac2);
|
|||
|
|
const sig = drbg(seed, k2sig);
|
|||
|
|
return sig.toBytes(opts.format);
|
|||
|
|
}
|
|||
|
|
function verify(signature, message, publicKey, opts = {}) {
|
|||
|
|
const { lowS, prehash, format } = validateSigOpts(opts, defaultSigOpts);
|
|||
|
|
publicKey = abytes(publicKey, void 0, "publicKey");
|
|||
|
|
message = validateMsgAndHash(message, prehash);
|
|||
|
|
if (!isBytes(signature)) {
|
|||
|
|
const end = signature instanceof Signature ? ", use sig.toBytes()" : "";
|
|||
|
|
throw new Error("verify expects Uint8Array signature" + end);
|
|||
|
|
}
|
|||
|
|
validateSigLength(signature, format);
|
|||
|
|
try {
|
|||
|
|
const sig = Signature.fromBytes(signature, format);
|
|||
|
|
const P = Point.fromBytes(publicKey);
|
|||
|
|
if (lowS && sig.hasHighS())
|
|||
|
|
return false;
|
|||
|
|
const { r, s } = sig;
|
|||
|
|
const h = bits2int_modN(message);
|
|||
|
|
const is = Fn.inv(s);
|
|||
|
|
const u1 = Fn.create(h * is);
|
|||
|
|
const u2 = Fn.create(r * is);
|
|||
|
|
const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2));
|
|||
|
|
if (R.is0())
|
|||
|
|
return false;
|
|||
|
|
const v = Fn.create(R.x);
|
|||
|
|
return v === r;
|
|||
|
|
} catch (e) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function recoverPublicKey(signature, message, opts = {}) {
|
|||
|
|
const { prehash } = validateSigOpts(opts, defaultSigOpts);
|
|||
|
|
message = validateMsgAndHash(message, prehash);
|
|||
|
|
return Signature.fromBytes(signature, "recovered").recoverPublicKey(message).toBytes();
|
|||
|
|
}
|
|||
|
|
return Object.freeze({
|
|||
|
|
keygen,
|
|||
|
|
getPublicKey: getPublicKey3,
|
|||
|
|
getSharedSecret,
|
|||
|
|
utils,
|
|||
|
|
lengths,
|
|||
|
|
Point,
|
|||
|
|
sign,
|
|||
|
|
verify,
|
|||
|
|
recoverPublicKey,
|
|||
|
|
Signature,
|
|||
|
|
hash
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
var divNearest, DERErr, DER, _0n4, _1n4, _2n2, _3n2, _4n2;
|
|||
|
|
var init_weierstrass = __esm({
|
|||
|
|
"node_modules/@noble/curves/abstract/weierstrass.js"() {
|
|||
|
|
init_hmac();
|
|||
|
|
init_utils();
|
|||
|
|
init_utils2();
|
|||
|
|
init_curve();
|
|||
|
|
init_modular();
|
|||
|
|
divNearest = (num2, den) => (num2 + (num2 >= 0 ? den : -den) / _2n2) / den;
|
|||
|
|
DERErr = class extends Error {
|
|||
|
|
constructor(m = "") {
|
|||
|
|
super(m);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
DER = {
|
|||
|
|
// asn.1 DER encoding utils
|
|||
|
|
Err: DERErr,
|
|||
|
|
// Basic building block is TLV (Tag-Length-Value)
|
|||
|
|
_tlv: {
|
|||
|
|
encode: (tag, data) => {
|
|||
|
|
const { Err: E } = DER;
|
|||
|
|
if (tag < 0 || tag > 256)
|
|||
|
|
throw new E("tlv.encode: wrong tag");
|
|||
|
|
if (data.length & 1)
|
|||
|
|
throw new E("tlv.encode: unpadded data");
|
|||
|
|
const dataLen = data.length / 2;
|
|||
|
|
const len = numberToHexUnpadded(dataLen);
|
|||
|
|
if (len.length / 2 & 128)
|
|||
|
|
throw new E("tlv.encode: long form length too big");
|
|||
|
|
const lenLen = dataLen > 127 ? numberToHexUnpadded(len.length / 2 | 128) : "";
|
|||
|
|
const t = numberToHexUnpadded(tag);
|
|||
|
|
return t + lenLen + len + data;
|
|||
|
|
},
|
|||
|
|
// v - value, l - left bytes (unparsed)
|
|||
|
|
decode(tag, data) {
|
|||
|
|
const { Err: E } = DER;
|
|||
|
|
let pos = 0;
|
|||
|
|
if (tag < 0 || tag > 256)
|
|||
|
|
throw new E("tlv.encode: wrong tag");
|
|||
|
|
if (data.length < 2 || data[pos++] !== tag)
|
|||
|
|
throw new E("tlv.decode: wrong tlv");
|
|||
|
|
const first = data[pos++];
|
|||
|
|
const isLong = !!(first & 128);
|
|||
|
|
let length = 0;
|
|||
|
|
if (!isLong)
|
|||
|
|
length = first;
|
|||
|
|
else {
|
|||
|
|
const lenLen = first & 127;
|
|||
|
|
if (!lenLen)
|
|||
|
|
throw new E("tlv.decode(long): indefinite length not supported");
|
|||
|
|
if (lenLen > 4)
|
|||
|
|
throw new E("tlv.decode(long): byte length is too big");
|
|||
|
|
const lengthBytes = data.subarray(pos, pos + lenLen);
|
|||
|
|
if (lengthBytes.length !== lenLen)
|
|||
|
|
throw new E("tlv.decode: length bytes not complete");
|
|||
|
|
if (lengthBytes[0] === 0)
|
|||
|
|
throw new E("tlv.decode(long): zero leftmost byte");
|
|||
|
|
for (const b of lengthBytes)
|
|||
|
|
length = length << 8 | b;
|
|||
|
|
pos += lenLen;
|
|||
|
|
if (length < 128)
|
|||
|
|
throw new E("tlv.decode(long): not minimal encoding");
|
|||
|
|
}
|
|||
|
|
const v = data.subarray(pos, pos + length);
|
|||
|
|
if (v.length !== length)
|
|||
|
|
throw new E("tlv.decode: wrong value length");
|
|||
|
|
return { v, l: data.subarray(pos + length) };
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
// https://crypto.stackexchange.com/a/57734 Leftmost bit of first byte is 'negative' flag,
|
|||
|
|
// since we always use positive integers here. It must always be empty:
|
|||
|
|
// - add zero byte if exists
|
|||
|
|
// - if next byte doesn't have a flag, leading zero is not allowed (minimal encoding)
|
|||
|
|
_int: {
|
|||
|
|
encode(num2) {
|
|||
|
|
const { Err: E } = DER;
|
|||
|
|
if (num2 < _0n4)
|
|||
|
|
throw new E("integer: negative integers are not allowed");
|
|||
|
|
let hex = numberToHexUnpadded(num2);
|
|||
|
|
if (Number.parseInt(hex[0], 16) & 8)
|
|||
|
|
hex = "00" + hex;
|
|||
|
|
if (hex.length & 1)
|
|||
|
|
throw new E("unexpected DER parsing assertion: unpadded hex");
|
|||
|
|
return hex;
|
|||
|
|
},
|
|||
|
|
decode(data) {
|
|||
|
|
const { Err: E } = DER;
|
|||
|
|
if (data[0] & 128)
|
|||
|
|
throw new E("invalid signature integer: negative");
|
|||
|
|
if (data[0] === 0 && !(data[1] & 128))
|
|||
|
|
throw new E("invalid signature integer: unnecessary leading zero");
|
|||
|
|
return bytesToNumberBE(data);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
toSig(bytes) {
|
|||
|
|
const { Err: E, _int: int, _tlv: tlv } = DER;
|
|||
|
|
const data = abytes(bytes, void 0, "signature");
|
|||
|
|
const { v: seqBytes, l: seqLeftBytes } = tlv.decode(48, data);
|
|||
|
|
if (seqLeftBytes.length)
|
|||
|
|
throw new E("invalid signature: left bytes after parsing");
|
|||
|
|
const { v: rBytes, l: rLeftBytes } = tlv.decode(2, seqBytes);
|
|||
|
|
const { v: sBytes, l: sLeftBytes } = tlv.decode(2, rLeftBytes);
|
|||
|
|
if (sLeftBytes.length)
|
|||
|
|
throw new E("invalid signature: left bytes after parsing");
|
|||
|
|
return { r: int.decode(rBytes), s: int.decode(sBytes) };
|
|||
|
|
},
|
|||
|
|
hexFromSig(sig) {
|
|||
|
|
const { _tlv: tlv, _int: int } = DER;
|
|||
|
|
const rs = tlv.encode(2, int.encode(sig.r));
|
|||
|
|
const ss = tlv.encode(2, int.encode(sig.s));
|
|||
|
|
const seq = rs + ss;
|
|||
|
|
return tlv.encode(48, seq);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
_0n4 = BigInt(0);
|
|||
|
|
_1n4 = BigInt(1);
|
|||
|
|
_2n2 = BigInt(2);
|
|||
|
|
_3n2 = BigInt(3);
|
|||
|
|
_4n2 = BigInt(4);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@noble/curves/secp256k1.js
|
|||
|
|
function sqrtMod(y) {
|
|||
|
|
const P = secp256k1_CURVE.p;
|
|||
|
|
const _3n3 = BigInt(3), _6n = BigInt(6), _11n = BigInt(11), _22n = BigInt(22);
|
|||
|
|
const _23n = BigInt(23), _44n = BigInt(44), _88n = BigInt(88);
|
|||
|
|
const b2 = y * y * y % P;
|
|||
|
|
const b3 = b2 * b2 * y % P;
|
|||
|
|
const b6 = pow2(b3, _3n3, P) * b3 % P;
|
|||
|
|
const b9 = pow2(b6, _3n3, P) * b3 % P;
|
|||
|
|
const b11 = pow2(b9, _2n3, P) * b2 % P;
|
|||
|
|
const b22 = pow2(b11, _11n, P) * b11 % P;
|
|||
|
|
const b44 = pow2(b22, _22n, P) * b22 % P;
|
|||
|
|
const b88 = pow2(b44, _44n, P) * b44 % P;
|
|||
|
|
const b176 = pow2(b88, _88n, P) * b88 % P;
|
|||
|
|
const b220 = pow2(b176, _44n, P) * b44 % P;
|
|||
|
|
const b223 = pow2(b220, _3n3, P) * b3 % P;
|
|||
|
|
const t1 = pow2(b223, _23n, P) * b22 % P;
|
|||
|
|
const t2 = pow2(t1, _6n, P) * b2 % P;
|
|||
|
|
const root = pow2(t2, _2n3, P);
|
|||
|
|
if (!Fpk1.eql(Fpk1.sqr(root), y))
|
|||
|
|
throw new Error("Cannot find square root");
|
|||
|
|
return root;
|
|||
|
|
}
|
|||
|
|
function taggedHash(tag, ...messages) {
|
|||
|
|
let tagP = TAGGED_HASH_PREFIXES[tag];
|
|||
|
|
if (tagP === void 0) {
|
|||
|
|
const tagH = sha256(asciiToBytes(tag));
|
|||
|
|
tagP = concatBytes(tagH, tagH);
|
|||
|
|
TAGGED_HASH_PREFIXES[tag] = tagP;
|
|||
|
|
}
|
|||
|
|
return sha256(concatBytes(tagP, ...messages));
|
|||
|
|
}
|
|||
|
|
function schnorrGetExtPubKey(priv) {
|
|||
|
|
const { Fn, BASE } = Pointk1;
|
|||
|
|
const d_ = Fn.fromBytes(priv);
|
|||
|
|
const p = BASE.multiply(d_);
|
|||
|
|
const scalar = hasEven(p.y) ? d_ : Fn.neg(d_);
|
|||
|
|
return { scalar, bytes: pointToBytes(p) };
|
|||
|
|
}
|
|||
|
|
function lift_x(x) {
|
|||
|
|
const Fp = Fpk1;
|
|||
|
|
if (!Fp.isValidNot0(x))
|
|||
|
|
throw new Error("invalid x: Fail if x \u2265 p");
|
|||
|
|
const xx = Fp.create(x * x);
|
|||
|
|
const c = Fp.create(xx * x + BigInt(7));
|
|||
|
|
let y = Fp.sqrt(c);
|
|||
|
|
if (!hasEven(y))
|
|||
|
|
y = Fp.neg(y);
|
|||
|
|
const p = Pointk1.fromAffine({ x, y });
|
|||
|
|
p.assertValidity();
|
|||
|
|
return p;
|
|||
|
|
}
|
|||
|
|
function challenge(...args) {
|
|||
|
|
return Pointk1.Fn.create(num(taggedHash("BIP0340/challenge", ...args)));
|
|||
|
|
}
|
|||
|
|
function schnorrGetPublicKey(secretKey) {
|
|||
|
|
return schnorrGetExtPubKey(secretKey).bytes;
|
|||
|
|
}
|
|||
|
|
function schnorrSign(message, secretKey, auxRand = randomBytes(32)) {
|
|||
|
|
const { Fn } = Pointk1;
|
|||
|
|
const m = abytes(message, void 0, "message");
|
|||
|
|
const { bytes: px, scalar: d } = schnorrGetExtPubKey(secretKey);
|
|||
|
|
const a = abytes(auxRand, 32, "auxRand");
|
|||
|
|
const t = Fn.toBytes(d ^ num(taggedHash("BIP0340/aux", a)));
|
|||
|
|
const rand = taggedHash("BIP0340/nonce", t, px, m);
|
|||
|
|
const { bytes: rx, scalar: k } = schnorrGetExtPubKey(rand);
|
|||
|
|
const e = challenge(rx, px, m);
|
|||
|
|
const sig = new Uint8Array(64);
|
|||
|
|
sig.set(rx, 0);
|
|||
|
|
sig.set(Fn.toBytes(Fn.create(k + e * d)), 32);
|
|||
|
|
if (!schnorrVerify(sig, m, px))
|
|||
|
|
throw new Error("sign: Invalid signature produced");
|
|||
|
|
return sig;
|
|||
|
|
}
|
|||
|
|
function schnorrVerify(signature, message, publicKey) {
|
|||
|
|
const { Fp, Fn, BASE } = Pointk1;
|
|||
|
|
const sig = abytes(signature, 64, "signature");
|
|||
|
|
const m = abytes(message, void 0, "message");
|
|||
|
|
const pub = abytes(publicKey, 32, "publicKey");
|
|||
|
|
try {
|
|||
|
|
const P = lift_x(num(pub));
|
|||
|
|
const r = num(sig.subarray(0, 32));
|
|||
|
|
if (!Fp.isValidNot0(r))
|
|||
|
|
return false;
|
|||
|
|
const s = num(sig.subarray(32, 64));
|
|||
|
|
if (!Fn.isValidNot0(s))
|
|||
|
|
return false;
|
|||
|
|
const e = challenge(Fn.toBytes(r), pointToBytes(P), m);
|
|||
|
|
const R = BASE.multiplyUnsafe(s).add(P.multiplyUnsafe(Fn.neg(e)));
|
|||
|
|
const { x, y } = R.toAffine();
|
|||
|
|
if (R.is0() || !hasEven(y) || x !== r)
|
|||
|
|
return false;
|
|||
|
|
return true;
|
|||
|
|
} catch (error2) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
var secp256k1_CURVE, secp256k1_ENDO, _0n5, _2n3, Fpk1, Pointk1, secp256k1, TAGGED_HASH_PREFIXES, pointToBytes, hasEven, num, schnorr;
|
|||
|
|
var init_secp256k1 = __esm({
|
|||
|
|
"node_modules/@noble/curves/secp256k1.js"() {
|
|||
|
|
init_sha2();
|
|||
|
|
init_utils();
|
|||
|
|
init_curve();
|
|||
|
|
init_modular();
|
|||
|
|
init_weierstrass();
|
|||
|
|
init_utils2();
|
|||
|
|
secp256k1_CURVE = {
|
|||
|
|
p: BigInt("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"),
|
|||
|
|
n: BigInt("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"),
|
|||
|
|
h: BigInt(1),
|
|||
|
|
a: BigInt(0),
|
|||
|
|
b: BigInt(7),
|
|||
|
|
Gx: BigInt("0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"),
|
|||
|
|
Gy: BigInt("0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8")
|
|||
|
|
};
|
|||
|
|
secp256k1_ENDO = {
|
|||
|
|
beta: BigInt("0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee"),
|
|||
|
|
basises: [
|
|||
|
|
[BigInt("0x3086d221a7d46bcde86c90e49284eb15"), -BigInt("0xe4437ed6010e88286f547fa90abfe4c3")],
|
|||
|
|
[BigInt("0x114ca50f7a8e2f3f657c1108d9d44cfd8"), BigInt("0x3086d221a7d46bcde86c90e49284eb15")]
|
|||
|
|
]
|
|||
|
|
};
|
|||
|
|
_0n5 = /* @__PURE__ */ BigInt(0);
|
|||
|
|
_2n3 = /* @__PURE__ */ BigInt(2);
|
|||
|
|
Fpk1 = Field(secp256k1_CURVE.p, { sqrt: sqrtMod });
|
|||
|
|
Pointk1 = /* @__PURE__ */ weierstrass(secp256k1_CURVE, {
|
|||
|
|
Fp: Fpk1,
|
|||
|
|
endo: secp256k1_ENDO
|
|||
|
|
});
|
|||
|
|
secp256k1 = /* @__PURE__ */ ecdsa(Pointk1, sha256);
|
|||
|
|
TAGGED_HASH_PREFIXES = {};
|
|||
|
|
pointToBytes = (point) => point.toBytes(true).slice(1);
|
|||
|
|
hasEven = (y) => y % _2n3 === _0n5;
|
|||
|
|
num = bytesToNumberBE;
|
|||
|
|
schnorr = /* @__PURE__ */ (() => {
|
|||
|
|
const size = 32;
|
|||
|
|
const seedLength = 48;
|
|||
|
|
const randomSecretKey = (seed = randomBytes(seedLength)) => {
|
|||
|
|
return mapHashToField(seed, secp256k1_CURVE.n);
|
|||
|
|
};
|
|||
|
|
return {
|
|||
|
|
keygen: createKeygen(randomSecretKey, schnorrGetPublicKey),
|
|||
|
|
getPublicKey: schnorrGetPublicKey,
|
|||
|
|
sign: schnorrSign,
|
|||
|
|
verify: schnorrVerify,
|
|||
|
|
Point: Pointk1,
|
|||
|
|
utils: {
|
|||
|
|
randomSecretKey,
|
|||
|
|
taggedHash,
|
|||
|
|
lift_x,
|
|||
|
|
pointToBytes
|
|||
|
|
},
|
|||
|
|
lengths: {
|
|||
|
|
secretKey: size,
|
|||
|
|
publicKey: size,
|
|||
|
|
publicKeyHasPrefix: false,
|
|||
|
|
signature: size * 2,
|
|||
|
|
seed: seedLength
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
})();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/nostr-tools/lib/esm/pool.js
|
|||
|
|
function normalizeURL(url) {
|
|||
|
|
try {
|
|||
|
|
if (url.indexOf("://") === -1)
|
|||
|
|
url = "wss://" + url;
|
|||
|
|
let p = new URL(url);
|
|||
|
|
if (p.protocol === "http:")
|
|||
|
|
p.protocol = "ws:";
|
|||
|
|
else if (p.protocol === "https:")
|
|||
|
|
p.protocol = "wss:";
|
|||
|
|
p.pathname = p.pathname.replace(/\/+/g, "/");
|
|||
|
|
if (p.pathname.endsWith("/"))
|
|||
|
|
p.pathname = p.pathname.slice(0, -1);
|
|||
|
|
if (p.port === "80" && p.protocol === "ws:" || p.port === "443" && p.protocol === "wss:")
|
|||
|
|
p.port = "";
|
|||
|
|
p.searchParams.sort();
|
|||
|
|
p.hash = "";
|
|||
|
|
return p.toString();
|
|||
|
|
} catch (e) {
|
|||
|
|
throw new Error(`Invalid URL: ${url}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function isHex32(input) {
|
|||
|
|
for (let i22 = 0; i22 < 64; i22++) {
|
|||
|
|
let cc = input.charCodeAt(i22);
|
|||
|
|
if (isNaN(cc) || cc < 48 || cc > 102 || cc > 57 && cc < 97) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
function validateEvent(event) {
|
|||
|
|
if (!isRecord(event))
|
|||
|
|
return false;
|
|||
|
|
if (typeof event.kind !== "number")
|
|||
|
|
return false;
|
|||
|
|
if (typeof event.content !== "string")
|
|||
|
|
return false;
|
|||
|
|
if (typeof event.created_at !== "number")
|
|||
|
|
return false;
|
|||
|
|
if (typeof event.pubkey !== "string")
|
|||
|
|
return false;
|
|||
|
|
if (!isHex32(event.pubkey))
|
|||
|
|
return false;
|
|||
|
|
if (!Array.isArray(event.tags))
|
|||
|
|
return false;
|
|||
|
|
for (let i22 = 0; i22 < event.tags.length; i22++) {
|
|||
|
|
let tag = event.tags[i22];
|
|||
|
|
if (!Array.isArray(tag))
|
|||
|
|
return false;
|
|||
|
|
for (let j = 0; j < tag.length; j++) {
|
|||
|
|
if (typeof tag[j] !== "string")
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
function serializeEvent(evt) {
|
|||
|
|
if (!validateEvent(evt))
|
|||
|
|
throw new Error("can't serialize event with wrong or missing properties");
|
|||
|
|
return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
|
|||
|
|
}
|
|||
|
|
function getEventHash(event) {
|
|||
|
|
let eventHash = sha256(utf8Encoder.encode(serializeEvent(event)));
|
|||
|
|
return bytesToHex(eventHash);
|
|||
|
|
}
|
|||
|
|
function matchFilter(filter, event) {
|
|||
|
|
if (filter.ids && filter.ids.indexOf(event.id) === -1) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
for (let f in filter) {
|
|||
|
|
if (f[0] === "#") {
|
|||
|
|
let tagName = f.slice(1);
|
|||
|
|
let values = filter[`#${tagName}`];
|
|||
|
|
if (values && !event.tags.find(([t, v]) => t === f.slice(1) && values.indexOf(v) !== -1))
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (filter.since && event.created_at < filter.since)
|
|||
|
|
return false;
|
|||
|
|
if (filter.until && event.created_at > filter.until)
|
|||
|
|
return false;
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
function matchFilters(filters, event) {
|
|||
|
|
for (let i22 = 0; i22 < filters.length; i22++) {
|
|||
|
|
if (matchFilter(filters[i22], event)) {
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
function getHex64(json, field) {
|
|||
|
|
let len = field.length + 3;
|
|||
|
|
let idx = json.indexOf(`"${field}":`) + len;
|
|||
|
|
let s = json.slice(idx).indexOf(`"`) + idx + 1;
|
|||
|
|
return json.slice(s, s + 64);
|
|||
|
|
}
|
|||
|
|
function getSubscriptionId(json) {
|
|||
|
|
let idx = json.slice(0, 22).indexOf(`"EVENT"`);
|
|||
|
|
if (idx === -1)
|
|||
|
|
return null;
|
|||
|
|
let pstart = json.slice(idx + 7 + 1).indexOf(`"`);
|
|||
|
|
if (pstart === -1)
|
|||
|
|
return null;
|
|||
|
|
let start = idx + 7 + 1 + pstart;
|
|||
|
|
let pend = json.slice(start + 1, 80).indexOf(`"`);
|
|||
|
|
if (pend === -1)
|
|||
|
|
return null;
|
|||
|
|
let end = start + 1 + pend;
|
|||
|
|
return json.slice(start + 1, end);
|
|||
|
|
}
|
|||
|
|
function makeAuthEvent(relayURL, challenge2) {
|
|||
|
|
return {
|
|||
|
|
kind: ClientAuth,
|
|||
|
|
created_at: Math.floor(Date.now() / 1e3),
|
|||
|
|
tags: [
|
|||
|
|
["relay", relayURL],
|
|||
|
|
["challenge", challenge2]
|
|||
|
|
],
|
|||
|
|
content: ""
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
function getCountManyFilter(target, directive) {
|
|||
|
|
switch (directive) {
|
|||
|
|
case "reactions":
|
|||
|
|
return { "#e": [target], kinds: [7] };
|
|||
|
|
case "reposts":
|
|||
|
|
return { "#e": [target], kinds: [6] };
|
|||
|
|
case "quotes":
|
|||
|
|
return { "#q": [target], kinds: [1, 1111] };
|
|||
|
|
case "replies":
|
|||
|
|
return { "#e": [target], kinds: [1] };
|
|||
|
|
case "comments":
|
|||
|
|
return { "#E": [target], kinds: [1111] };
|
|||
|
|
case "followers":
|
|||
|
|
return { "#p": [target], kinds: [3] };
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function newHll() {
|
|||
|
|
return new Uint8Array(M);
|
|||
|
|
}
|
|||
|
|
function hllDecode(hex) {
|
|||
|
|
if (hex.length !== HLL_HEX_LENGTH || !/^[0-9a-f]+$/.test(hex))
|
|||
|
|
return void 0;
|
|||
|
|
const registers = new Uint8Array(M);
|
|||
|
|
for (let i22 = 0; i22 < M; i22++) {
|
|||
|
|
registers[i22] = parseInt(hex.slice(i22 * 2, i22 * 2 + 2), 16);
|
|||
|
|
}
|
|||
|
|
return registers;
|
|||
|
|
}
|
|||
|
|
function hllEncode(registers) {
|
|||
|
|
if (registers.length !== M)
|
|||
|
|
throw new Error(`invalid number of registers ${registers.length}`);
|
|||
|
|
let hex = "";
|
|||
|
|
for (let i22 = 0; i22 < M; i22++) {
|
|||
|
|
hex += registers[i22].toString(16).padStart(2, "0");
|
|||
|
|
}
|
|||
|
|
return hex;
|
|||
|
|
}
|
|||
|
|
function mergeHll(target, source) {
|
|||
|
|
if (target.length === 0)
|
|||
|
|
target = newHll();
|
|||
|
|
if (target.length !== M)
|
|||
|
|
throw new Error(`invalid number of registers ${target.length}`);
|
|||
|
|
if (source.length !== M)
|
|||
|
|
throw new Error(`invalid number of registers ${source.length}`);
|
|||
|
|
for (let i22 = 0; i22 < M; i22++) {
|
|||
|
|
if (source[i22] > target[i22])
|
|||
|
|
target[i22] = source[i22];
|
|||
|
|
}
|
|||
|
|
return target;
|
|||
|
|
}
|
|||
|
|
function useWebSocketImplementation(websocketImplementation) {
|
|||
|
|
_WebSocket = websocketImplementation;
|
|||
|
|
}
|
|||
|
|
var utf8Decoder, utf8Encoder, verifiedSymbol, isRecord, JS, i, generateSecretKey, getPublicKey, finalizeEvent, verifyEvent, ClientAuth, SendingOnClosedConnection, AbstractRelay, Subscription, M, HLL_HEX_LENGTH, utf8Encoder2, AbstractSimplePool, _WebSocket, SimplePool;
|
|||
|
|
var init_pool = __esm({
|
|||
|
|
"node_modules/nostr-tools/lib/esm/pool.js"() {
|
|||
|
|
init_secp256k1();
|
|||
|
|
init_utils();
|
|||
|
|
init_sha2();
|
|||
|
|
utf8Decoder = new TextDecoder("utf-8");
|
|||
|
|
utf8Encoder = new TextEncoder();
|
|||
|
|
verifiedSymbol = /* @__PURE__ */ Symbol("verified");
|
|||
|
|
isRecord = (obj) => obj instanceof Object;
|
|||
|
|
JS = class {
|
|||
|
|
generateSecretKey() {
|
|||
|
|
return schnorr.utils.randomSecretKey();
|
|||
|
|
}
|
|||
|
|
getPublicKey(secretKey) {
|
|||
|
|
return bytesToHex(schnorr.getPublicKey(secretKey));
|
|||
|
|
}
|
|||
|
|
finalizeEvent(t, secretKey) {
|
|||
|
|
const event = t;
|
|||
|
|
event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey));
|
|||
|
|
event.id = getEventHash(event);
|
|||
|
|
event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash(event)), secretKey));
|
|||
|
|
event[verifiedSymbol] = true;
|
|||
|
|
return event;
|
|||
|
|
}
|
|||
|
|
verifyEvent(event) {
|
|||
|
|
if (typeof event[verifiedSymbol] === "boolean")
|
|||
|
|
return event[verifiedSymbol];
|
|||
|
|
try {
|
|||
|
|
const hash = getEventHash(event);
|
|||
|
|
if (hash !== event.id) {
|
|||
|
|
event[verifiedSymbol] = false;
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey));
|
|||
|
|
event[verifiedSymbol] = valid;
|
|||
|
|
return valid;
|
|||
|
|
} catch (err) {
|
|||
|
|
event[verifiedSymbol] = false;
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
i = new JS();
|
|||
|
|
generateSecretKey = i.generateSecretKey;
|
|||
|
|
getPublicKey = i.getPublicKey;
|
|||
|
|
finalizeEvent = i.finalizeEvent;
|
|||
|
|
verifyEvent = i.verifyEvent;
|
|||
|
|
ClientAuth = 22242;
|
|||
|
|
SendingOnClosedConnection = class extends Error {
|
|||
|
|
constructor(message, relay) {
|
|||
|
|
super(`Tried to send message '${message} on a closed connection to ${relay}.`);
|
|||
|
|
this.name = "SendingOnClosedConnection";
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
AbstractRelay = class {
|
|||
|
|
url;
|
|||
|
|
_connected = false;
|
|||
|
|
onclose = null;
|
|||
|
|
onnotice = (msg) => console.debug(`NOTICE from ${this.url}: ${msg}`);
|
|||
|
|
onauth;
|
|||
|
|
baseEoseTimeout = 4400;
|
|||
|
|
publishTimeout = 4400;
|
|||
|
|
pingFrequency = 29e3;
|
|||
|
|
pingTimeout = 2e4;
|
|||
|
|
resubscribeBackoff = [1e4, 1e4, 1e4, 2e4, 2e4, 3e4, 6e4];
|
|||
|
|
openSubs = /* @__PURE__ */ new Map();
|
|||
|
|
enablePing;
|
|||
|
|
enableReconnect;
|
|||
|
|
idleTimeout = 0;
|
|||
|
|
idleSince = Date.now();
|
|||
|
|
ongoingOperations = 0;
|
|||
|
|
reconnectTimeoutHandle;
|
|||
|
|
pingIntervalHandle;
|
|||
|
|
reconnectAttempts = 0;
|
|||
|
|
skipReconnection = false;
|
|||
|
|
idleTimeoutHandle;
|
|||
|
|
connectionPromise;
|
|||
|
|
openCountRequests = /* @__PURE__ */ new Map();
|
|||
|
|
openEventPublishes = /* @__PURE__ */ new Map();
|
|||
|
|
ws;
|
|||
|
|
challenge;
|
|||
|
|
authPromise;
|
|||
|
|
serial = 0;
|
|||
|
|
verifyEvent;
|
|||
|
|
_WebSocket;
|
|||
|
|
constructor(url, opts) {
|
|||
|
|
this.url = normalizeURL(url);
|
|||
|
|
this.verifyEvent = opts.verifyEvent;
|
|||
|
|
this._WebSocket = opts.websocketImplementation || WebSocket;
|
|||
|
|
this.enablePing = opts.enablePing;
|
|||
|
|
this.enableReconnect = opts.enableReconnect || false;
|
|||
|
|
if (opts.idleTimeout)
|
|||
|
|
this.idleTimeout = opts.idleTimeout;
|
|||
|
|
}
|
|||
|
|
static async connect(url, opts) {
|
|||
|
|
const relay = new AbstractRelay(url, opts);
|
|||
|
|
await relay.connect(opts);
|
|||
|
|
return relay;
|
|||
|
|
}
|
|||
|
|
closeAllSubscriptions(reason) {
|
|||
|
|
for (let [_, sub] of this.openSubs) {
|
|||
|
|
sub.close(reason);
|
|||
|
|
}
|
|||
|
|
this.openSubs.clear();
|
|||
|
|
for (let [_, ep] of this.openEventPublishes) {
|
|||
|
|
ep.reject(new Error(reason));
|
|||
|
|
}
|
|||
|
|
this.openEventPublishes.clear();
|
|||
|
|
for (let [_, cr] of this.openCountRequests) {
|
|||
|
|
cr.reject(new Error(reason));
|
|||
|
|
}
|
|||
|
|
this.openCountRequests.clear();
|
|||
|
|
}
|
|||
|
|
get connected() {
|
|||
|
|
return this._connected;
|
|||
|
|
}
|
|||
|
|
clearIdleTimeout() {
|
|||
|
|
if (this.idleTimeoutHandle) {
|
|||
|
|
clearTimeout(this.idleTimeoutHandle);
|
|||
|
|
this.idleTimeoutHandle = void 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
scheduleIdleClose() {
|
|||
|
|
this.clearIdleTimeout();
|
|||
|
|
if (this.idleTimeout > 0) {
|
|||
|
|
this.idleTimeoutHandle = setTimeout(() => {
|
|||
|
|
if (this.ongoingOperations === 0 && this.idleSince) {
|
|||
|
|
this.close();
|
|||
|
|
}
|
|||
|
|
}, this.idleTimeout);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
async reconnect() {
|
|||
|
|
const backoff = this.resubscribeBackoff[Math.min(this.reconnectAttempts, this.resubscribeBackoff.length - 1)];
|
|||
|
|
this.reconnectAttempts++;
|
|||
|
|
this.reconnectTimeoutHandle = setTimeout(async () => {
|
|||
|
|
try {
|
|||
|
|
await this.connect();
|
|||
|
|
} catch (err) {
|
|||
|
|
}
|
|||
|
|
}, backoff);
|
|||
|
|
}
|
|||
|
|
handleHardClose(reason) {
|
|||
|
|
if (this.ws) {
|
|||
|
|
this.ws.onopen = null;
|
|||
|
|
this.ws.onerror = null;
|
|||
|
|
this.ws.onclose = null;
|
|||
|
|
}
|
|||
|
|
if (this.pingIntervalHandle) {
|
|||
|
|
clearInterval(this.pingIntervalHandle);
|
|||
|
|
this.pingIntervalHandle = void 0;
|
|||
|
|
}
|
|||
|
|
this._connected = false;
|
|||
|
|
this.connectionPromise = void 0;
|
|||
|
|
this.idleSince = void 0;
|
|||
|
|
this.clearIdleTimeout();
|
|||
|
|
if (this.enableReconnect && !this.skipReconnection) {
|
|||
|
|
this.reconnect();
|
|||
|
|
} else {
|
|||
|
|
this.onclose?.();
|
|||
|
|
this.closeAllSubscriptions(reason);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
async connect(opts) {
|
|||
|
|
let connectionTimeoutHandle;
|
|||
|
|
if (this.connectionPromise)
|
|||
|
|
return this.connectionPromise;
|
|||
|
|
this.challenge = void 0;
|
|||
|
|
this.authPromise = void 0;
|
|||
|
|
this.skipReconnection = false;
|
|||
|
|
this.connectionPromise = new Promise((resolve, reject) => {
|
|||
|
|
if (opts?.timeout) {
|
|||
|
|
connectionTimeoutHandle = setTimeout(() => {
|
|||
|
|
reject("connection timed out");
|
|||
|
|
this.connectionPromise = void 0;
|
|||
|
|
if (this.reconnectAttempts === 0) {
|
|||
|
|
this.skipReconnection = true;
|
|||
|
|
}
|
|||
|
|
this.handleHardClose("relay connection timed out");
|
|||
|
|
}, opts.timeout);
|
|||
|
|
}
|
|||
|
|
if (opts?.abort) {
|
|||
|
|
opts.abort.onabort = reject;
|
|||
|
|
}
|
|||
|
|
try {
|
|||
|
|
this.ws = new this._WebSocket(this.url);
|
|||
|
|
} catch (err) {
|
|||
|
|
clearTimeout(connectionTimeoutHandle);
|
|||
|
|
reject(err);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
this.ws.onopen = () => {
|
|||
|
|
if (this.reconnectTimeoutHandle) {
|
|||
|
|
clearTimeout(this.reconnectTimeoutHandle);
|
|||
|
|
this.reconnectTimeoutHandle = void 0;
|
|||
|
|
}
|
|||
|
|
clearTimeout(connectionTimeoutHandle);
|
|||
|
|
this._connected = true;
|
|||
|
|
const isReconnection = this.reconnectAttempts > 0;
|
|||
|
|
this.reconnectAttempts = 0;
|
|||
|
|
for (const sub of this.openSubs.values()) {
|
|||
|
|
sub.eosed = false;
|
|||
|
|
if (isReconnection) {
|
|||
|
|
for (let f = 0; f < sub.filters.length; f++) {
|
|||
|
|
if (sub.lastEmitted) {
|
|||
|
|
sub.filters[f].since = sub.lastEmitted + 1;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
sub.fire();
|
|||
|
|
}
|
|||
|
|
if (this.enablePing) {
|
|||
|
|
this.pingIntervalHandle = setInterval(() => this.pingpong(), this.pingFrequency);
|
|||
|
|
}
|
|||
|
|
resolve();
|
|||
|
|
};
|
|||
|
|
this.ws.onerror = () => {
|
|||
|
|
clearTimeout(connectionTimeoutHandle);
|
|||
|
|
reject("connection failed");
|
|||
|
|
this.connectionPromise = void 0;
|
|||
|
|
if (this.reconnectAttempts === 0) {
|
|||
|
|
this.skipReconnection = true;
|
|||
|
|
}
|
|||
|
|
this.handleHardClose("relay connection failed");
|
|||
|
|
};
|
|||
|
|
this.ws.onclose = (ev) => {
|
|||
|
|
clearTimeout(connectionTimeoutHandle);
|
|||
|
|
reject(ev.message || "websocket closed");
|
|||
|
|
this.handleHardClose("relay connection closed");
|
|||
|
|
};
|
|||
|
|
this.ws.onmessage = this._onmessage.bind(this);
|
|||
|
|
});
|
|||
|
|
return this.connectionPromise;
|
|||
|
|
}
|
|||
|
|
waitForPingPong() {
|
|||
|
|
return new Promise((resolve) => {
|
|||
|
|
;
|
|||
|
|
this.ws.once("pong", () => resolve(true));
|
|||
|
|
this.ws.ping();
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
waitForDummyReq() {
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
if (!this.connectionPromise)
|
|||
|
|
return reject(new Error(`no connection to ${this.url}, can't ping`));
|
|||
|
|
try {
|
|||
|
|
const sub = this.subscribe(
|
|||
|
|
[{ ids: ["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"], limit: 0 }],
|
|||
|
|
{
|
|||
|
|
label: "<forced-ping>",
|
|||
|
|
oneose: () => {
|
|||
|
|
resolve(true);
|
|||
|
|
sub.close();
|
|||
|
|
},
|
|||
|
|
onclose() {
|
|||
|
|
resolve(true);
|
|||
|
|
},
|
|||
|
|
eoseTimeout: this.pingTimeout + 1e3
|
|||
|
|
}
|
|||
|
|
);
|
|||
|
|
} catch (err) {
|
|||
|
|
reject(err);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
async pingpong() {
|
|||
|
|
if (this.ws?.readyState === 1) {
|
|||
|
|
const result = await Promise.any([
|
|||
|
|
this.ws && this.ws.ping && this.ws.once ? this.waitForPingPong() : this.waitForDummyReq(),
|
|||
|
|
new Promise((res) => setTimeout(() => res(false), this.pingTimeout))
|
|||
|
|
]);
|
|||
|
|
if (!result) {
|
|||
|
|
if (this.ws?.readyState === this._WebSocket.OPEN) {
|
|||
|
|
this.ws?.close();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
async send(message) {
|
|||
|
|
if (!this.connectionPromise)
|
|||
|
|
throw new SendingOnClosedConnection(message, this.url);
|
|||
|
|
this.connectionPromise.then(() => {
|
|||
|
|
this.ws?.send(message);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
async auth(signAuthEvent) {
|
|||
|
|
const challenge2 = this.challenge;
|
|||
|
|
if (!challenge2)
|
|||
|
|
throw new Error("can't perform auth, no challenge was received");
|
|||
|
|
if (this.authPromise)
|
|||
|
|
return this.authPromise;
|
|||
|
|
this.authPromise = new Promise(async (resolve, reject) => {
|
|||
|
|
try {
|
|||
|
|
let evt = await signAuthEvent(makeAuthEvent(this.url, challenge2));
|
|||
|
|
let timeout = setTimeout(() => {
|
|||
|
|
let ep = this.openEventPublishes.get(evt.id);
|
|||
|
|
if (ep) {
|
|||
|
|
ep.reject(new Error("auth timed out"));
|
|||
|
|
this.openEventPublishes.delete(evt.id);
|
|||
|
|
}
|
|||
|
|
}, this.publishTimeout);
|
|||
|
|
this.openEventPublishes.set(evt.id, { resolve, reject, timeout });
|
|||
|
|
this.send('["AUTH",' + JSON.stringify(evt) + "]");
|
|||
|
|
} catch (err) {
|
|||
|
|
console.warn("subscribe auth function failed:", err);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
return this.authPromise;
|
|||
|
|
}
|
|||
|
|
async publish(event) {
|
|||
|
|
this.idleSince = void 0;
|
|||
|
|
this.clearIdleTimeout();
|
|||
|
|
this.ongoingOperations++;
|
|||
|
|
const ret = new Promise((resolve, reject) => {
|
|||
|
|
const timeout = setTimeout(() => {
|
|||
|
|
const ep = this.openEventPublishes.get(event.id);
|
|||
|
|
if (ep) {
|
|||
|
|
ep.reject(new Error("publish timed out"));
|
|||
|
|
this.openEventPublishes.delete(event.id);
|
|||
|
|
}
|
|||
|
|
}, this.publishTimeout);
|
|||
|
|
this.openEventPublishes.set(event.id, { resolve, reject, timeout });
|
|||
|
|
});
|
|||
|
|
try {
|
|||
|
|
await this.send('["EVENT",' + JSON.stringify(event) + "]");
|
|||
|
|
} catch (err) {
|
|||
|
|
const ep = this.openEventPublishes.get(event.id);
|
|||
|
|
if (ep) {
|
|||
|
|
ep.reject(err);
|
|||
|
|
this.openEventPublishes.delete(event.id);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
this.ongoingOperations--;
|
|||
|
|
if (this.ongoingOperations === 0) {
|
|||
|
|
this.idleSince = Date.now();
|
|||
|
|
this.scheduleIdleClose();
|
|||
|
|
}
|
|||
|
|
return ret;
|
|||
|
|
}
|
|||
|
|
async count(filters, params) {
|
|||
|
|
return (await this.countWithHLL(filters, params)).count;
|
|||
|
|
}
|
|||
|
|
async countWithHLL(filters, params) {
|
|||
|
|
this.serial++;
|
|||
|
|
const id = params?.id || "count:" + this.serial;
|
|||
|
|
const ret = new Promise((resolve, reject) => {
|
|||
|
|
this.openCountRequests.set(id, { resolve, reject });
|
|||
|
|
});
|
|||
|
|
try {
|
|||
|
|
await this.send('["COUNT","' + id + '",' + JSON.stringify(filters).substring(1));
|
|||
|
|
} catch (err) {
|
|||
|
|
const cr = this.openCountRequests.get(id);
|
|||
|
|
if (cr) {
|
|||
|
|
cr.reject(err);
|
|||
|
|
this.openCountRequests.delete(id);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return ret;
|
|||
|
|
}
|
|||
|
|
subscribe(filters, params) {
|
|||
|
|
if (params.label !== "<forced-ping>") {
|
|||
|
|
this.idleSince = void 0;
|
|||
|
|
this.clearIdleTimeout();
|
|||
|
|
this.ongoingOperations++;
|
|||
|
|
}
|
|||
|
|
const sub = this.prepareSubscription(filters, params);
|
|||
|
|
sub.fire();
|
|||
|
|
if (params.abort) {
|
|||
|
|
params.abort.onabort = () => sub.close(String(params.abort.reason || "<aborted>"));
|
|||
|
|
}
|
|||
|
|
return sub;
|
|||
|
|
}
|
|||
|
|
prepareSubscription(filters, params) {
|
|||
|
|
this.serial++;
|
|||
|
|
const id = params.id || (params.label ? params.label + ":" : "sub:") + this.serial;
|
|||
|
|
const sub = new Subscription(this, id, filters, params);
|
|||
|
|
this.openSubs.set(id, sub);
|
|||
|
|
return sub;
|
|||
|
|
}
|
|||
|
|
close() {
|
|||
|
|
this.skipReconnection = true;
|
|||
|
|
if (this.reconnectTimeoutHandle) {
|
|||
|
|
clearTimeout(this.reconnectTimeoutHandle);
|
|||
|
|
this.reconnectTimeoutHandle = void 0;
|
|||
|
|
}
|
|||
|
|
if (this.pingIntervalHandle) {
|
|||
|
|
clearInterval(this.pingIntervalHandle);
|
|||
|
|
this.pingIntervalHandle = void 0;
|
|||
|
|
}
|
|||
|
|
this.closeAllSubscriptions("relay connection closed by us");
|
|||
|
|
this._connected = false;
|
|||
|
|
this.connectionPromise = void 0;
|
|||
|
|
this.idleSince = void 0;
|
|||
|
|
this.clearIdleTimeout();
|
|||
|
|
this.onclose?.();
|
|||
|
|
if (this.ws) {
|
|||
|
|
this.ws.onopen = null;
|
|||
|
|
this.ws.onerror = null;
|
|||
|
|
this.ws.onclose = null;
|
|||
|
|
if (this.ws.readyState !== this._WebSocket.CLOSING && this.ws.readyState !== this._WebSocket.CLOSED) {
|
|||
|
|
this.ws.close();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
_onmessage(ev) {
|
|||
|
|
const json = ev.data;
|
|||
|
|
if (!json) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
const subid = getSubscriptionId(json);
|
|||
|
|
if (subid) {
|
|||
|
|
const so = this.openSubs.get(subid);
|
|||
|
|
if (!so) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
const id = getHex64(json, "id");
|
|||
|
|
const alreadyHave = so.alreadyHaveEvent?.(id);
|
|||
|
|
so.receivedEvent?.(this, id);
|
|||
|
|
if (alreadyHave) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
try {
|
|||
|
|
let data = JSON.parse(json);
|
|||
|
|
switch (data[0]) {
|
|||
|
|
case "EVENT": {
|
|||
|
|
const so = this.openSubs.get(data[1]);
|
|||
|
|
const event = data[2];
|
|||
|
|
if (matchFilters(so.filters, event) && this.verifyEvent(event, this.url)) {
|
|||
|
|
so.onevent(event);
|
|||
|
|
} else {
|
|||
|
|
so.oninvalidevent?.(event);
|
|||
|
|
}
|
|||
|
|
if (!so.lastEmitted || so.lastEmitted < event.created_at)
|
|||
|
|
so.lastEmitted = event.created_at;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
case "COUNT": {
|
|||
|
|
const id = data[1];
|
|||
|
|
const payload = data[2];
|
|||
|
|
const cr = this.openCountRequests.get(id);
|
|||
|
|
if (cr) {
|
|||
|
|
cr.resolve(payload);
|
|||
|
|
this.openCountRequests.delete(id);
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
case "EOSE": {
|
|||
|
|
const so = this.openSubs.get(data[1]);
|
|||
|
|
if (!so)
|
|||
|
|
return;
|
|||
|
|
so.receivedEose();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
case "OK": {
|
|||
|
|
const id = data[1];
|
|||
|
|
const ok = data[2];
|
|||
|
|
const reason = data[3];
|
|||
|
|
const ep = this.openEventPublishes.get(id);
|
|||
|
|
if (ep) {
|
|||
|
|
clearTimeout(ep.timeout);
|
|||
|
|
if (ok)
|
|||
|
|
ep.resolve(reason);
|
|||
|
|
else
|
|||
|
|
ep.reject(new Error(reason));
|
|||
|
|
this.openEventPublishes.delete(id);
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
case "CLOSED": {
|
|||
|
|
const id = data[1];
|
|||
|
|
const so = this.openSubs.get(id);
|
|||
|
|
if (!so) {
|
|||
|
|
const cr = this.openCountRequests.get(id);
|
|||
|
|
if (cr) {
|
|||
|
|
cr.reject(new Error(data[2]));
|
|||
|
|
this.openCountRequests.delete(id);
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
so.closed = true;
|
|||
|
|
so.close(data[2]);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
case "NOTICE": {
|
|||
|
|
this.onnotice(data[1]);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
case "AUTH": {
|
|||
|
|
this.challenge = data[1];
|
|||
|
|
if (this.onauth) {
|
|||
|
|
this.auth(this.onauth).catch((err) => {
|
|||
|
|
if (!(err instanceof SendingOnClosedConnection)) {
|
|||
|
|
throw err;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
default: {
|
|||
|
|
const so = this.openSubs.get(data[1]);
|
|||
|
|
so?.oncustom?.(data);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} catch (err) {
|
|||
|
|
try {
|
|||
|
|
const [_, __, event] = JSON.parse(json);
|
|||
|
|
console.warn(`[nostr] relay ${this.url} error processing message:`, err, event);
|
|||
|
|
} catch (_) {
|
|||
|
|
console.warn(`[nostr] relay ${this.url} error processing message:`, err);
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
Subscription = class {
|
|||
|
|
relay;
|
|||
|
|
id;
|
|||
|
|
lastEmitted;
|
|||
|
|
closed = false;
|
|||
|
|
eosed = false;
|
|||
|
|
filters;
|
|||
|
|
alreadyHaveEvent;
|
|||
|
|
receivedEvent;
|
|||
|
|
onevent;
|
|||
|
|
oninvalidevent;
|
|||
|
|
oneose;
|
|||
|
|
onclose;
|
|||
|
|
oncustom;
|
|||
|
|
eoseTimeout;
|
|||
|
|
eoseTimeoutHandle;
|
|||
|
|
constructor(relay, id, filters, params) {
|
|||
|
|
if (filters.length === 0)
|
|||
|
|
throw new Error("subscription can't be created with zero filters");
|
|||
|
|
this.relay = relay;
|
|||
|
|
this.filters = filters;
|
|||
|
|
this.id = id;
|
|||
|
|
this.alreadyHaveEvent = params.alreadyHaveEvent;
|
|||
|
|
this.receivedEvent = params.receivedEvent;
|
|||
|
|
this.eoseTimeout = params.eoseTimeout || relay.baseEoseTimeout;
|
|||
|
|
this.oneose = params.oneose;
|
|||
|
|
this.onclose = params.onclose;
|
|||
|
|
this.oninvalidevent = params.oninvalidevent;
|
|||
|
|
this.onevent = params.onevent || ((event) => {
|
|||
|
|
console.warn(
|
|||
|
|
`onevent() callback not defined for subscription '${this.id}' in relay ${this.relay.url}. event received:`,
|
|||
|
|
event
|
|||
|
|
);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
fire() {
|
|||
|
|
this.relay.send('["REQ","' + this.id + '",' + JSON.stringify(this.filters).substring(1));
|
|||
|
|
this.eoseTimeoutHandle = setTimeout(this.receivedEose.bind(this), this.eoseTimeout);
|
|||
|
|
}
|
|||
|
|
receivedEose() {
|
|||
|
|
if (this.eosed)
|
|||
|
|
return;
|
|||
|
|
clearTimeout(this.eoseTimeoutHandle);
|
|||
|
|
this.eosed = true;
|
|||
|
|
this.oneose?.();
|
|||
|
|
}
|
|||
|
|
close(reason = "closed by caller") {
|
|||
|
|
if (!this.closed && this.relay.connected) {
|
|||
|
|
try {
|
|||
|
|
this.relay.send('["CLOSE",' + JSON.stringify(this.id) + "]");
|
|||
|
|
} catch (err) {
|
|||
|
|
if (err instanceof SendingOnClosedConnection) {
|
|||
|
|
} else {
|
|||
|
|
throw err;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
this.closed = true;
|
|||
|
|
}
|
|||
|
|
this.relay.openSubs.delete(this.id);
|
|||
|
|
this.relay.ongoingOperations--;
|
|||
|
|
if (this.relay.ongoingOperations === 0) {
|
|||
|
|
this.relay.idleSince = Date.now();
|
|||
|
|
this.relay.scheduleIdleClose();
|
|||
|
|
}
|
|||
|
|
this.onclose?.(reason);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
M = 256;
|
|||
|
|
HLL_HEX_LENGTH = M * 2;
|
|||
|
|
utf8Encoder2 = new TextEncoder();
|
|||
|
|
AbstractSimplePool = class {
|
|||
|
|
relays = /* @__PURE__ */ new Map();
|
|||
|
|
seenOn = /* @__PURE__ */ new Map();
|
|||
|
|
trackRelays = false;
|
|||
|
|
verifyEvent;
|
|||
|
|
enablePing;
|
|||
|
|
enableReconnect;
|
|||
|
|
idleTimeout = 2e4;
|
|||
|
|
automaticallyAuth;
|
|||
|
|
onRelayConnectionFailure;
|
|||
|
|
onRelayConnectionSuccess;
|
|||
|
|
allowConnectingToRelay;
|
|||
|
|
maxWaitForConnection;
|
|||
|
|
_WebSocket;
|
|||
|
|
constructor(opts) {
|
|||
|
|
this.verifyEvent = opts.verifyEvent;
|
|||
|
|
this._WebSocket = opts.websocketImplementation;
|
|||
|
|
this.enablePing = opts.enablePing;
|
|||
|
|
this.enableReconnect = opts.enableReconnect || false;
|
|||
|
|
if (opts.idleTimeout)
|
|||
|
|
this.idleTimeout = opts.idleTimeout;
|
|||
|
|
this.automaticallyAuth = opts.automaticallyAuth;
|
|||
|
|
this.onRelayConnectionFailure = opts.onRelayConnectionFailure;
|
|||
|
|
this.onRelayConnectionSuccess = opts.onRelayConnectionSuccess;
|
|||
|
|
this.allowConnectingToRelay = opts.allowConnectingToRelay;
|
|||
|
|
this.maxWaitForConnection = opts.maxWaitForConnection || 3e3;
|
|||
|
|
}
|
|||
|
|
async ensureRelay(url, params) {
|
|||
|
|
url = normalizeURL(url);
|
|||
|
|
let relay = this.relays.get(url);
|
|||
|
|
if (!relay) {
|
|||
|
|
relay = new AbstractRelay(url, {
|
|||
|
|
verifyEvent: this.verifyEvent,
|
|||
|
|
websocketImplementation: this._WebSocket,
|
|||
|
|
enablePing: this.enablePing,
|
|||
|
|
enableReconnect: this.enableReconnect,
|
|||
|
|
idleTimeout: this.idleTimeout
|
|||
|
|
});
|
|||
|
|
relay.onclose = () => {
|
|||
|
|
this.relays.delete(url);
|
|||
|
|
};
|
|||
|
|
this.relays.set(url, relay);
|
|||
|
|
}
|
|||
|
|
if (this.automaticallyAuth) {
|
|||
|
|
const authSignerFn = this.automaticallyAuth(url);
|
|||
|
|
if (authSignerFn) {
|
|||
|
|
relay.onauth = authSignerFn;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
try {
|
|||
|
|
await relay.connect({
|
|||
|
|
timeout: params?.connectionTimeout,
|
|||
|
|
abort: params?.abort
|
|||
|
|
});
|
|||
|
|
} catch (err) {
|
|||
|
|
this.relays.delete(url);
|
|||
|
|
throw err;
|
|||
|
|
}
|
|||
|
|
return relay;
|
|||
|
|
}
|
|||
|
|
close(relays) {
|
|||
|
|
relays.map(normalizeURL).forEach((url) => {
|
|||
|
|
this.relays.get(url)?.close();
|
|||
|
|
this.relays.delete(url);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
subscribe(relays, filter, params) {
|
|||
|
|
const request = [];
|
|||
|
|
const uniqUrls = [];
|
|||
|
|
for (let i22 = 0; i22 < relays.length; i22++) {
|
|||
|
|
const url = normalizeURL(relays[i22]);
|
|||
|
|
if (!request.find((r) => r.url === url)) {
|
|||
|
|
if (uniqUrls.indexOf(url) === -1) {
|
|||
|
|
uniqUrls.push(url);
|
|||
|
|
request.push({ url, filter });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return this.subscribeMap(request, params);
|
|||
|
|
}
|
|||
|
|
subscribeMany(relays, filter, params) {
|
|||
|
|
return this.subscribe(relays, filter, params);
|
|||
|
|
}
|
|||
|
|
subscribeMap(requests, params) {
|
|||
|
|
const grouped = /* @__PURE__ */ new Map();
|
|||
|
|
for (const req of requests) {
|
|||
|
|
const { url, filter } = req;
|
|||
|
|
if (!grouped.has(url))
|
|||
|
|
grouped.set(url, []);
|
|||
|
|
grouped.get(url).push(filter);
|
|||
|
|
}
|
|||
|
|
const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }));
|
|||
|
|
if (this.trackRelays) {
|
|||
|
|
params.receivedEvent = (relay, id) => {
|
|||
|
|
let set = this.seenOn.get(id);
|
|||
|
|
if (!set) {
|
|||
|
|
set = /* @__PURE__ */ new Set();
|
|||
|
|
this.seenOn.set(id, set);
|
|||
|
|
}
|
|||
|
|
set.add(relay);
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
const _knownIds = /* @__PURE__ */ new Set();
|
|||
|
|
const subs = [];
|
|||
|
|
const eosesReceived = [];
|
|||
|
|
let handleEose = (i22) => {
|
|||
|
|
if (eosesReceived[i22])
|
|||
|
|
return;
|
|||
|
|
eosesReceived[i22] = true;
|
|||
|
|
if (eosesReceived.filter((a) => a).length === groupedRequests.length) {
|
|||
|
|
params.oneose?.();
|
|||
|
|
handleEose = () => {
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
const closesReceived = [];
|
|||
|
|
let handleClose = (i22, url, reason) => {
|
|||
|
|
if (closesReceived[i22])
|
|||
|
|
return;
|
|||
|
|
handleEose(i22);
|
|||
|
|
closesReceived[i22] = { url, reason };
|
|||
|
|
if (closesReceived.filter((a) => a).length === groupedRequests.length) {
|
|||
|
|
params.onclose?.(closesReceived);
|
|||
|
|
handleClose = () => {
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
const localAlreadyHaveEventHandler = (id) => {
|
|||
|
|
if (params.alreadyHaveEvent?.(id)) {
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
const have = _knownIds.has(id);
|
|||
|
|
_knownIds.add(id);
|
|||
|
|
return have;
|
|||
|
|
};
|
|||
|
|
const allOpened = Promise.all(
|
|||
|
|
groupedRequests.map(async ({ url, filters }, i22) => {
|
|||
|
|
if (this.allowConnectingToRelay?.(url, ["read", filters]) === false) {
|
|||
|
|
handleClose(i22, url, "connection skipped by allowConnectingToRelay");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
let relay;
|
|||
|
|
try {
|
|||
|
|
relay = await this.ensureRelay(url, {
|
|||
|
|
connectionTimeout: this.maxWaitForConnection < (params.maxWait || 0) ? Math.max(params.maxWait * 0.8, params.maxWait - 1e3) : this.maxWaitForConnection,
|
|||
|
|
abort: params.abort
|
|||
|
|
});
|
|||
|
|
} catch (err) {
|
|||
|
|
this.onRelayConnectionFailure?.(url);
|
|||
|
|
handleClose(i22, url, err?.message || String(err));
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
this.onRelayConnectionSuccess?.(url);
|
|||
|
|
let subscription = relay.subscribe(filters, {
|
|||
|
|
...params,
|
|||
|
|
oneose: () => handleEose(i22),
|
|||
|
|
onclose: (reason) => {
|
|||
|
|
if (reason.startsWith("auth-required: ") && params.onauth) {
|
|||
|
|
relay.auth(params.onauth).then(() => {
|
|||
|
|
relay.subscribe(filters, {
|
|||
|
|
...params,
|
|||
|
|
oneose: () => handleEose(i22),
|
|||
|
|
onclose: (reason2) => {
|
|||
|
|
handleClose(i22, url, reason2);
|
|||
|
|
},
|
|||
|
|
alreadyHaveEvent: localAlreadyHaveEventHandler,
|
|||
|
|
eoseTimeout: params.maxWait,
|
|||
|
|
abort: params.abort
|
|||
|
|
});
|
|||
|
|
}).catch((err) => {
|
|||
|
|
handleClose(i22, url, `auth was required and attempted, but failed with: ${err}`);
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
handleClose(i22, url, reason);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
alreadyHaveEvent: localAlreadyHaveEventHandler,
|
|||
|
|
eoseTimeout: params.maxWait,
|
|||
|
|
abort: params.abort
|
|||
|
|
});
|
|||
|
|
subs.push(subscription);
|
|||
|
|
})
|
|||
|
|
);
|
|||
|
|
return {
|
|||
|
|
async close(reason) {
|
|||
|
|
await allOpened;
|
|||
|
|
subs.forEach((sub) => {
|
|||
|
|
sub.close(reason);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
subscribeEose(relays, filter, params) {
|
|||
|
|
let subcloser;
|
|||
|
|
subcloser = this.subscribe(relays, filter, {
|
|||
|
|
...params,
|
|||
|
|
oneose() {
|
|||
|
|
const reason = "closed automatically on eose";
|
|||
|
|
if (subcloser)
|
|||
|
|
subcloser.close(reason);
|
|||
|
|
else
|
|||
|
|
params.onclose?.(relays.map((url) => ({ url, reason })));
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
return subcloser;
|
|||
|
|
}
|
|||
|
|
subscribeManyEose(relays, filter, params) {
|
|||
|
|
return this.subscribeEose(relays, filter, params);
|
|||
|
|
}
|
|||
|
|
async querySync(relays, filter, params) {
|
|||
|
|
return new Promise(async (resolve) => {
|
|||
|
|
const events = [];
|
|||
|
|
this.subscribeEose(relays, filter, {
|
|||
|
|
...params,
|
|||
|
|
onevent(event) {
|
|||
|
|
events.push(event);
|
|||
|
|
},
|
|||
|
|
onclose(_) {
|
|||
|
|
resolve(events);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
async get(relays, filter, params) {
|
|||
|
|
filter.limit = 1;
|
|||
|
|
const events = await this.querySync(relays, filter, params);
|
|||
|
|
events.sort((a, b) => b.created_at - a.created_at);
|
|||
|
|
return events[0] || null;
|
|||
|
|
}
|
|||
|
|
async countMany(relays, target, directive, params) {
|
|||
|
|
const filter = getCountManyFilter(target, directive);
|
|||
|
|
const urls = [];
|
|||
|
|
for (let i22 = 0; i22 < relays.length; i22++) {
|
|||
|
|
const url = normalizeURL(relays[i22]);
|
|||
|
|
if (urls.indexOf(url) === -1)
|
|||
|
|
urls.push(url);
|
|||
|
|
}
|
|||
|
|
const responses = await Promise.all(
|
|||
|
|
urls.map(async (url) => {
|
|||
|
|
if (this.allowConnectingToRelay?.(url, ["read", [filter]]) === false)
|
|||
|
|
return null;
|
|||
|
|
let relay;
|
|||
|
|
try {
|
|||
|
|
relay = await this.ensureRelay(url, {
|
|||
|
|
connectionTimeout: this.maxWaitForConnection < (params?.maxWait || 0) ? Math.max(params.maxWait * 0.8, params.maxWait - 1e3) : this.maxWaitForConnection,
|
|||
|
|
abort: params?.abort
|
|||
|
|
});
|
|||
|
|
} catch (err) {
|
|||
|
|
this.onRelayConnectionFailure?.(url);
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
this.onRelayConnectionSuccess?.(url);
|
|||
|
|
return relay.countWithHLL([filter], { id: params?.id }).catch(() => null);
|
|||
|
|
})
|
|||
|
|
);
|
|||
|
|
let count = 0;
|
|||
|
|
let hll;
|
|||
|
|
for (const response of responses) {
|
|||
|
|
if (!response)
|
|||
|
|
continue;
|
|||
|
|
if (response.count > count)
|
|||
|
|
count = response.count;
|
|||
|
|
if (!response.hll || response.hll.length !== 512)
|
|||
|
|
continue;
|
|||
|
|
const registers = hllDecode(response.hll);
|
|||
|
|
if (!registers)
|
|||
|
|
continue;
|
|||
|
|
hll = mergeHll(hll || new Uint8Array(0), registers);
|
|||
|
|
}
|
|||
|
|
return hll ? { count, hll: hllEncode(hll) } : { count };
|
|||
|
|
}
|
|||
|
|
publish(relays, event, params) {
|
|||
|
|
return relays.map(normalizeURL).map(async (url, i22, arr) => {
|
|||
|
|
if (arr.indexOf(url) !== i22) {
|
|||
|
|
return Promise.reject("duplicate url");
|
|||
|
|
}
|
|||
|
|
if (this.allowConnectingToRelay?.(url, ["write", event]) === false) {
|
|||
|
|
return Promise.reject("connection skipped by allowConnectingToRelay");
|
|||
|
|
}
|
|||
|
|
let r;
|
|||
|
|
try {
|
|||
|
|
r = await this.ensureRelay(url, {
|
|||
|
|
connectionTimeout: this.maxWaitForConnection < (params?.maxWait || 0) ? Math.max(params.maxWait * 0.8, params.maxWait - 1e3) : this.maxWaitForConnection,
|
|||
|
|
abort: params?.abort
|
|||
|
|
});
|
|||
|
|
} catch (err) {
|
|||
|
|
this.onRelayConnectionFailure?.(url);
|
|||
|
|
return String("connection failure: " + String(err));
|
|||
|
|
}
|
|||
|
|
return r.publish(event).catch(async (err) => {
|
|||
|
|
if (err instanceof Error && err.message.startsWith("auth-required: ") && params?.onauth) {
|
|||
|
|
await r.auth(params.onauth);
|
|||
|
|
return r.publish(event);
|
|||
|
|
}
|
|||
|
|
throw err;
|
|||
|
|
}).then((reason) => {
|
|||
|
|
if (this.trackRelays) {
|
|||
|
|
let set = this.seenOn.get(event.id);
|
|||
|
|
if (!set) {
|
|||
|
|
set = /* @__PURE__ */ new Set();
|
|||
|
|
this.seenOn.set(event.id, set);
|
|||
|
|
}
|
|||
|
|
set.add(r);
|
|||
|
|
}
|
|||
|
|
return reason;
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
listConnectionStatus() {
|
|||
|
|
const map = /* @__PURE__ */ new Map();
|
|||
|
|
this.relays.forEach((relay, url) => map.set(url, relay.connected));
|
|||
|
|
return map;
|
|||
|
|
}
|
|||
|
|
destroy() {
|
|||
|
|
this.relays.forEach((conn) => conn.close());
|
|||
|
|
this.relays = /* @__PURE__ */ new Map();
|
|||
|
|
}
|
|||
|
|
pruneIdleRelays(idleThresholdMs = 1e4) {
|
|||
|
|
const prunedUrls = [];
|
|||
|
|
for (const [url, relay] of this.relays) {
|
|||
|
|
if (relay.idleSince && Date.now() - relay.idleSince >= idleThresholdMs) {
|
|||
|
|
this.relays.delete(url);
|
|||
|
|
prunedUrls.push(url);
|
|||
|
|
relay.close();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return prunedUrls;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
try {
|
|||
|
|
_WebSocket = WebSocket;
|
|||
|
|
} catch {
|
|||
|
|
}
|
|||
|
|
SimplePool = class extends AbstractSimplePool {
|
|||
|
|
constructor(options) {
|
|||
|
|
super({ verifyEvent, websocketImplementation: _WebSocket, maxWaitForConnection: 3e3, ...options });
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@noble/ciphers/utils.js
|
|||
|
|
function isBytes2(a) {
|
|||
|
|
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
|
|||
|
|
}
|
|||
|
|
function abool2(b) {
|
|||
|
|
if (typeof b !== "boolean")
|
|||
|
|
throw new Error(`boolean expected, not ${b}`);
|
|||
|
|
}
|
|||
|
|
function anumber2(n) {
|
|||
|
|
if (!Number.isSafeInteger(n) || n < 0)
|
|||
|
|
throw new Error("positive integer expected, got " + n);
|
|||
|
|
}
|
|||
|
|
function abytes2(value, length, title = "") {
|
|||
|
|
const bytes = isBytes2(value);
|
|||
|
|
const len = value?.length;
|
|||
|
|
const needsLen = length !== void 0;
|
|||
|
|
if (!bytes || needsLen && len !== length) {
|
|||
|
|
const prefix = title && `"${title}" `;
|
|||
|
|
const ofLen = needsLen ? ` of length ${length}` : "";
|
|||
|
|
const got = bytes ? `length=${len}` : `type=${typeof value}`;
|
|||
|
|
throw new Error(prefix + "expected Uint8Array" + ofLen + ", got " + got);
|
|||
|
|
}
|
|||
|
|
return value;
|
|||
|
|
}
|
|||
|
|
function aexists2(instance, checkFinished = true) {
|
|||
|
|
if (instance.destroyed)
|
|||
|
|
throw new Error("Hash instance has been destroyed");
|
|||
|
|
if (checkFinished && instance.finished)
|
|||
|
|
throw new Error("Hash#digest() has already been called");
|
|||
|
|
}
|
|||
|
|
function aoutput2(out, instance) {
|
|||
|
|
abytes2(out, void 0, "output");
|
|||
|
|
const min = instance.outputLen;
|
|||
|
|
if (out.length < min) {
|
|||
|
|
throw new Error("digestInto() expects output buffer of length at least " + min);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function u32(arr) {
|
|||
|
|
return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
|
|||
|
|
}
|
|||
|
|
function clean2(...arrays) {
|
|||
|
|
for (let i3 = 0; i3 < arrays.length; i3++) {
|
|||
|
|
arrays[i3].fill(0);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function createView2(arr) {
|
|||
|
|
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
|
|||
|
|
}
|
|||
|
|
function checkOpts(defaults, opts) {
|
|||
|
|
if (opts == null || typeof opts !== "object")
|
|||
|
|
throw new Error("options must be defined");
|
|||
|
|
const merged = Object.assign(defaults, opts);
|
|||
|
|
return merged;
|
|||
|
|
}
|
|||
|
|
function equalBytes(a, b) {
|
|||
|
|
if (a.length !== b.length)
|
|||
|
|
return false;
|
|||
|
|
let diff = 0;
|
|||
|
|
for (let i3 = 0; i3 < a.length; i3++)
|
|||
|
|
diff |= a[i3] ^ b[i3];
|
|||
|
|
return diff === 0;
|
|||
|
|
}
|
|||
|
|
function getOutput(expectedLength, out, onlyAligned = true) {
|
|||
|
|
if (out === void 0)
|
|||
|
|
return new Uint8Array(expectedLength);
|
|||
|
|
if (out.length !== expectedLength)
|
|||
|
|
throw new Error('"output" expected Uint8Array of length ' + expectedLength + ", got: " + out.length);
|
|||
|
|
if (onlyAligned && !isAligned32(out))
|
|||
|
|
throw new Error("invalid output, must be aligned");
|
|||
|
|
return out;
|
|||
|
|
}
|
|||
|
|
function u64Lengths(dataLength, aadLength, isLE2) {
|
|||
|
|
abool2(isLE2);
|
|||
|
|
const num2 = new Uint8Array(16);
|
|||
|
|
const view = createView2(num2);
|
|||
|
|
view.setBigUint64(0, BigInt(aadLength), isLE2);
|
|||
|
|
view.setBigUint64(8, BigInt(dataLength), isLE2);
|
|||
|
|
return num2;
|
|||
|
|
}
|
|||
|
|
function isAligned32(bytes) {
|
|||
|
|
return bytes.byteOffset % 4 === 0;
|
|||
|
|
}
|
|||
|
|
function copyBytes2(bytes) {
|
|||
|
|
return Uint8Array.from(bytes);
|
|||
|
|
}
|
|||
|
|
var isLE, wrapCipher;
|
|||
|
|
var init_utils3 = __esm({
|
|||
|
|
"node_modules/@noble/ciphers/utils.js"() {
|
|||
|
|
isLE = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68)();
|
|||
|
|
wrapCipher = /* @__NO_SIDE_EFFECTS__ */ (params, constructor) => {
|
|||
|
|
function wrappedCipher(key, ...args) {
|
|||
|
|
abytes2(key, void 0, "key");
|
|||
|
|
if (!isLE)
|
|||
|
|
throw new Error("Non little-endian hardware is not yet supported");
|
|||
|
|
if (params.nonceLength !== void 0) {
|
|||
|
|
const nonce = args[0];
|
|||
|
|
abytes2(nonce, params.varSizeNonce ? void 0 : params.nonceLength, "nonce");
|
|||
|
|
}
|
|||
|
|
const tagl = params.tagLength;
|
|||
|
|
if (tagl && args[1] !== void 0)
|
|||
|
|
abytes2(args[1], void 0, "AAD");
|
|||
|
|
const cipher = constructor(key, ...args);
|
|||
|
|
const checkOutput = (fnLength, output) => {
|
|||
|
|
if (output !== void 0) {
|
|||
|
|
if (fnLength !== 2)
|
|||
|
|
throw new Error("cipher output not supported");
|
|||
|
|
abytes2(output, void 0, "output");
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
let called = false;
|
|||
|
|
const wrCipher = {
|
|||
|
|
encrypt(data, output) {
|
|||
|
|
if (called)
|
|||
|
|
throw new Error("cannot encrypt() twice with same key + nonce");
|
|||
|
|
called = true;
|
|||
|
|
abytes2(data);
|
|||
|
|
checkOutput(cipher.encrypt.length, output);
|
|||
|
|
return cipher.encrypt(data, output);
|
|||
|
|
},
|
|||
|
|
decrypt(data, output) {
|
|||
|
|
abytes2(data);
|
|||
|
|
if (tagl && data.length < tagl)
|
|||
|
|
throw new Error('"ciphertext" expected length bigger than tagLength=' + tagl);
|
|||
|
|
checkOutput(cipher.decrypt.length, output);
|
|||
|
|
return cipher.decrypt(data, output);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
return wrCipher;
|
|||
|
|
}
|
|||
|
|
Object.assign(wrappedCipher, params);
|
|||
|
|
return wrappedCipher;
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@noble/ciphers/_arx.js
|
|||
|
|
function rotl(a, b) {
|
|||
|
|
return a << b | a >>> 32 - b;
|
|||
|
|
}
|
|||
|
|
function isAligned322(b) {
|
|||
|
|
return b.byteOffset % 4 === 0;
|
|||
|
|
}
|
|||
|
|
function runCipher(core, sigma, key, nonce, data, output, counter, rounds) {
|
|||
|
|
const len = data.length;
|
|||
|
|
const block = new Uint8Array(BLOCK_LEN);
|
|||
|
|
const b32 = u32(block);
|
|||
|
|
const isAligned = isAligned322(data) && isAligned322(output);
|
|||
|
|
const d32 = isAligned ? u32(data) : U32_EMPTY;
|
|||
|
|
const o32 = isAligned ? u32(output) : U32_EMPTY;
|
|||
|
|
for (let pos = 0; pos < len; counter++) {
|
|||
|
|
core(sigma, key, nonce, b32, counter, rounds);
|
|||
|
|
if (counter >= MAX_COUNTER)
|
|||
|
|
throw new Error("arx: counter overflow");
|
|||
|
|
const take = Math.min(BLOCK_LEN, len - pos);
|
|||
|
|
if (isAligned && take === BLOCK_LEN) {
|
|||
|
|
const pos32 = pos / 4;
|
|||
|
|
if (pos % 4 !== 0)
|
|||
|
|
throw new Error("arx: invalid block position");
|
|||
|
|
for (let j = 0, posj; j < BLOCK_LEN32; j++) {
|
|||
|
|
posj = pos32 + j;
|
|||
|
|
o32[posj] = d32[posj] ^ b32[j];
|
|||
|
|
}
|
|||
|
|
pos += BLOCK_LEN;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
for (let j = 0, posj; j < take; j++) {
|
|||
|
|
posj = pos + j;
|
|||
|
|
output[posj] = data[posj] ^ block[j];
|
|||
|
|
}
|
|||
|
|
pos += take;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function createCipher(core, opts) {
|
|||
|
|
const { allowShortKeys, extendNonceFn, counterLength, counterRight, rounds } = checkOpts({ allowShortKeys: false, counterLength: 8, counterRight: false, rounds: 20 }, opts);
|
|||
|
|
if (typeof core !== "function")
|
|||
|
|
throw new Error("core must be a function");
|
|||
|
|
anumber2(counterLength);
|
|||
|
|
anumber2(rounds);
|
|||
|
|
abool2(counterRight);
|
|||
|
|
abool2(allowShortKeys);
|
|||
|
|
return (key, nonce, data, output, counter = 0) => {
|
|||
|
|
abytes2(key, void 0, "key");
|
|||
|
|
abytes2(nonce, void 0, "nonce");
|
|||
|
|
abytes2(data, void 0, "data");
|
|||
|
|
const len = data.length;
|
|||
|
|
if (output === void 0)
|
|||
|
|
output = new Uint8Array(len);
|
|||
|
|
abytes2(output, void 0, "output");
|
|||
|
|
anumber2(counter);
|
|||
|
|
if (counter < 0 || counter >= MAX_COUNTER)
|
|||
|
|
throw new Error("arx: counter overflow");
|
|||
|
|
if (output.length < len)
|
|||
|
|
throw new Error(`arx: output (${output.length}) is shorter than data (${len})`);
|
|||
|
|
const toClean = [];
|
|||
|
|
let l = key.length;
|
|||
|
|
let k;
|
|||
|
|
let sigma;
|
|||
|
|
if (l === 32) {
|
|||
|
|
toClean.push(k = copyBytes2(key));
|
|||
|
|
sigma = sigma32_32;
|
|||
|
|
} else if (l === 16 && allowShortKeys) {
|
|||
|
|
k = new Uint8Array(32);
|
|||
|
|
k.set(key);
|
|||
|
|
k.set(key, 16);
|
|||
|
|
sigma = sigma16_32;
|
|||
|
|
toClean.push(k);
|
|||
|
|
} else {
|
|||
|
|
abytes2(key, 32, "arx key");
|
|||
|
|
throw new Error("invalid key size");
|
|||
|
|
}
|
|||
|
|
if (!isAligned322(nonce))
|
|||
|
|
toClean.push(nonce = copyBytes2(nonce));
|
|||
|
|
const k32 = u32(k);
|
|||
|
|
if (extendNonceFn) {
|
|||
|
|
if (nonce.length !== 24)
|
|||
|
|
throw new Error(`arx: extended nonce must be 24 bytes`);
|
|||
|
|
extendNonceFn(sigma, k32, u32(nonce.subarray(0, 16)), k32);
|
|||
|
|
nonce = nonce.subarray(16);
|
|||
|
|
}
|
|||
|
|
const nonceNcLen = 16 - counterLength;
|
|||
|
|
if (nonceNcLen !== nonce.length)
|
|||
|
|
throw new Error(`arx: nonce must be ${nonceNcLen} or 16 bytes`);
|
|||
|
|
if (nonceNcLen !== 12) {
|
|||
|
|
const nc = new Uint8Array(12);
|
|||
|
|
nc.set(nonce, counterRight ? 0 : 12 - nonce.length);
|
|||
|
|
nonce = nc;
|
|||
|
|
toClean.push(nonce);
|
|||
|
|
}
|
|||
|
|
const n32 = u32(nonce);
|
|||
|
|
runCipher(core, sigma, k32, n32, data, output, counter, rounds);
|
|||
|
|
clean2(...toClean);
|
|||
|
|
return output;
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
var encodeStr, sigma16, sigma32, sigma16_32, sigma32_32, BLOCK_LEN, BLOCK_LEN32, MAX_COUNTER, U32_EMPTY;
|
|||
|
|
var init_arx = __esm({
|
|||
|
|
"node_modules/@noble/ciphers/_arx.js"() {
|
|||
|
|
init_utils3();
|
|||
|
|
encodeStr = (str) => Uint8Array.from(str.split(""), (c) => c.charCodeAt(0));
|
|||
|
|
sigma16 = encodeStr("expand 16-byte k");
|
|||
|
|
sigma32 = encodeStr("expand 32-byte k");
|
|||
|
|
sigma16_32 = u32(sigma16);
|
|||
|
|
sigma32_32 = u32(sigma32);
|
|||
|
|
BLOCK_LEN = 64;
|
|||
|
|
BLOCK_LEN32 = 16;
|
|||
|
|
MAX_COUNTER = 2 ** 32 - 1;
|
|||
|
|
U32_EMPTY = Uint32Array.of();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@noble/ciphers/_poly1305.js
|
|||
|
|
function u8to16(a, i3) {
|
|||
|
|
return a[i3++] & 255 | (a[i3++] & 255) << 8;
|
|||
|
|
}
|
|||
|
|
function wrapConstructorWithKey(hashCons) {
|
|||
|
|
const hashC = (msg, key) => hashCons(key).update(msg).digest();
|
|||
|
|
const tmp = hashCons(new Uint8Array(32));
|
|||
|
|
hashC.outputLen = tmp.outputLen;
|
|||
|
|
hashC.blockLen = tmp.blockLen;
|
|||
|
|
hashC.create = (key) => hashCons(key);
|
|||
|
|
return hashC;
|
|||
|
|
}
|
|||
|
|
var Poly1305, poly1305;
|
|||
|
|
var init_poly1305 = __esm({
|
|||
|
|
"node_modules/@noble/ciphers/_poly1305.js"() {
|
|||
|
|
init_utils3();
|
|||
|
|
Poly1305 = class {
|
|||
|
|
blockLen = 16;
|
|||
|
|
outputLen = 16;
|
|||
|
|
buffer = new Uint8Array(16);
|
|||
|
|
r = new Uint16Array(10);
|
|||
|
|
// Allocating 1 array with .subarray() here is slower than 3
|
|||
|
|
h = new Uint16Array(10);
|
|||
|
|
pad = new Uint16Array(8);
|
|||
|
|
pos = 0;
|
|||
|
|
finished = false;
|
|||
|
|
// Can be speed-up using BigUint64Array, at the cost of complexity
|
|||
|
|
constructor(key) {
|
|||
|
|
key = copyBytes2(abytes2(key, 32, "key"));
|
|||
|
|
const t0 = u8to16(key, 0);
|
|||
|
|
const t1 = u8to16(key, 2);
|
|||
|
|
const t2 = u8to16(key, 4);
|
|||
|
|
const t3 = u8to16(key, 6);
|
|||
|
|
const t4 = u8to16(key, 8);
|
|||
|
|
const t5 = u8to16(key, 10);
|
|||
|
|
const t6 = u8to16(key, 12);
|
|||
|
|
const t7 = u8to16(key, 14);
|
|||
|
|
this.r[0] = t0 & 8191;
|
|||
|
|
this.r[1] = (t0 >>> 13 | t1 << 3) & 8191;
|
|||
|
|
this.r[2] = (t1 >>> 10 | t2 << 6) & 7939;
|
|||
|
|
this.r[3] = (t2 >>> 7 | t3 << 9) & 8191;
|
|||
|
|
this.r[4] = (t3 >>> 4 | t4 << 12) & 255;
|
|||
|
|
this.r[5] = t4 >>> 1 & 8190;
|
|||
|
|
this.r[6] = (t4 >>> 14 | t5 << 2) & 8191;
|
|||
|
|
this.r[7] = (t5 >>> 11 | t6 << 5) & 8065;
|
|||
|
|
this.r[8] = (t6 >>> 8 | t7 << 8) & 8191;
|
|||
|
|
this.r[9] = t7 >>> 5 & 127;
|
|||
|
|
for (let i3 = 0; i3 < 8; i3++)
|
|||
|
|
this.pad[i3] = u8to16(key, 16 + 2 * i3);
|
|||
|
|
}
|
|||
|
|
process(data, offset, isLast = false) {
|
|||
|
|
const hibit = isLast ? 0 : 1 << 11;
|
|||
|
|
const { h, r } = this;
|
|||
|
|
const r0 = r[0];
|
|||
|
|
const r1 = r[1];
|
|||
|
|
const r2 = r[2];
|
|||
|
|
const r3 = r[3];
|
|||
|
|
const r4 = r[4];
|
|||
|
|
const r5 = r[5];
|
|||
|
|
const r6 = r[6];
|
|||
|
|
const r7 = r[7];
|
|||
|
|
const r8 = r[8];
|
|||
|
|
const r9 = r[9];
|
|||
|
|
const t0 = u8to16(data, offset + 0);
|
|||
|
|
const t1 = u8to16(data, offset + 2);
|
|||
|
|
const t2 = u8to16(data, offset + 4);
|
|||
|
|
const t3 = u8to16(data, offset + 6);
|
|||
|
|
const t4 = u8to16(data, offset + 8);
|
|||
|
|
const t5 = u8to16(data, offset + 10);
|
|||
|
|
const t6 = u8to16(data, offset + 12);
|
|||
|
|
const t7 = u8to16(data, offset + 14);
|
|||
|
|
let h0 = h[0] + (t0 & 8191);
|
|||
|
|
let h1 = h[1] + ((t0 >>> 13 | t1 << 3) & 8191);
|
|||
|
|
let h2 = h[2] + ((t1 >>> 10 | t2 << 6) & 8191);
|
|||
|
|
let h3 = h[3] + ((t2 >>> 7 | t3 << 9) & 8191);
|
|||
|
|
let h4 = h[4] + ((t3 >>> 4 | t4 << 12) & 8191);
|
|||
|
|
let h5 = h[5] + (t4 >>> 1 & 8191);
|
|||
|
|
let h6 = h[6] + ((t4 >>> 14 | t5 << 2) & 8191);
|
|||
|
|
let h7 = h[7] + ((t5 >>> 11 | t6 << 5) & 8191);
|
|||
|
|
let h8 = h[8] + ((t6 >>> 8 | t7 << 8) & 8191);
|
|||
|
|
let h9 = h[9] + (t7 >>> 5 | hibit);
|
|||
|
|
let c = 0;
|
|||
|
|
let d0 = c + h0 * r0 + h1 * (5 * r9) + h2 * (5 * r8) + h3 * (5 * r7) + h4 * (5 * r6);
|
|||
|
|
c = d0 >>> 13;
|
|||
|
|
d0 &= 8191;
|
|||
|
|
d0 += h5 * (5 * r5) + h6 * (5 * r4) + h7 * (5 * r3) + h8 * (5 * r2) + h9 * (5 * r1);
|
|||
|
|
c += d0 >>> 13;
|
|||
|
|
d0 &= 8191;
|
|||
|
|
let d1 = c + h0 * r1 + h1 * r0 + h2 * (5 * r9) + h3 * (5 * r8) + h4 * (5 * r7);
|
|||
|
|
c = d1 >>> 13;
|
|||
|
|
d1 &= 8191;
|
|||
|
|
d1 += h5 * (5 * r6) + h6 * (5 * r5) + h7 * (5 * r4) + h8 * (5 * r3) + h9 * (5 * r2);
|
|||
|
|
c += d1 >>> 13;
|
|||
|
|
d1 &= 8191;
|
|||
|
|
let d2 = c + h0 * r2 + h1 * r1 + h2 * r0 + h3 * (5 * r9) + h4 * (5 * r8);
|
|||
|
|
c = d2 >>> 13;
|
|||
|
|
d2 &= 8191;
|
|||
|
|
d2 += h5 * (5 * r7) + h6 * (5 * r6) + h7 * (5 * r5) + h8 * (5 * r4) + h9 * (5 * r3);
|
|||
|
|
c += d2 >>> 13;
|
|||
|
|
d2 &= 8191;
|
|||
|
|
let d3 = c + h0 * r3 + h1 * r2 + h2 * r1 + h3 * r0 + h4 * (5 * r9);
|
|||
|
|
c = d3 >>> 13;
|
|||
|
|
d3 &= 8191;
|
|||
|
|
d3 += h5 * (5 * r8) + h6 * (5 * r7) + h7 * (5 * r6) + h8 * (5 * r5) + h9 * (5 * r4);
|
|||
|
|
c += d3 >>> 13;
|
|||
|
|
d3 &= 8191;
|
|||
|
|
let d4 = c + h0 * r4 + h1 * r3 + h2 * r2 + h3 * r1 + h4 * r0;
|
|||
|
|
c = d4 >>> 13;
|
|||
|
|
d4 &= 8191;
|
|||
|
|
d4 += h5 * (5 * r9) + h6 * (5 * r8) + h7 * (5 * r7) + h8 * (5 * r6) + h9 * (5 * r5);
|
|||
|
|
c += d4 >>> 13;
|
|||
|
|
d4 &= 8191;
|
|||
|
|
let d5 = c + h0 * r5 + h1 * r4 + h2 * r3 + h3 * r2 + h4 * r1;
|
|||
|
|
c = d5 >>> 13;
|
|||
|
|
d5 &= 8191;
|
|||
|
|
d5 += h5 * r0 + h6 * (5 * r9) + h7 * (5 * r8) + h8 * (5 * r7) + h9 * (5 * r6);
|
|||
|
|
c += d5 >>> 13;
|
|||
|
|
d5 &= 8191;
|
|||
|
|
let d6 = c + h0 * r6 + h1 * r5 + h2 * r4 + h3 * r3 + h4 * r2;
|
|||
|
|
c = d6 >>> 13;
|
|||
|
|
d6 &= 8191;
|
|||
|
|
d6 += h5 * r1 + h6 * r0 + h7 * (5 * r9) + h8 * (5 * r8) + h9 * (5 * r7);
|
|||
|
|
c += d6 >>> 13;
|
|||
|
|
d6 &= 8191;
|
|||
|
|
let d7 = c + h0 * r7 + h1 * r6 + h2 * r5 + h3 * r4 + h4 * r3;
|
|||
|
|
c = d7 >>> 13;
|
|||
|
|
d7 &= 8191;
|
|||
|
|
d7 += h5 * r2 + h6 * r1 + h7 * r0 + h8 * (5 * r9) + h9 * (5 * r8);
|
|||
|
|
c += d7 >>> 13;
|
|||
|
|
d7 &= 8191;
|
|||
|
|
let d8 = c + h0 * r8 + h1 * r7 + h2 * r6 + h3 * r5 + h4 * r4;
|
|||
|
|
c = d8 >>> 13;
|
|||
|
|
d8 &= 8191;
|
|||
|
|
d8 += h5 * r3 + h6 * r2 + h7 * r1 + h8 * r0 + h9 * (5 * r9);
|
|||
|
|
c += d8 >>> 13;
|
|||
|
|
d8 &= 8191;
|
|||
|
|
let d9 = c + h0 * r9 + h1 * r8 + h2 * r7 + h3 * r6 + h4 * r5;
|
|||
|
|
c = d9 >>> 13;
|
|||
|
|
d9 &= 8191;
|
|||
|
|
d9 += h5 * r4 + h6 * r3 + h7 * r2 + h8 * r1 + h9 * r0;
|
|||
|
|
c += d9 >>> 13;
|
|||
|
|
d9 &= 8191;
|
|||
|
|
c = (c << 2) + c | 0;
|
|||
|
|
c = c + d0 | 0;
|
|||
|
|
d0 = c & 8191;
|
|||
|
|
c = c >>> 13;
|
|||
|
|
d1 += c;
|
|||
|
|
h[0] = d0;
|
|||
|
|
h[1] = d1;
|
|||
|
|
h[2] = d2;
|
|||
|
|
h[3] = d3;
|
|||
|
|
h[4] = d4;
|
|||
|
|
h[5] = d5;
|
|||
|
|
h[6] = d6;
|
|||
|
|
h[7] = d7;
|
|||
|
|
h[8] = d8;
|
|||
|
|
h[9] = d9;
|
|||
|
|
}
|
|||
|
|
finalize() {
|
|||
|
|
const { h, pad: pad2 } = this;
|
|||
|
|
const g = new Uint16Array(10);
|
|||
|
|
let c = h[1] >>> 13;
|
|||
|
|
h[1] &= 8191;
|
|||
|
|
for (let i3 = 2; i3 < 10; i3++) {
|
|||
|
|
h[i3] += c;
|
|||
|
|
c = h[i3] >>> 13;
|
|||
|
|
h[i3] &= 8191;
|
|||
|
|
}
|
|||
|
|
h[0] += c * 5;
|
|||
|
|
c = h[0] >>> 13;
|
|||
|
|
h[0] &= 8191;
|
|||
|
|
h[1] += c;
|
|||
|
|
c = h[1] >>> 13;
|
|||
|
|
h[1] &= 8191;
|
|||
|
|
h[2] += c;
|
|||
|
|
g[0] = h[0] + 5;
|
|||
|
|
c = g[0] >>> 13;
|
|||
|
|
g[0] &= 8191;
|
|||
|
|
for (let i3 = 1; i3 < 10; i3++) {
|
|||
|
|
g[i3] = h[i3] + c;
|
|||
|
|
c = g[i3] >>> 13;
|
|||
|
|
g[i3] &= 8191;
|
|||
|
|
}
|
|||
|
|
g[9] -= 1 << 13;
|
|||
|
|
let mask = (c ^ 1) - 1;
|
|||
|
|
for (let i3 = 0; i3 < 10; i3++)
|
|||
|
|
g[i3] &= mask;
|
|||
|
|
mask = ~mask;
|
|||
|
|
for (let i3 = 0; i3 < 10; i3++)
|
|||
|
|
h[i3] = h[i3] & mask | g[i3];
|
|||
|
|
h[0] = (h[0] | h[1] << 13) & 65535;
|
|||
|
|
h[1] = (h[1] >>> 3 | h[2] << 10) & 65535;
|
|||
|
|
h[2] = (h[2] >>> 6 | h[3] << 7) & 65535;
|
|||
|
|
h[3] = (h[3] >>> 9 | h[4] << 4) & 65535;
|
|||
|
|
h[4] = (h[4] >>> 12 | h[5] << 1 | h[6] << 14) & 65535;
|
|||
|
|
h[5] = (h[6] >>> 2 | h[7] << 11) & 65535;
|
|||
|
|
h[6] = (h[7] >>> 5 | h[8] << 8) & 65535;
|
|||
|
|
h[7] = (h[8] >>> 8 | h[9] << 5) & 65535;
|
|||
|
|
let f = h[0] + pad2[0];
|
|||
|
|
h[0] = f & 65535;
|
|||
|
|
for (let i3 = 1; i3 < 8; i3++) {
|
|||
|
|
f = (h[i3] + pad2[i3] | 0) + (f >>> 16) | 0;
|
|||
|
|
h[i3] = f & 65535;
|
|||
|
|
}
|
|||
|
|
clean2(g);
|
|||
|
|
}
|
|||
|
|
update(data) {
|
|||
|
|
aexists2(this);
|
|||
|
|
abytes2(data);
|
|||
|
|
data = copyBytes2(data);
|
|||
|
|
const { buffer, blockLen } = this;
|
|||
|
|
const len = data.length;
|
|||
|
|
for (let pos = 0; pos < len; ) {
|
|||
|
|
const take = Math.min(blockLen - this.pos, len - pos);
|
|||
|
|
if (take === blockLen) {
|
|||
|
|
for (; blockLen <= len - pos; pos += blockLen)
|
|||
|
|
this.process(data, pos);
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
buffer.set(data.subarray(pos, pos + take), this.pos);
|
|||
|
|
this.pos += take;
|
|||
|
|
pos += take;
|
|||
|
|
if (this.pos === blockLen) {
|
|||
|
|
this.process(buffer, 0, false);
|
|||
|
|
this.pos = 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return this;
|
|||
|
|
}
|
|||
|
|
destroy() {
|
|||
|
|
clean2(this.h, this.r, this.buffer, this.pad);
|
|||
|
|
}
|
|||
|
|
digestInto(out) {
|
|||
|
|
aexists2(this);
|
|||
|
|
aoutput2(out, this);
|
|||
|
|
this.finished = true;
|
|||
|
|
const { buffer, h } = this;
|
|||
|
|
let { pos } = this;
|
|||
|
|
if (pos) {
|
|||
|
|
buffer[pos++] = 1;
|
|||
|
|
for (; pos < 16; pos++)
|
|||
|
|
buffer[pos] = 0;
|
|||
|
|
this.process(buffer, 0, true);
|
|||
|
|
}
|
|||
|
|
this.finalize();
|
|||
|
|
let opos = 0;
|
|||
|
|
for (let i3 = 0; i3 < 8; i3++) {
|
|||
|
|
out[opos++] = h[i3] >>> 0;
|
|||
|
|
out[opos++] = h[i3] >>> 8;
|
|||
|
|
}
|
|||
|
|
return out;
|
|||
|
|
}
|
|||
|
|
digest() {
|
|||
|
|
const { buffer, outputLen } = this;
|
|||
|
|
this.digestInto(buffer);
|
|||
|
|
const res = buffer.slice(0, outputLen);
|
|||
|
|
this.destroy();
|
|||
|
|
return res;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
poly1305 = /* @__PURE__ */ (() => wrapConstructorWithKey((key) => new Poly1305(key)))();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@noble/ciphers/chacha.js
|
|||
|
|
function chachaCore(s, k, n, out, cnt, rounds = 20) {
|
|||
|
|
let y00 = s[0], y01 = s[1], y02 = s[2], y03 = s[3], y04 = k[0], y05 = k[1], y06 = k[2], y07 = k[3], y08 = k[4], y09 = k[5], y10 = k[6], y11 = k[7], y12 = cnt, y13 = n[0], y14 = n[1], y15 = n[2];
|
|||
|
|
let x00 = y00, x01 = y01, x02 = y02, x03 = y03, x04 = y04, x05 = y05, x06 = y06, x07 = y07, x08 = y08, x09 = y09, x10 = y10, x11 = y11, x12 = y12, x13 = y13, x14 = y14, x15 = y15;
|
|||
|
|
for (let r = 0; r < rounds; r += 2) {
|
|||
|
|
x00 = x00 + x04 | 0;
|
|||
|
|
x12 = rotl(x12 ^ x00, 16);
|
|||
|
|
x08 = x08 + x12 | 0;
|
|||
|
|
x04 = rotl(x04 ^ x08, 12);
|
|||
|
|
x00 = x00 + x04 | 0;
|
|||
|
|
x12 = rotl(x12 ^ x00, 8);
|
|||
|
|
x08 = x08 + x12 | 0;
|
|||
|
|
x04 = rotl(x04 ^ x08, 7);
|
|||
|
|
x01 = x01 + x05 | 0;
|
|||
|
|
x13 = rotl(x13 ^ x01, 16);
|
|||
|
|
x09 = x09 + x13 | 0;
|
|||
|
|
x05 = rotl(x05 ^ x09, 12);
|
|||
|
|
x01 = x01 + x05 | 0;
|
|||
|
|
x13 = rotl(x13 ^ x01, 8);
|
|||
|
|
x09 = x09 + x13 | 0;
|
|||
|
|
x05 = rotl(x05 ^ x09, 7);
|
|||
|
|
x02 = x02 + x06 | 0;
|
|||
|
|
x14 = rotl(x14 ^ x02, 16);
|
|||
|
|
x10 = x10 + x14 | 0;
|
|||
|
|
x06 = rotl(x06 ^ x10, 12);
|
|||
|
|
x02 = x02 + x06 | 0;
|
|||
|
|
x14 = rotl(x14 ^ x02, 8);
|
|||
|
|
x10 = x10 + x14 | 0;
|
|||
|
|
x06 = rotl(x06 ^ x10, 7);
|
|||
|
|
x03 = x03 + x07 | 0;
|
|||
|
|
x15 = rotl(x15 ^ x03, 16);
|
|||
|
|
x11 = x11 + x15 | 0;
|
|||
|
|
x07 = rotl(x07 ^ x11, 12);
|
|||
|
|
x03 = x03 + x07 | 0;
|
|||
|
|
x15 = rotl(x15 ^ x03, 8);
|
|||
|
|
x11 = x11 + x15 | 0;
|
|||
|
|
x07 = rotl(x07 ^ x11, 7);
|
|||
|
|
x00 = x00 + x05 | 0;
|
|||
|
|
x15 = rotl(x15 ^ x00, 16);
|
|||
|
|
x10 = x10 + x15 | 0;
|
|||
|
|
x05 = rotl(x05 ^ x10, 12);
|
|||
|
|
x00 = x00 + x05 | 0;
|
|||
|
|
x15 = rotl(x15 ^ x00, 8);
|
|||
|
|
x10 = x10 + x15 | 0;
|
|||
|
|
x05 = rotl(x05 ^ x10, 7);
|
|||
|
|
x01 = x01 + x06 | 0;
|
|||
|
|
x12 = rotl(x12 ^ x01, 16);
|
|||
|
|
x11 = x11 + x12 | 0;
|
|||
|
|
x06 = rotl(x06 ^ x11, 12);
|
|||
|
|
x01 = x01 + x06 | 0;
|
|||
|
|
x12 = rotl(x12 ^ x01, 8);
|
|||
|
|
x11 = x11 + x12 | 0;
|
|||
|
|
x06 = rotl(x06 ^ x11, 7);
|
|||
|
|
x02 = x02 + x07 | 0;
|
|||
|
|
x13 = rotl(x13 ^ x02, 16);
|
|||
|
|
x08 = x08 + x13 | 0;
|
|||
|
|
x07 = rotl(x07 ^ x08, 12);
|
|||
|
|
x02 = x02 + x07 | 0;
|
|||
|
|
x13 = rotl(x13 ^ x02, 8);
|
|||
|
|
x08 = x08 + x13 | 0;
|
|||
|
|
x07 = rotl(x07 ^ x08, 7);
|
|||
|
|
x03 = x03 + x04 | 0;
|
|||
|
|
x14 = rotl(x14 ^ x03, 16);
|
|||
|
|
x09 = x09 + x14 | 0;
|
|||
|
|
x04 = rotl(x04 ^ x09, 12);
|
|||
|
|
x03 = x03 + x04 | 0;
|
|||
|
|
x14 = rotl(x14 ^ x03, 8);
|
|||
|
|
x09 = x09 + x14 | 0;
|
|||
|
|
x04 = rotl(x04 ^ x09, 7);
|
|||
|
|
}
|
|||
|
|
let oi = 0;
|
|||
|
|
out[oi++] = y00 + x00 | 0;
|
|||
|
|
out[oi++] = y01 + x01 | 0;
|
|||
|
|
out[oi++] = y02 + x02 | 0;
|
|||
|
|
out[oi++] = y03 + x03 | 0;
|
|||
|
|
out[oi++] = y04 + x04 | 0;
|
|||
|
|
out[oi++] = y05 + x05 | 0;
|
|||
|
|
out[oi++] = y06 + x06 | 0;
|
|||
|
|
out[oi++] = y07 + x07 | 0;
|
|||
|
|
out[oi++] = y08 + x08 | 0;
|
|||
|
|
out[oi++] = y09 + x09 | 0;
|
|||
|
|
out[oi++] = y10 + x10 | 0;
|
|||
|
|
out[oi++] = y11 + x11 | 0;
|
|||
|
|
out[oi++] = y12 + x12 | 0;
|
|||
|
|
out[oi++] = y13 + x13 | 0;
|
|||
|
|
out[oi++] = y14 + x14 | 0;
|
|||
|
|
out[oi++] = y15 + x15 | 0;
|
|||
|
|
}
|
|||
|
|
function hchacha(s, k, i3, out) {
|
|||
|
|
let x00 = s[0], x01 = s[1], x02 = s[2], x03 = s[3], x04 = k[0], x05 = k[1], x06 = k[2], x07 = k[3], x08 = k[4], x09 = k[5], x10 = k[6], x11 = k[7], x12 = i3[0], x13 = i3[1], x14 = i3[2], x15 = i3[3];
|
|||
|
|
for (let r = 0; r < 20; r += 2) {
|
|||
|
|
x00 = x00 + x04 | 0;
|
|||
|
|
x12 = rotl(x12 ^ x00, 16);
|
|||
|
|
x08 = x08 + x12 | 0;
|
|||
|
|
x04 = rotl(x04 ^ x08, 12);
|
|||
|
|
x00 = x00 + x04 | 0;
|
|||
|
|
x12 = rotl(x12 ^ x00, 8);
|
|||
|
|
x08 = x08 + x12 | 0;
|
|||
|
|
x04 = rotl(x04 ^ x08, 7);
|
|||
|
|
x01 = x01 + x05 | 0;
|
|||
|
|
x13 = rotl(x13 ^ x01, 16);
|
|||
|
|
x09 = x09 + x13 | 0;
|
|||
|
|
x05 = rotl(x05 ^ x09, 12);
|
|||
|
|
x01 = x01 + x05 | 0;
|
|||
|
|
x13 = rotl(x13 ^ x01, 8);
|
|||
|
|
x09 = x09 + x13 | 0;
|
|||
|
|
x05 = rotl(x05 ^ x09, 7);
|
|||
|
|
x02 = x02 + x06 | 0;
|
|||
|
|
x14 = rotl(x14 ^ x02, 16);
|
|||
|
|
x10 = x10 + x14 | 0;
|
|||
|
|
x06 = rotl(x06 ^ x10, 12);
|
|||
|
|
x02 = x02 + x06 | 0;
|
|||
|
|
x14 = rotl(x14 ^ x02, 8);
|
|||
|
|
x10 = x10 + x14 | 0;
|
|||
|
|
x06 = rotl(x06 ^ x10, 7);
|
|||
|
|
x03 = x03 + x07 | 0;
|
|||
|
|
x15 = rotl(x15 ^ x03, 16);
|
|||
|
|
x11 = x11 + x15 | 0;
|
|||
|
|
x07 = rotl(x07 ^ x11, 12);
|
|||
|
|
x03 = x03 + x07 | 0;
|
|||
|
|
x15 = rotl(x15 ^ x03, 8);
|
|||
|
|
x11 = x11 + x15 | 0;
|
|||
|
|
x07 = rotl(x07 ^ x11, 7);
|
|||
|
|
x00 = x00 + x05 | 0;
|
|||
|
|
x15 = rotl(x15 ^ x00, 16);
|
|||
|
|
x10 = x10 + x15 | 0;
|
|||
|
|
x05 = rotl(x05 ^ x10, 12);
|
|||
|
|
x00 = x00 + x05 | 0;
|
|||
|
|
x15 = rotl(x15 ^ x00, 8);
|
|||
|
|
x10 = x10 + x15 | 0;
|
|||
|
|
x05 = rotl(x05 ^ x10, 7);
|
|||
|
|
x01 = x01 + x06 | 0;
|
|||
|
|
x12 = rotl(x12 ^ x01, 16);
|
|||
|
|
x11 = x11 + x12 | 0;
|
|||
|
|
x06 = rotl(x06 ^ x11, 12);
|
|||
|
|
x01 = x01 + x06 | 0;
|
|||
|
|
x12 = rotl(x12 ^ x01, 8);
|
|||
|
|
x11 = x11 + x12 | 0;
|
|||
|
|
x06 = rotl(x06 ^ x11, 7);
|
|||
|
|
x02 = x02 + x07 | 0;
|
|||
|
|
x13 = rotl(x13 ^ x02, 16);
|
|||
|
|
x08 = x08 + x13 | 0;
|
|||
|
|
x07 = rotl(x07 ^ x08, 12);
|
|||
|
|
x02 = x02 + x07 | 0;
|
|||
|
|
x13 = rotl(x13 ^ x02, 8);
|
|||
|
|
x08 = x08 + x13 | 0;
|
|||
|
|
x07 = rotl(x07 ^ x08, 7);
|
|||
|
|
x03 = x03 + x04 | 0;
|
|||
|
|
x14 = rotl(x14 ^ x03, 16);
|
|||
|
|
x09 = x09 + x14 | 0;
|
|||
|
|
x04 = rotl(x04 ^ x09, 12);
|
|||
|
|
x03 = x03 + x04 | 0;
|
|||
|
|
x14 = rotl(x14 ^ x03, 8);
|
|||
|
|
x09 = x09 + x14 | 0;
|
|||
|
|
x04 = rotl(x04 ^ x09, 7);
|
|||
|
|
}
|
|||
|
|
let oi = 0;
|
|||
|
|
out[oi++] = x00;
|
|||
|
|
out[oi++] = x01;
|
|||
|
|
out[oi++] = x02;
|
|||
|
|
out[oi++] = x03;
|
|||
|
|
out[oi++] = x12;
|
|||
|
|
out[oi++] = x13;
|
|||
|
|
out[oi++] = x14;
|
|||
|
|
out[oi++] = x15;
|
|||
|
|
}
|
|||
|
|
function computeTag(fn, key, nonce, ciphertext, AAD) {
|
|||
|
|
if (AAD !== void 0)
|
|||
|
|
abytes2(AAD, void 0, "AAD");
|
|||
|
|
const authKey = fn(key, nonce, ZEROS32);
|
|||
|
|
const lengths = u64Lengths(ciphertext.length, AAD ? AAD.length : 0, true);
|
|||
|
|
const h = poly1305.create(authKey);
|
|||
|
|
if (AAD)
|
|||
|
|
updatePadded(h, AAD);
|
|||
|
|
updatePadded(h, ciphertext);
|
|||
|
|
h.update(lengths);
|
|||
|
|
const res = h.digest();
|
|||
|
|
clean2(authKey, lengths);
|
|||
|
|
return res;
|
|||
|
|
}
|
|||
|
|
var chacha20, xchacha20, ZEROS16, updatePadded, ZEROS32, _poly1305_aead, chacha20poly1305, xchacha20poly1305;
|
|||
|
|
var init_chacha = __esm({
|
|||
|
|
"node_modules/@noble/ciphers/chacha.js"() {
|
|||
|
|
init_arx();
|
|||
|
|
init_poly1305();
|
|||
|
|
init_utils3();
|
|||
|
|
chacha20 = /* @__PURE__ */ createCipher(chachaCore, {
|
|||
|
|
counterRight: false,
|
|||
|
|
counterLength: 4,
|
|||
|
|
allowShortKeys: false
|
|||
|
|
});
|
|||
|
|
xchacha20 = /* @__PURE__ */ createCipher(chachaCore, {
|
|||
|
|
counterRight: false,
|
|||
|
|
counterLength: 8,
|
|||
|
|
extendNonceFn: hchacha,
|
|||
|
|
allowShortKeys: false
|
|||
|
|
});
|
|||
|
|
ZEROS16 = /* @__PURE__ */ new Uint8Array(16);
|
|||
|
|
updatePadded = (h, msg) => {
|
|||
|
|
h.update(msg);
|
|||
|
|
const leftover = msg.length % 16;
|
|||
|
|
if (leftover)
|
|||
|
|
h.update(ZEROS16.subarray(leftover));
|
|||
|
|
};
|
|||
|
|
ZEROS32 = /* @__PURE__ */ new Uint8Array(32);
|
|||
|
|
_poly1305_aead = (xorStream) => (key, nonce, AAD) => {
|
|||
|
|
const tagLength = 16;
|
|||
|
|
return {
|
|||
|
|
encrypt(plaintext, output) {
|
|||
|
|
const plength = plaintext.length;
|
|||
|
|
output = getOutput(plength + tagLength, output, false);
|
|||
|
|
output.set(plaintext);
|
|||
|
|
const oPlain = output.subarray(0, -tagLength);
|
|||
|
|
xorStream(key, nonce, oPlain, oPlain, 1);
|
|||
|
|
const tag = computeTag(xorStream, key, nonce, oPlain, AAD);
|
|||
|
|
output.set(tag, plength);
|
|||
|
|
clean2(tag);
|
|||
|
|
return output;
|
|||
|
|
},
|
|||
|
|
decrypt(ciphertext, output) {
|
|||
|
|
output = getOutput(ciphertext.length - tagLength, output, false);
|
|||
|
|
const data = ciphertext.subarray(0, -tagLength);
|
|||
|
|
const passedTag = ciphertext.subarray(-tagLength);
|
|||
|
|
const tag = computeTag(xorStream, key, nonce, data, AAD);
|
|||
|
|
if (!equalBytes(passedTag, tag))
|
|||
|
|
throw new Error("invalid tag");
|
|||
|
|
output.set(ciphertext.subarray(0, -tagLength));
|
|||
|
|
xorStream(key, nonce, output, output, 1);
|
|||
|
|
clean2(tag);
|
|||
|
|
return output;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
chacha20poly1305 = /* @__PURE__ */ wrapCipher({ blockSize: 64, nonceLength: 12, tagLength: 16 }, _poly1305_aead(chacha20));
|
|||
|
|
xchacha20poly1305 = /* @__PURE__ */ wrapCipher({ blockSize: 64, nonceLength: 24, tagLength: 16 }, _poly1305_aead(xchacha20));
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@noble/hashes/hkdf.js
|
|||
|
|
function extract(hash, ikm, salt) {
|
|||
|
|
ahash(hash);
|
|||
|
|
if (salt === void 0)
|
|||
|
|
salt = new Uint8Array(hash.outputLen);
|
|||
|
|
return hmac(hash, salt, ikm);
|
|||
|
|
}
|
|||
|
|
function expand(hash, prk, info, length = 32) {
|
|||
|
|
ahash(hash);
|
|||
|
|
anumber(length, "length");
|
|||
|
|
const olen = hash.outputLen;
|
|||
|
|
if (length > 255 * olen)
|
|||
|
|
throw new Error("Length must be <= 255*HashLen");
|
|||
|
|
const blocks = Math.ceil(length / olen);
|
|||
|
|
if (info === void 0)
|
|||
|
|
info = EMPTY_BUFFER;
|
|||
|
|
else
|
|||
|
|
abytes(info, void 0, "info");
|
|||
|
|
const okm = new Uint8Array(blocks * olen);
|
|||
|
|
const HMAC = hmac.create(hash, prk);
|
|||
|
|
const HMACTmp = HMAC._cloneInto();
|
|||
|
|
const T = new Uint8Array(HMAC.outputLen);
|
|||
|
|
for (let counter = 0; counter < blocks; counter++) {
|
|||
|
|
HKDF_COUNTER[0] = counter + 1;
|
|||
|
|
HMACTmp.update(counter === 0 ? EMPTY_BUFFER : T).update(info).update(HKDF_COUNTER).digestInto(T);
|
|||
|
|
okm.set(T, olen * counter);
|
|||
|
|
HMAC._cloneInto(HMACTmp);
|
|||
|
|
}
|
|||
|
|
HMAC.destroy();
|
|||
|
|
HMACTmp.destroy();
|
|||
|
|
clean(T, HKDF_COUNTER);
|
|||
|
|
return okm.slice(0, length);
|
|||
|
|
}
|
|||
|
|
var HKDF_COUNTER, EMPTY_BUFFER;
|
|||
|
|
var init_hkdf = __esm({
|
|||
|
|
"node_modules/@noble/hashes/hkdf.js"() {
|
|||
|
|
init_hmac();
|
|||
|
|
init_utils();
|
|||
|
|
HKDF_COUNTER = /* @__PURE__ */ Uint8Array.of(0);
|
|||
|
|
EMPTY_BUFFER = /* @__PURE__ */ Uint8Array.of();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@scure/base/index.js
|
|||
|
|
function isBytes3(a) {
|
|||
|
|
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
|
|||
|
|
}
|
|||
|
|
function abytes3(b) {
|
|||
|
|
if (!isBytes3(b))
|
|||
|
|
throw new Error("Uint8Array expected");
|
|||
|
|
}
|
|||
|
|
function isArrayOf(isString, arr) {
|
|||
|
|
if (!Array.isArray(arr))
|
|||
|
|
return false;
|
|||
|
|
if (arr.length === 0)
|
|||
|
|
return true;
|
|||
|
|
if (isString) {
|
|||
|
|
return arr.every((item) => typeof item === "string");
|
|||
|
|
} else {
|
|||
|
|
return arr.every((item) => Number.isSafeInteger(item));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function astr(label, input) {
|
|||
|
|
if (typeof input !== "string")
|
|||
|
|
throw new Error(`${label}: string expected`);
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
function anumber3(n) {
|
|||
|
|
if (!Number.isSafeInteger(n))
|
|||
|
|
throw new Error(`invalid integer: ${n}`);
|
|||
|
|
}
|
|||
|
|
function aArr(input) {
|
|||
|
|
if (!Array.isArray(input))
|
|||
|
|
throw new Error("array expected");
|
|||
|
|
}
|
|||
|
|
function astrArr(label, input) {
|
|||
|
|
if (!isArrayOf(true, input))
|
|||
|
|
throw new Error(`${label}: array of strings expected`);
|
|||
|
|
}
|
|||
|
|
function anumArr(label, input) {
|
|||
|
|
if (!isArrayOf(false, input))
|
|||
|
|
throw new Error(`${label}: array of numbers expected`);
|
|||
|
|
}
|
|||
|
|
// @__NO_SIDE_EFFECTS__
|
|||
|
|
function chain(...args) {
|
|||
|
|
const id = (a) => a;
|
|||
|
|
const wrap = (a, b) => (c) => a(b(c));
|
|||
|
|
const encode = args.map((x) => x.encode).reduceRight(wrap, id);
|
|||
|
|
const decode = args.map((x) => x.decode).reduce(wrap, id);
|
|||
|
|
return { encode, decode };
|
|||
|
|
}
|
|||
|
|
// @__NO_SIDE_EFFECTS__
|
|||
|
|
function alphabet(letters) {
|
|||
|
|
const lettersA = typeof letters === "string" ? letters.split("") : letters;
|
|||
|
|
const len = lettersA.length;
|
|||
|
|
astrArr("alphabet", lettersA);
|
|||
|
|
const indexes = new Map(lettersA.map((l, i3) => [l, i3]));
|
|||
|
|
return {
|
|||
|
|
encode: (digits) => {
|
|||
|
|
aArr(digits);
|
|||
|
|
return digits.map((i3) => {
|
|||
|
|
if (!Number.isSafeInteger(i3) || i3 < 0 || i3 >= len)
|
|||
|
|
throw new Error(`alphabet.encode: digit index outside alphabet "${i3}". Allowed: ${letters}`);
|
|||
|
|
return lettersA[i3];
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
decode: (input) => {
|
|||
|
|
aArr(input);
|
|||
|
|
return input.map((letter) => {
|
|||
|
|
astr("alphabet.decode", letter);
|
|||
|
|
const i3 = indexes.get(letter);
|
|||
|
|
if (i3 === void 0)
|
|||
|
|
throw new Error(`Unknown letter: "${letter}". Allowed: ${letters}`);
|
|||
|
|
return i3;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
// @__NO_SIDE_EFFECTS__
|
|||
|
|
function join(separator = "") {
|
|||
|
|
astr("join", separator);
|
|||
|
|
return {
|
|||
|
|
encode: (from) => {
|
|||
|
|
astrArr("join.decode", from);
|
|||
|
|
return from.join(separator);
|
|||
|
|
},
|
|||
|
|
decode: (to) => {
|
|||
|
|
astr("join.decode", to);
|
|||
|
|
return to.split(separator);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
// @__NO_SIDE_EFFECTS__
|
|||
|
|
function padding(bits, chr = "=") {
|
|||
|
|
anumber3(bits);
|
|||
|
|
astr("padding", chr);
|
|||
|
|
return {
|
|||
|
|
encode(data) {
|
|||
|
|
astrArr("padding.encode", data);
|
|||
|
|
while (data.length * bits % 8)
|
|||
|
|
data.push(chr);
|
|||
|
|
return data;
|
|||
|
|
},
|
|||
|
|
decode(input) {
|
|||
|
|
astrArr("padding.decode", input);
|
|||
|
|
let end = input.length;
|
|||
|
|
if (end * bits % 8)
|
|||
|
|
throw new Error("padding: invalid, string should have whole number of bytes");
|
|||
|
|
for (; end > 0 && input[end - 1] === chr; end--) {
|
|||
|
|
const last = end - 1;
|
|||
|
|
const byte = last * bits;
|
|||
|
|
if (byte % 8 === 0)
|
|||
|
|
throw new Error("padding: invalid, string has too much padding");
|
|||
|
|
}
|
|||
|
|
return input.slice(0, end);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
function convertRadix2(data, from, to, padding2) {
|
|||
|
|
aArr(data);
|
|||
|
|
if (from <= 0 || from > 32)
|
|||
|
|
throw new Error(`convertRadix2: wrong from=${from}`);
|
|||
|
|
if (to <= 0 || to > 32)
|
|||
|
|
throw new Error(`convertRadix2: wrong to=${to}`);
|
|||
|
|
if (/* @__PURE__ */ radix2carry(from, to) > 32) {
|
|||
|
|
throw new Error(`convertRadix2: carry overflow from=${from} to=${to} carryBits=${/* @__PURE__ */ radix2carry(from, to)}`);
|
|||
|
|
}
|
|||
|
|
let carry = 0;
|
|||
|
|
let pos = 0;
|
|||
|
|
const max = powers[from];
|
|||
|
|
const mask = powers[to] - 1;
|
|||
|
|
const res = [];
|
|||
|
|
for (const n of data) {
|
|||
|
|
anumber3(n);
|
|||
|
|
if (n >= max)
|
|||
|
|
throw new Error(`convertRadix2: invalid data word=${n} from=${from}`);
|
|||
|
|
carry = carry << from | n;
|
|||
|
|
if (pos + from > 32)
|
|||
|
|
throw new Error(`convertRadix2: carry overflow pos=${pos} from=${from}`);
|
|||
|
|
pos += from;
|
|||
|
|
for (; pos >= to; pos -= to)
|
|||
|
|
res.push((carry >> pos - to & mask) >>> 0);
|
|||
|
|
const pow = powers[pos];
|
|||
|
|
if (pow === void 0)
|
|||
|
|
throw new Error("invalid carry");
|
|||
|
|
carry &= pow - 1;
|
|||
|
|
}
|
|||
|
|
carry = carry << to - pos & mask;
|
|||
|
|
if (!padding2 && pos >= from)
|
|||
|
|
throw new Error("Excess padding");
|
|||
|
|
if (!padding2 && carry > 0)
|
|||
|
|
throw new Error(`Non-zero padding: ${carry}`);
|
|||
|
|
if (padding2 && pos > 0)
|
|||
|
|
res.push(carry >>> 0);
|
|||
|
|
return res;
|
|||
|
|
}
|
|||
|
|
// @__NO_SIDE_EFFECTS__
|
|||
|
|
function radix2(bits, revPadding = false) {
|
|||
|
|
anumber3(bits);
|
|||
|
|
if (bits <= 0 || bits > 32)
|
|||
|
|
throw new Error("radix2: bits should be in (0..32]");
|
|||
|
|
if (/* @__PURE__ */ radix2carry(8, bits) > 32 || /* @__PURE__ */ radix2carry(bits, 8) > 32)
|
|||
|
|
throw new Error("radix2: carry overflow");
|
|||
|
|
return {
|
|||
|
|
encode: (bytes) => {
|
|||
|
|
if (!isBytes3(bytes))
|
|||
|
|
throw new Error("radix2.encode input should be Uint8Array");
|
|||
|
|
return convertRadix2(Array.from(bytes), 8, bits, !revPadding);
|
|||
|
|
},
|
|||
|
|
decode: (digits) => {
|
|||
|
|
anumArr("radix2.decode", digits);
|
|||
|
|
return Uint8Array.from(convertRadix2(digits, bits, 8, revPadding));
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
var gcd, radix2carry, powers, hasBase64Builtin, decodeBase64Builtin, base64;
|
|||
|
|
var init_base = __esm({
|
|||
|
|
"node_modules/@scure/base/index.js"() {
|
|||
|
|
gcd = (a, b) => b === 0 ? a : gcd(b, a % b);
|
|||
|
|
radix2carry = /* @__NO_SIDE_EFFECTS__ */ (from, to) => from + (to - gcd(from, to));
|
|||
|
|
powers = /* @__PURE__ */ (() => {
|
|||
|
|
let res = [];
|
|||
|
|
for (let i3 = 0; i3 < 40; i3++)
|
|||
|
|
res.push(2 ** i3);
|
|||
|
|
return res;
|
|||
|
|
})();
|
|||
|
|
hasBase64Builtin = /* @__PURE__ */ (() => typeof Uint8Array.from([]).toBase64 === "function" && typeof Uint8Array.fromBase64 === "function")();
|
|||
|
|
decodeBase64Builtin = (s, isUrl) => {
|
|||
|
|
astr("base64", s);
|
|||
|
|
const re = isUrl ? /^[A-Za-z0-9=_-]+$/ : /^[A-Za-z0-9=+/]+$/;
|
|||
|
|
const alphabet2 = isUrl ? "base64url" : "base64";
|
|||
|
|
if (s.length > 0 && !re.test(s))
|
|||
|
|
throw new Error("invalid base64");
|
|||
|
|
return Uint8Array.fromBase64(s, { alphabet: alphabet2, lastChunkHandling: "strict" });
|
|||
|
|
};
|
|||
|
|
base64 = hasBase64Builtin ? {
|
|||
|
|
encode(b) {
|
|||
|
|
abytes3(b);
|
|||
|
|
return b.toBase64();
|
|||
|
|
},
|
|||
|
|
decode(s) {
|
|||
|
|
return decodeBase64Builtin(s, false);
|
|||
|
|
}
|
|||
|
|
} : /* @__PURE__ */ chain(/* @__PURE__ */ radix2(6), /* @__PURE__ */ alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"), /* @__PURE__ */ padding(6), /* @__PURE__ */ join(""));
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/nostr-tools/lib/esm/nip59.js
|
|||
|
|
function isHex322(input) {
|
|||
|
|
for (let i22 = 0; i22 < 64; i22++) {
|
|||
|
|
let cc = input.charCodeAt(i22);
|
|||
|
|
if (isNaN(cc) || cc < 48 || cc > 102 || cc > 57 && cc < 97) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
function getConversationKey(privkeyA, pubkeyB) {
|
|||
|
|
const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes("02" + pubkeyB)).subarray(1, 33);
|
|||
|
|
return extract(sha256, sharedX, utf8Encoder3.encode("nip44-v2"));
|
|||
|
|
}
|
|||
|
|
function getMessageKeys(conversationKey, nonce) {
|
|||
|
|
const keys = expand(sha256, conversationKey, nonce, 76);
|
|||
|
|
return {
|
|||
|
|
chacha_key: keys.subarray(0, 32),
|
|||
|
|
chacha_nonce: keys.subarray(32, 44),
|
|||
|
|
hmac_key: keys.subarray(44, 76)
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
function calcPaddedLen(len) {
|
|||
|
|
if (!Number.isSafeInteger(len) || len < 1)
|
|||
|
|
throw new Error("expected positive integer");
|
|||
|
|
if (len <= 32)
|
|||
|
|
return 32;
|
|||
|
|
const nextPower = 2 ** (Math.floor(Math.log2(len - 1)) + 1);
|
|||
|
|
const chunk = nextPower <= 256 ? 32 : nextPower / 8;
|
|||
|
|
return chunk * (Math.floor((len - 1) / chunk) + 1);
|
|||
|
|
}
|
|||
|
|
function writeU16BE(num2) {
|
|||
|
|
if (!Number.isSafeInteger(num2) || num2 < minPlaintextSize || num2 > 65535)
|
|||
|
|
throw new Error("invalid plaintext size: must be between 1 and 65535 bytes");
|
|||
|
|
const arr = new Uint8Array(2);
|
|||
|
|
new DataView(arr.buffer).setUint16(0, num2, false);
|
|||
|
|
return arr;
|
|||
|
|
}
|
|||
|
|
function writeU32BE(num2) {
|
|||
|
|
if (!Number.isSafeInteger(num2) || num2 < extendedPrefixThreshold || num2 > maxPlaintextSize)
|
|||
|
|
throw new Error("invalid plaintext size: must be between 65536 and 4294967295 bytes");
|
|||
|
|
const arr = new Uint8Array(4);
|
|||
|
|
new DataView(arr.buffer).setUint32(0, num2, false);
|
|||
|
|
return arr;
|
|||
|
|
}
|
|||
|
|
function pad(plaintext) {
|
|||
|
|
const unpadded = utf8Encoder3.encode(plaintext);
|
|||
|
|
const unpaddedLen = unpadded.length;
|
|||
|
|
if (unpaddedLen < minPlaintextSize || unpaddedLen > maxPlaintextSize)
|
|||
|
|
throw new Error("invalid plaintext size: must be between 1 and 4294967295 bytes");
|
|||
|
|
const prefix = unpaddedLen >= extendedPrefixThreshold ? concatBytes(new Uint8Array([0, 0]), writeU32BE(unpaddedLen)) : writeU16BE(unpaddedLen);
|
|||
|
|
const suffix = new Uint8Array(calcPaddedLen(unpaddedLen) - unpaddedLen);
|
|||
|
|
return concatBytes(prefix, unpadded, suffix);
|
|||
|
|
}
|
|||
|
|
function unpad(padded) {
|
|||
|
|
const dv = new DataView(padded.buffer, padded.byteOffset, padded.byteLength);
|
|||
|
|
const firstTwo = dv.getUint16(0);
|
|||
|
|
let unpaddedLen;
|
|||
|
|
let prefixLen;
|
|||
|
|
if (firstTwo === 0) {
|
|||
|
|
unpaddedLen = dv.getUint32(2);
|
|||
|
|
if (unpaddedLen < extendedPrefixThreshold)
|
|||
|
|
throw new Error("invalid padding");
|
|||
|
|
prefixLen = 6;
|
|||
|
|
} else {
|
|||
|
|
unpaddedLen = firstTwo;
|
|||
|
|
prefixLen = 2;
|
|||
|
|
}
|
|||
|
|
const unpadded = padded.subarray(prefixLen, prefixLen + unpaddedLen);
|
|||
|
|
if (unpaddedLen < minPlaintextSize || unpaddedLen > maxPlaintextSize || unpadded.length !== unpaddedLen || padded.length !== prefixLen + calcPaddedLen(unpaddedLen))
|
|||
|
|
throw new Error("invalid padding");
|
|||
|
|
return utf8Decoder2.decode(unpadded);
|
|||
|
|
}
|
|||
|
|
function hmacAad(key, message, aad) {
|
|||
|
|
if (aad.length !== 32)
|
|||
|
|
throw new Error("AAD associated data must be 32 bytes");
|
|||
|
|
const combined = concatBytes(aad, message);
|
|||
|
|
return hmac(sha256, key, combined);
|
|||
|
|
}
|
|||
|
|
function decodePayload(payload) {
|
|||
|
|
if (typeof payload !== "string")
|
|||
|
|
throw new Error("payload must be a valid string");
|
|||
|
|
const plen = payload.length;
|
|||
|
|
if (plen < 132)
|
|||
|
|
throw new Error("invalid payload length: " + plen);
|
|||
|
|
if (payload[0] === "#")
|
|||
|
|
throw new Error("unknown encryption version");
|
|||
|
|
let data;
|
|||
|
|
try {
|
|||
|
|
data = base64.decode(payload);
|
|||
|
|
} catch (error2) {
|
|||
|
|
throw new Error("invalid base64: " + error2.message);
|
|||
|
|
}
|
|||
|
|
const dlen = data.length;
|
|||
|
|
if (dlen < 99)
|
|||
|
|
throw new Error("invalid data length: " + dlen);
|
|||
|
|
const vers = data[0];
|
|||
|
|
if (vers !== 2)
|
|||
|
|
throw new Error("unknown encryption version " + vers);
|
|||
|
|
return {
|
|||
|
|
nonce: data.subarray(1, 33),
|
|||
|
|
ciphertext: data.subarray(33, -32),
|
|||
|
|
mac: data.subarray(-32)
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
function encrypt(plaintext, conversationKey, nonce = randomBytes(32)) {
|
|||
|
|
const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);
|
|||
|
|
const padded = pad(plaintext);
|
|||
|
|
const ciphertext = chacha20(chacha_key, chacha_nonce, padded);
|
|||
|
|
const mac = hmacAad(hmac_key, ciphertext, nonce);
|
|||
|
|
return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac));
|
|||
|
|
}
|
|||
|
|
function decrypt(payload, conversationKey) {
|
|||
|
|
const { nonce, ciphertext, mac } = decodePayload(payload);
|
|||
|
|
const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);
|
|||
|
|
const calculatedMac = hmacAad(hmac_key, ciphertext, nonce);
|
|||
|
|
if (!equalBytes(calculatedMac, mac))
|
|||
|
|
throw new Error("invalid MAC");
|
|||
|
|
const padded = chacha20(chacha_key, chacha_nonce, ciphertext);
|
|||
|
|
return unpad(padded);
|
|||
|
|
}
|
|||
|
|
function validateEvent2(event) {
|
|||
|
|
if (!isRecord2(event))
|
|||
|
|
return false;
|
|||
|
|
if (typeof event.kind !== "number")
|
|||
|
|
return false;
|
|||
|
|
if (typeof event.content !== "string")
|
|||
|
|
return false;
|
|||
|
|
if (typeof event.created_at !== "number")
|
|||
|
|
return false;
|
|||
|
|
if (typeof event.pubkey !== "string")
|
|||
|
|
return false;
|
|||
|
|
if (!isHex322(event.pubkey))
|
|||
|
|
return false;
|
|||
|
|
if (!Array.isArray(event.tags))
|
|||
|
|
return false;
|
|||
|
|
for (let i22 = 0; i22 < event.tags.length; i22++) {
|
|||
|
|
let tag = event.tags[i22];
|
|||
|
|
if (!Array.isArray(tag))
|
|||
|
|
return false;
|
|||
|
|
for (let j = 0; j < tag.length; j++) {
|
|||
|
|
if (typeof tag[j] !== "string")
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
function serializeEvent2(evt) {
|
|||
|
|
if (!validateEvent2(evt))
|
|||
|
|
throw new Error("can't serialize event with wrong or missing properties");
|
|||
|
|
return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]);
|
|||
|
|
}
|
|||
|
|
function getEventHash2(event) {
|
|||
|
|
let eventHash = sha256(utf8Encoder3.encode(serializeEvent2(event)));
|
|||
|
|
return bytesToHex(eventHash);
|
|||
|
|
}
|
|||
|
|
function createRumor(event, privateKey) {
|
|||
|
|
const rumor = {
|
|||
|
|
created_at: now(),
|
|||
|
|
content: "",
|
|||
|
|
tags: [],
|
|||
|
|
...event,
|
|||
|
|
pubkey: getPublicKey2(privateKey)
|
|||
|
|
};
|
|||
|
|
rumor.id = getEventHash2(rumor);
|
|||
|
|
return rumor;
|
|||
|
|
}
|
|||
|
|
function createSeal(rumor, privateKey, recipientPublicKey) {
|
|||
|
|
return finalizeEvent2(
|
|||
|
|
{
|
|||
|
|
kind: Seal,
|
|||
|
|
content: nip44Encrypt(rumor, privateKey, recipientPublicKey),
|
|||
|
|
created_at: randomNow(),
|
|||
|
|
tags: []
|
|||
|
|
},
|
|||
|
|
privateKey
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
function createWrap(seal, recipientPublicKey) {
|
|||
|
|
const randomKey = generateSecretKey2();
|
|||
|
|
return finalizeEvent2(
|
|||
|
|
{
|
|||
|
|
kind: GiftWrap,
|
|||
|
|
content: nip44Encrypt(seal, randomKey, recipientPublicKey),
|
|||
|
|
created_at: randomNow(),
|
|||
|
|
tags: [["p", recipientPublicKey]]
|
|||
|
|
},
|
|||
|
|
randomKey
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
function wrapEvent(event, senderPrivateKey, recipientPublicKey) {
|
|||
|
|
const rumor = createRumor(event, senderPrivateKey);
|
|||
|
|
const seal = createSeal(rumor, senderPrivateKey, recipientPublicKey);
|
|||
|
|
return createWrap(seal, recipientPublicKey);
|
|||
|
|
}
|
|||
|
|
function unwrapEvent(wrap, recipientPrivateKey) {
|
|||
|
|
const unwrappedSeal = nip44Decrypt(wrap, recipientPrivateKey);
|
|||
|
|
return nip44Decrypt(unwrappedSeal, recipientPrivateKey);
|
|||
|
|
}
|
|||
|
|
var utf8Decoder2, utf8Encoder3, minPlaintextSize, maxPlaintextSize, extendedPrefixThreshold, verifiedSymbol2, isRecord2, JS2, i2, generateSecretKey2, getPublicKey2, finalizeEvent2, verifyEvent2, Seal, GiftWrap, TWO_DAYS, now, randomNow, nip44ConversationKey, nip44Encrypt, nip44Decrypt;
|
|||
|
|
var init_nip59 = __esm({
|
|||
|
|
"node_modules/nostr-tools/lib/esm/nip59.js"() {
|
|||
|
|
init_chacha();
|
|||
|
|
init_utils3();
|
|||
|
|
init_secp256k1();
|
|||
|
|
init_hkdf();
|
|||
|
|
init_hmac();
|
|||
|
|
init_sha2();
|
|||
|
|
init_utils();
|
|||
|
|
init_base();
|
|||
|
|
init_secp256k1();
|
|||
|
|
init_utils();
|
|||
|
|
init_sha2();
|
|||
|
|
utf8Decoder2 = new TextDecoder("utf-8");
|
|||
|
|
utf8Encoder3 = new TextEncoder();
|
|||
|
|
minPlaintextSize = 1;
|
|||
|
|
maxPlaintextSize = 4294967295;
|
|||
|
|
extendedPrefixThreshold = 65536;
|
|||
|
|
verifiedSymbol2 = /* @__PURE__ */ Symbol("verified");
|
|||
|
|
isRecord2 = (obj) => obj instanceof Object;
|
|||
|
|
JS2 = class {
|
|||
|
|
generateSecretKey() {
|
|||
|
|
return schnorr.utils.randomSecretKey();
|
|||
|
|
}
|
|||
|
|
getPublicKey(secretKey) {
|
|||
|
|
return bytesToHex(schnorr.getPublicKey(secretKey));
|
|||
|
|
}
|
|||
|
|
finalizeEvent(t, secretKey) {
|
|||
|
|
const event = t;
|
|||
|
|
event.pubkey = bytesToHex(schnorr.getPublicKey(secretKey));
|
|||
|
|
event.id = getEventHash2(event);
|
|||
|
|
event.sig = bytesToHex(schnorr.sign(hexToBytes(getEventHash2(event)), secretKey));
|
|||
|
|
event[verifiedSymbol2] = true;
|
|||
|
|
return event;
|
|||
|
|
}
|
|||
|
|
verifyEvent(event) {
|
|||
|
|
if (typeof event[verifiedSymbol2] === "boolean")
|
|||
|
|
return event[verifiedSymbol2];
|
|||
|
|
try {
|
|||
|
|
const hash = getEventHash2(event);
|
|||
|
|
if (hash !== event.id) {
|
|||
|
|
event[verifiedSymbol2] = false;
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
const valid = schnorr.verify(hexToBytes(event.sig), hexToBytes(hash), hexToBytes(event.pubkey));
|
|||
|
|
event[verifiedSymbol2] = valid;
|
|||
|
|
return valid;
|
|||
|
|
} catch (err) {
|
|||
|
|
event[verifiedSymbol2] = false;
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
i2 = new JS2();
|
|||
|
|
generateSecretKey2 = i2.generateSecretKey;
|
|||
|
|
getPublicKey2 = i2.getPublicKey;
|
|||
|
|
finalizeEvent2 = i2.finalizeEvent;
|
|||
|
|
verifyEvent2 = i2.verifyEvent;
|
|||
|
|
Seal = 13;
|
|||
|
|
GiftWrap = 1059;
|
|||
|
|
TWO_DAYS = 2 * 24 * 60 * 60;
|
|||
|
|
now = () => Math.round(Date.now() / 1e3);
|
|||
|
|
randomNow = () => Math.round(now() - Math.random() * TWO_DAYS);
|
|||
|
|
nip44ConversationKey = (privateKey, publicKey) => getConversationKey(privateKey, publicKey);
|
|||
|
|
nip44Encrypt = (data, privateKey, publicKey) => encrypt(JSON.stringify(data), nip44ConversationKey(privateKey, publicKey));
|
|||
|
|
nip44Decrypt = (data, privateKey) => JSON.parse(decrypt(data.content, nip44ConversationKey(privateKey, data.pubkey)));
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/isomorphic-ws/browser.js
|
|||
|
|
var ws, browser_default;
|
|||
|
|
var init_browser = __esm({
|
|||
|
|
"node_modules/isomorphic-ws/browser.js"() {
|
|||
|
|
ws = null;
|
|||
|
|
if (typeof WebSocket !== "undefined") {
|
|||
|
|
ws = WebSocket;
|
|||
|
|
} else if (typeof MozWebSocket !== "undefined") {
|
|||
|
|
ws = MozWebSocket;
|
|||
|
|
} else if (typeof global !== "undefined") {
|
|||
|
|
ws = global.WebSocket || global.MozWebSocket;
|
|||
|
|
} else if (typeof window !== "undefined") {
|
|||
|
|
ws = window.WebSocket || window.MozWebSocket;
|
|||
|
|
} else if (typeof self !== "undefined") {
|
|||
|
|
ws = self.WebSocket || self.MozWebSocket;
|
|||
|
|
}
|
|||
|
|
browser_default = ws;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/eventemitter3/index.js
|
|||
|
|
var require_eventemitter3 = __commonJS({
|
|||
|
|
"node_modules/eventemitter3/index.js"(exports, module) {
|
|||
|
|
"use strict";
|
|||
|
|
var has = Object.prototype.hasOwnProperty;
|
|||
|
|
var prefix = "~";
|
|||
|
|
function Events() {
|
|||
|
|
}
|
|||
|
|
if (Object.create) {
|
|||
|
|
Events.prototype = /* @__PURE__ */ Object.create(null);
|
|||
|
|
if (!new Events().__proto__) prefix = false;
|
|||
|
|
}
|
|||
|
|
function EE(fn, context, once) {
|
|||
|
|
this.fn = fn;
|
|||
|
|
this.context = context;
|
|||
|
|
this.once = once || false;
|
|||
|
|
}
|
|||
|
|
function addListener(emitter, event, fn, context, once) {
|
|||
|
|
if (typeof fn !== "function") {
|
|||
|
|
throw new TypeError("The listener must be a function");
|
|||
|
|
}
|
|||
|
|
var listener = new EE(fn, context || emitter, once), evt = prefix ? prefix + event : event;
|
|||
|
|
if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;
|
|||
|
|
else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);
|
|||
|
|
else emitter._events[evt] = [emitter._events[evt], listener];
|
|||
|
|
return emitter;
|
|||
|
|
}
|
|||
|
|
function clearEvent(emitter, evt) {
|
|||
|
|
if (--emitter._eventsCount === 0) emitter._events = new Events();
|
|||
|
|
else delete emitter._events[evt];
|
|||
|
|
}
|
|||
|
|
function EventEmitter2() {
|
|||
|
|
this._events = new Events();
|
|||
|
|
this._eventsCount = 0;
|
|||
|
|
}
|
|||
|
|
EventEmitter2.prototype.eventNames = function eventNames() {
|
|||
|
|
var names = [], events, name;
|
|||
|
|
if (this._eventsCount === 0) return names;
|
|||
|
|
for (name in events = this._events) {
|
|||
|
|
if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);
|
|||
|
|
}
|
|||
|
|
if (Object.getOwnPropertySymbols) {
|
|||
|
|
return names.concat(Object.getOwnPropertySymbols(events));
|
|||
|
|
}
|
|||
|
|
return names;
|
|||
|
|
};
|
|||
|
|
EventEmitter2.prototype.listeners = function listeners(event) {
|
|||
|
|
var evt = prefix ? prefix + event : event, handlers = this._events[evt];
|
|||
|
|
if (!handlers) return [];
|
|||
|
|
if (handlers.fn) return [handlers.fn];
|
|||
|
|
for (var i3 = 0, l = handlers.length, ee = new Array(l); i3 < l; i3++) {
|
|||
|
|
ee[i3] = handlers[i3].fn;
|
|||
|
|
}
|
|||
|
|
return ee;
|
|||
|
|
};
|
|||
|
|
EventEmitter2.prototype.listenerCount = function listenerCount(event) {
|
|||
|
|
var evt = prefix ? prefix + event : event, listeners = this._events[evt];
|
|||
|
|
if (!listeners) return 0;
|
|||
|
|
if (listeners.fn) return 1;
|
|||
|
|
return listeners.length;
|
|||
|
|
};
|
|||
|
|
EventEmitter2.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
|
|||
|
|
var evt = prefix ? prefix + event : event;
|
|||
|
|
if (!this._events[evt]) return false;
|
|||
|
|
var listeners = this._events[evt], len = arguments.length, args, i3;
|
|||
|
|
if (listeners.fn) {
|
|||
|
|
if (listeners.once) this.removeListener(event, listeners.fn, void 0, true);
|
|||
|
|
switch (len) {
|
|||
|
|
case 1:
|
|||
|
|
return listeners.fn.call(listeners.context), true;
|
|||
|
|
case 2:
|
|||
|
|
return listeners.fn.call(listeners.context, a1), true;
|
|||
|
|
case 3:
|
|||
|
|
return listeners.fn.call(listeners.context, a1, a2), true;
|
|||
|
|
case 4:
|
|||
|
|
return listeners.fn.call(listeners.context, a1, a2, a3), true;
|
|||
|
|
case 5:
|
|||
|
|
return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
|
|||
|
|
case 6:
|
|||
|
|
return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
|
|||
|
|
}
|
|||
|
|
for (i3 = 1, args = new Array(len - 1); i3 < len; i3++) {
|
|||
|
|
args[i3 - 1] = arguments[i3];
|
|||
|
|
}
|
|||
|
|
listeners.fn.apply(listeners.context, args);
|
|||
|
|
} else {
|
|||
|
|
var length = listeners.length, j;
|
|||
|
|
for (i3 = 0; i3 < length; i3++) {
|
|||
|
|
if (listeners[i3].once) this.removeListener(event, listeners[i3].fn, void 0, true);
|
|||
|
|
switch (len) {
|
|||
|
|
case 1:
|
|||
|
|
listeners[i3].fn.call(listeners[i3].context);
|
|||
|
|
break;
|
|||
|
|
case 2:
|
|||
|
|
listeners[i3].fn.call(listeners[i3].context, a1);
|
|||
|
|
break;
|
|||
|
|
case 3:
|
|||
|
|
listeners[i3].fn.call(listeners[i3].context, a1, a2);
|
|||
|
|
break;
|
|||
|
|
case 4:
|
|||
|
|
listeners[i3].fn.call(listeners[i3].context, a1, a2, a3);
|
|||
|
|
break;
|
|||
|
|
default:
|
|||
|
|
if (!args) for (j = 1, args = new Array(len - 1); j < len; j++) {
|
|||
|
|
args[j - 1] = arguments[j];
|
|||
|
|
}
|
|||
|
|
listeners[i3].fn.apply(listeners[i3].context, args);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
};
|
|||
|
|
EventEmitter2.prototype.on = function on(event, fn, context) {
|
|||
|
|
return addListener(this, event, fn, context, false);
|
|||
|
|
};
|
|||
|
|
EventEmitter2.prototype.once = function once(event, fn, context) {
|
|||
|
|
return addListener(this, event, fn, context, true);
|
|||
|
|
};
|
|||
|
|
EventEmitter2.prototype.removeListener = function removeListener(event, fn, context, once) {
|
|||
|
|
var evt = prefix ? prefix + event : event;
|
|||
|
|
if (!this._events[evt]) return this;
|
|||
|
|
if (!fn) {
|
|||
|
|
clearEvent(this, evt);
|
|||
|
|
return this;
|
|||
|
|
}
|
|||
|
|
var listeners = this._events[evt];
|
|||
|
|
if (listeners.fn) {
|
|||
|
|
if (listeners.fn === fn && (!once || listeners.once) && (!context || listeners.context === context)) {
|
|||
|
|
clearEvent(this, evt);
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
for (var i3 = 0, events = [], length = listeners.length; i3 < length; i3++) {
|
|||
|
|
if (listeners[i3].fn !== fn || once && !listeners[i3].once || context && listeners[i3].context !== context) {
|
|||
|
|
events.push(listeners[i3]);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;
|
|||
|
|
else clearEvent(this, evt);
|
|||
|
|
}
|
|||
|
|
return this;
|
|||
|
|
};
|
|||
|
|
EventEmitter2.prototype.removeAllListeners = function removeAllListeners(event) {
|
|||
|
|
var evt;
|
|||
|
|
if (event) {
|
|||
|
|
evt = prefix ? prefix + event : event;
|
|||
|
|
if (this._events[evt]) clearEvent(this, evt);
|
|||
|
|
} else {
|
|||
|
|
this._events = new Events();
|
|||
|
|
this._eventsCount = 0;
|
|||
|
|
}
|
|||
|
|
return this;
|
|||
|
|
};
|
|||
|
|
EventEmitter2.prototype.off = EventEmitter2.prototype.removeListener;
|
|||
|
|
EventEmitter2.prototype.addListener = EventEmitter2.prototype.on;
|
|||
|
|
EventEmitter2.prefixed = prefix;
|
|||
|
|
EventEmitter2.EventEmitter = EventEmitter2;
|
|||
|
|
if ("undefined" !== typeof module) {
|
|||
|
|
module.exports = EventEmitter2;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/eventemitter3/index.mjs
|
|||
|
|
var import_index;
|
|||
|
|
var init_eventemitter3 = __esm({
|
|||
|
|
"node_modules/eventemitter3/index.mjs"() {
|
|||
|
|
import_index = __toESM(require_eventemitter3(), 1);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@wizardconnect/core/dist/protocols/hdwalletv1.js
|
|||
|
|
function isPathXpub(obj) {
|
|||
|
|
return obj && typeof obj === "object" && typeof obj.name === "string" && typeof obj.xpub === "string";
|
|||
|
|
}
|
|||
|
|
function childIndexOfPathName(name) {
|
|||
|
|
switch (name) {
|
|||
|
|
case PATH_RECEIVE:
|
|||
|
|
return 0;
|
|||
|
|
case PATH_CHANGE:
|
|||
|
|
return 1;
|
|||
|
|
case PATH_DEFI:
|
|||
|
|
return 7;
|
|||
|
|
default:
|
|||
|
|
return void 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function isHdwalletv1Session(obj) {
|
|||
|
|
const s = obj;
|
|||
|
|
return obj !== null && typeof obj === "object" && Array.isArray(s.paths) && s.paths.every((p) => isPathXpub(p)) && (s.extensions === void 0 || typeof s.extensions === "object" && s.extensions !== null);
|
|||
|
|
}
|
|||
|
|
function isProtocolMessage(payload) {
|
|||
|
|
return payload && typeof payload === "object" && typeof payload.action === "string" && typeof payload.time === "number";
|
|||
|
|
}
|
|||
|
|
function isErrorMessage(payload) {
|
|||
|
|
return payload && typeof payload === "object" && typeof payload.error === "string";
|
|||
|
|
}
|
|||
|
|
function isSignTransactionRequest(msg) {
|
|||
|
|
return msg && typeof msg === "object" && msg.action === RelayMsgAction.SignTransactionRequest && msg.transaction && typeof msg.transaction === "object" && typeof msg.sequence === "number" && Array.isArray(msg.inputPaths) && msg.inputPaths.every((p) => Array.isArray(p) && p.length === 3 && typeof p[0] === "number" && typeof p[1] === "string" && typeof p[2] === "number");
|
|||
|
|
}
|
|||
|
|
function isSignCancelMessage(msg) {
|
|||
|
|
return msg && typeof msg === "object" && msg.action === RelayMsgAction.SignCancel && typeof msg.sequence === "number";
|
|||
|
|
}
|
|||
|
|
function isChunkMessage(msg) {
|
|||
|
|
return msg && typeof msg === "object" && msg.action === RelayMsgAction.Chunk && typeof msg.msgId === "string" && typeof msg.index === "number" && typeof msg.total === "number" && typeof msg.data === "string" && Number.isInteger(msg.index) && Number.isInteger(msg.total) && msg.total >= 1 && msg.index >= 0 && msg.index < msg.total;
|
|||
|
|
}
|
|||
|
|
var PROTOCOL_NAME, RelayMsgAction, PATH_RECEIVE, PATH_CHANGE, PATH_DEFI;
|
|||
|
|
var init_hdwalletv1 = __esm({
|
|||
|
|
"node_modules/@wizardconnect/core/dist/protocols/hdwalletv1.js"() {
|
|||
|
|
PROTOCOL_NAME = "hdwalletv1";
|
|||
|
|
(function(RelayMsgAction2) {
|
|||
|
|
RelayMsgAction2["DappReady"] = "dapp_ready";
|
|||
|
|
RelayMsgAction2["WalletReady"] = "wallet_ready";
|
|||
|
|
RelayMsgAction2["SignTransactionRequest"] = "sign_transaction_request";
|
|||
|
|
RelayMsgAction2["SignTransactionResponse"] = "sign_transaction_response";
|
|||
|
|
RelayMsgAction2["SignCancel"] = "sign_cancel";
|
|||
|
|
RelayMsgAction2["Disconnect"] = "disconnect";
|
|||
|
|
RelayMsgAction2["Chunk"] = "chunk";
|
|||
|
|
RelayMsgAction2["Ping"] = "ping";
|
|||
|
|
RelayMsgAction2["Pong"] = "pong";
|
|||
|
|
})(RelayMsgAction || (RelayMsgAction = {}));
|
|||
|
|
PATH_RECEIVE = "receive";
|
|||
|
|
PATH_CHANGE = "change";
|
|||
|
|
PATH_DEFI = "defi";
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@wizardconnect/core/dist/utilnostr.js
|
|||
|
|
import { binToHex, secp256k1 as secp256k12 } from "@bitauth/libauth";
|
|||
|
|
function deriveNostrPublicKey(privateKey) {
|
|||
|
|
const publicKeyCompressed = unwrap(secp256k12.derivePublicKeyCompressed(privateKey));
|
|||
|
|
const publicKeyNostr = publicKeyCompressed.slice(1);
|
|||
|
|
return binToHex(publicKeyNostr);
|
|||
|
|
}
|
|||
|
|
function deriveNostrPublicKeyBytes(privateKey) {
|
|||
|
|
const publicKeyCompressed = unwrap(secp256k12.derivePublicKeyCompressed(privateKey));
|
|||
|
|
return publicKeyCompressed.slice(1);
|
|||
|
|
}
|
|||
|
|
var init_utilnostr = __esm({
|
|||
|
|
"node_modules/@wizardconnect/core/dist/utilnostr.js"() {
|
|||
|
|
init_primitives();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@wizardconnect/core/dist/log.js
|
|||
|
|
function debug(scope, ...args) {
|
|||
|
|
console.log(`[${scope}]`, ...args);
|
|||
|
|
}
|
|||
|
|
function warn(scope, ...args) {
|
|||
|
|
console.warn(`[${scope}]`, ...args);
|
|||
|
|
}
|
|||
|
|
function error(scope, ...args) {
|
|||
|
|
console.error(`[${scope}]`, ...args);
|
|||
|
|
}
|
|||
|
|
var Scope;
|
|||
|
|
var init_log = __esm({
|
|||
|
|
"node_modules/@wizardconnect/core/dist/log.js"() {
|
|||
|
|
(function(Scope2) {
|
|||
|
|
Scope2["Relay"] = "relay";
|
|||
|
|
Scope2["Network"] = "network";
|
|||
|
|
Scope2["Misc"] = "misc";
|
|||
|
|
})(Scope || (Scope = {}));
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@wizardconnect/core/dist/message-queue.js
|
|||
|
|
var MessageQueue;
|
|||
|
|
var init_message_queue = __esm({
|
|||
|
|
"node_modules/@wizardconnect/core/dist/message-queue.js"() {
|
|||
|
|
init_log();
|
|||
|
|
MessageQueue = class {
|
|||
|
|
queue = [];
|
|||
|
|
isReady = false;
|
|||
|
|
logActivity = false;
|
|||
|
|
constructor(options) {
|
|||
|
|
this.logActivity = options?.logActivity ?? false;
|
|||
|
|
}
|
|||
|
|
getReady() {
|
|||
|
|
return this.isReady;
|
|||
|
|
}
|
|||
|
|
getQueueLength() {
|
|||
|
|
return this.queue.length;
|
|||
|
|
}
|
|||
|
|
enqueue(message) {
|
|||
|
|
if (this.isReady) {
|
|||
|
|
return Promise.resolve();
|
|||
|
|
}
|
|||
|
|
if (this.logActivity) {
|
|||
|
|
debug(Scope.Relay, `net: Relays not ready, queuing message ${message.action}`);
|
|||
|
|
}
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
this.queue.push({ message, resolve, reject });
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
async setReady(publishFn) {
|
|||
|
|
this.isReady = true;
|
|||
|
|
const queuedMessages = [...this.queue];
|
|||
|
|
this.queue = [];
|
|||
|
|
if (queuedMessages.length > 0 && this.logActivity) {
|
|||
|
|
debug(Scope.Relay, `Processing ${queuedMessages.length} queued messages`);
|
|||
|
|
}
|
|||
|
|
for (const queued of queuedMessages) {
|
|||
|
|
try {
|
|||
|
|
await publishFn(queued.message);
|
|||
|
|
queued.resolve();
|
|||
|
|
} catch (error2) {
|
|||
|
|
queued.reject(error2);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
setNotReady(errorMessage = "Connection closed before message could be sent") {
|
|||
|
|
this.isReady = false;
|
|||
|
|
const queuedMessages = [...this.queue];
|
|||
|
|
this.queue = [];
|
|||
|
|
for (const queued of queuedMessages) {
|
|||
|
|
queued.reject(new Error(errorMessage));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
clear() {
|
|||
|
|
const queuedMessages = [...this.queue];
|
|||
|
|
this.queue = [];
|
|||
|
|
for (const queued of queuedMessages) {
|
|||
|
|
queued.reject(new Error("Queue cleared"));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@wizardconnect/core/dist/transforms/chunk.js
|
|||
|
|
function chunkExtensionAdvertisement() {
|
|||
|
|
return { version: CHUNK_EXTENSION_VERSION };
|
|||
|
|
}
|
|||
|
|
function peerSupportsChunk(extensions) {
|
|||
|
|
return !!extensions && extensions[CHUNK_EXTENSION_NAME] !== void 0;
|
|||
|
|
}
|
|||
|
|
function utf8ByteLength(s) {
|
|||
|
|
return new TextEncoder().encode(s).length;
|
|||
|
|
}
|
|||
|
|
function needsChunking(serialized) {
|
|||
|
|
return utf8ByteLength(serialized) > CHUNK_REQUIRED_BYTES;
|
|||
|
|
}
|
|||
|
|
function splitIntoChunks(serialized, opts) {
|
|||
|
|
const msgId = opts?.msgId ?? newMsgId();
|
|||
|
|
const time = opts?.time ?? Math.floor(Date.now() / 1e3);
|
|||
|
|
const utf8 = new TextEncoder().encode(serialized);
|
|||
|
|
const b64 = bytesToBase64(utf8);
|
|||
|
|
const sliceChars = Math.ceil(CHUNK_RAW_BYTES * 4 / 3);
|
|||
|
|
const total = Math.max(1, Math.ceil(b64.length / sliceChars));
|
|||
|
|
const chunks = [];
|
|||
|
|
for (let i3 = 0; i3 < total; i3++) {
|
|||
|
|
chunks.push({
|
|||
|
|
action: RelayMsgAction.Chunk,
|
|||
|
|
time,
|
|||
|
|
msgId,
|
|||
|
|
index: i3,
|
|||
|
|
total,
|
|||
|
|
data: b64.slice(i3 * sliceChars, (i3 + 1) * sliceChars)
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
return chunks;
|
|||
|
|
}
|
|||
|
|
function bytesToBase64(bytes) {
|
|||
|
|
const CHUNK = 32768;
|
|||
|
|
let binary = "";
|
|||
|
|
for (let i3 = 0; i3 < bytes.length; i3 += CHUNK) {
|
|||
|
|
const slice = bytes.subarray(i3, i3 + CHUNK);
|
|||
|
|
binary += String.fromCharCode.apply(null, slice);
|
|||
|
|
}
|
|||
|
|
return btoa(binary);
|
|||
|
|
}
|
|||
|
|
function base64ToBytes(b64) {
|
|||
|
|
const binary = atob(b64);
|
|||
|
|
const out = new Uint8Array(binary.length);
|
|||
|
|
for (let i3 = 0; i3 < binary.length; i3++) {
|
|||
|
|
out[i3] = binary.charCodeAt(i3);
|
|||
|
|
}
|
|||
|
|
return out;
|
|||
|
|
}
|
|||
|
|
function newMsgId() {
|
|||
|
|
const g = globalThis;
|
|||
|
|
if (g.crypto?.randomUUID)
|
|||
|
|
return g.crypto.randomUUID();
|
|||
|
|
const bytes = new Uint8Array(16);
|
|||
|
|
const cryptoObj = globalThis.crypto;
|
|||
|
|
if (cryptoObj?.getRandomValues)
|
|||
|
|
cryptoObj.getRandomValues(bytes);
|
|||
|
|
else
|
|||
|
|
for (let i3 = 0; i3 < 16; i3++)
|
|||
|
|
bytes[i3] = Math.floor(Math.random() * 256);
|
|||
|
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|||
|
|
}
|
|||
|
|
var CHUNK_EXTENSION_NAME, CHUNK_EXTENSION_VERSION, CHUNK_RAW_BYTES, CHUNK_REQUIRED_BYTES, REASSEMBLY_TTL_MS, SWEEP_INTERVAL_MS, ChunkReassembler;
|
|||
|
|
var init_chunk = __esm({
|
|||
|
|
"node_modules/@wizardconnect/core/dist/transforms/chunk.js"() {
|
|||
|
|
init_hdwalletv1();
|
|||
|
|
init_log();
|
|||
|
|
CHUNK_EXTENSION_NAME = "chunk";
|
|||
|
|
CHUNK_EXTENSION_VERSION = 1;
|
|||
|
|
CHUNK_RAW_BYTES = 3e4;
|
|||
|
|
CHUNK_REQUIRED_BYTES = 4e4;
|
|||
|
|
REASSEMBLY_TTL_MS = 12e4;
|
|||
|
|
SWEEP_INTERVAL_MS = 1e4;
|
|||
|
|
ChunkReassembler = class {
|
|||
|
|
onComplete;
|
|||
|
|
logActivity;
|
|||
|
|
now;
|
|||
|
|
buffers = /* @__PURE__ */ new Map();
|
|||
|
|
/// msgIds that have already been delivered, kept for a short grace period
|
|||
|
|
/// so late-arriving duplicate chunks (e.g. cross-subscription replay after
|
|||
|
|
/// reconnect) don't spawn a second reassembly and double-deliver.
|
|||
|
|
completed = /* @__PURE__ */ new Map();
|
|||
|
|
sweeperId = null;
|
|||
|
|
constructor(onComplete, logActivity = true, now2 = () => Date.now()) {
|
|||
|
|
this.onComplete = onComplete;
|
|||
|
|
this.logActivity = logActivity;
|
|||
|
|
this.now = now2;
|
|||
|
|
}
|
|||
|
|
start() {
|
|||
|
|
if (this.sweeperId !== null)
|
|||
|
|
return;
|
|||
|
|
this.sweeperId = setInterval(() => this.sweep(), SWEEP_INTERVAL_MS);
|
|||
|
|
}
|
|||
|
|
stop() {
|
|||
|
|
if (this.sweeperId !== null) {
|
|||
|
|
clearInterval(this.sweeperId);
|
|||
|
|
this.sweeperId = null;
|
|||
|
|
}
|
|||
|
|
this.buffers.clear();
|
|||
|
|
this.completed.clear();
|
|||
|
|
}
|
|||
|
|
/// For tests. Otherwise start() schedules this automatically.
|
|||
|
|
sweep() {
|
|||
|
|
const now2 = this.now();
|
|||
|
|
for (const [id, entry] of this.buffers) {
|
|||
|
|
if (entry.expiresAt <= now2) {
|
|||
|
|
this.buffers.delete(id);
|
|||
|
|
if (this.logActivity) {
|
|||
|
|
debug(Scope.Relay, `Chunk reassembly timeout: ${id} (${entry.received}/${entry.total} received)`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
for (const [id, expiresAt] of this.completed) {
|
|||
|
|
if (expiresAt <= now2)
|
|||
|
|
this.completed.delete(id);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// Ingest one chunk. If this completes the message, onComplete fires.
|
|||
|
|
/// Duplicate chunks (same msgId/index) are idempotent. Malformed chunks
|
|||
|
|
/// are dropped silently.
|
|||
|
|
ingest(chunk) {
|
|||
|
|
if (this.completed.has(chunk.msgId)) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
let entry = this.buffers.get(chunk.msgId);
|
|||
|
|
if (!entry) {
|
|||
|
|
entry = {
|
|||
|
|
total: chunk.total,
|
|||
|
|
chunks: new Array(chunk.total),
|
|||
|
|
received: 0,
|
|||
|
|
expiresAt: this.now() + REASSEMBLY_TTL_MS
|
|||
|
|
};
|
|||
|
|
this.buffers.set(chunk.msgId, entry);
|
|||
|
|
} else if (entry.total !== chunk.total) {
|
|||
|
|
if (this.logActivity) {
|
|||
|
|
error(Scope.Relay, `Chunk total mismatch for ${chunk.msgId}: ${chunk.total} vs expected ${entry.total}`);
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (entry.chunks[chunk.index] !== void 0) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
entry.chunks[chunk.index] = chunk.data;
|
|||
|
|
entry.received++;
|
|||
|
|
if (entry.received !== entry.total)
|
|||
|
|
return;
|
|||
|
|
this.buffers.delete(chunk.msgId);
|
|||
|
|
this.completed.set(chunk.msgId, this.now() + REASSEMBLY_TTL_MS);
|
|||
|
|
let reassembled;
|
|||
|
|
try {
|
|||
|
|
const fullB64 = entry.chunks.join("");
|
|||
|
|
const bytes = base64ToBytes(fullB64);
|
|||
|
|
const json = new TextDecoder().decode(bytes);
|
|||
|
|
const parsed = JSON.parse(json);
|
|||
|
|
if (!isProtocolMessage(parsed)) {
|
|||
|
|
if (this.logActivity) {
|
|||
|
|
error(Scope.Relay, `Reassembled chunk msgId=${chunk.msgId} is not a valid ProtocolMessage`);
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
reassembled = parsed;
|
|||
|
|
} catch (e) {
|
|||
|
|
if (this.logActivity) {
|
|||
|
|
error(Scope.Relay, `Chunk reassembly failed for msgId=${chunk.msgId}:`, e);
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (this.logActivity) {
|
|||
|
|
debug(Scope.Relay, `Reassembled chunked message: action=${reassembled.action} chunks=${entry.total}`);
|
|||
|
|
}
|
|||
|
|
this.onComplete(reassembled);
|
|||
|
|
}
|
|||
|
|
/// Test helper: number of in-flight partial messages.
|
|||
|
|
get bufferCount() {
|
|||
|
|
return this.buffers.size;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@wizardconnect/core/dist/relay-client.js
|
|||
|
|
import { binToHex as binToHex2, hash256, secp256k1 as secp256k13 } from "@bitauth/libauth";
|
|||
|
|
var KIND_GIFT_WRAP, KIND_PRIVATE_DIRECT_MESSAGE, RelayClient;
|
|||
|
|
var init_relay_client = __esm({
|
|||
|
|
"node_modules/@wizardconnect/core/dist/relay-client.js"() {
|
|||
|
|
init_primitives();
|
|||
|
|
init_pool();
|
|||
|
|
init_nip59();
|
|||
|
|
init_browser();
|
|||
|
|
init_eventemitter3();
|
|||
|
|
init_hdwalletv1();
|
|||
|
|
init_utilnostr();
|
|||
|
|
init_message_queue();
|
|||
|
|
init_log();
|
|||
|
|
init_chunk();
|
|||
|
|
useWebSocketImplementation(browser_default);
|
|||
|
|
KIND_GIFT_WRAP = 1059;
|
|||
|
|
KIND_PRIVATE_DIRECT_MESSAGE = 14;
|
|||
|
|
RelayClient = class _RelayClient extends import_index.default {
|
|||
|
|
// Keyed by "walletPubkeyHex:dappPubkeyHex". Persists the high-water mark
|
|||
|
|
// across RelayClient instance teardowns within the same JS session so that
|
|||
|
|
// reconnects after an explicit disconnect()+connect() still filter
|
|||
|
|
// relay-replayed messages from the prior session.
|
|||
|
|
static sessionTimestamps = /* @__PURE__ */ new Map();
|
|||
|
|
pool;
|
|||
|
|
sharedPool;
|
|||
|
|
pairedPubkeyHex;
|
|||
|
|
config;
|
|||
|
|
subscription = null;
|
|||
|
|
myPubkey;
|
|||
|
|
myPubkeyHex;
|
|||
|
|
lastProcessedTimestamp = 0;
|
|||
|
|
messageQueue;
|
|||
|
|
readyTimeoutId = null;
|
|||
|
|
disconnecting = false;
|
|||
|
|
/// Capability flag: peer advertised support for the `chunk` transport
|
|||
|
|
/// extension in its dapp_ready / wallet_ready. Set via setPeerCapabilities.
|
|||
|
|
peerSupportsChunk = false;
|
|||
|
|
/// Receiver-side reassembly buffer. Always active — if no chunks arrive it
|
|||
|
|
/// stays empty. Started in connect(), stopped in disconnect().
|
|||
|
|
reassembler;
|
|||
|
|
sequence = Math.floor(Math.random() * (Number.MAX_SAFE_INTEGER - 5e5));
|
|||
|
|
pendingCalls = /* @__PURE__ */ new Map();
|
|||
|
|
pendingDeliveries = /* @__PURE__ */ new Map();
|
|||
|
|
get sessionKey() {
|
|||
|
|
return this.pairedPubkeyHex ? `${this.myPubkeyHex}:${this.pairedPubkeyHex}` : null;
|
|||
|
|
}
|
|||
|
|
constructor(config, pool) {
|
|||
|
|
super();
|
|||
|
|
this.config = {
|
|||
|
|
logNetworkActivity: true,
|
|||
|
|
...config
|
|||
|
|
};
|
|||
|
|
this.pool = pool ?? new SimplePool({ enablePing: true });
|
|||
|
|
this.sharedPool = pool !== void 0;
|
|||
|
|
this.messageQueue = new MessageQueue({
|
|||
|
|
logActivity: this.config.logNetworkActivity
|
|||
|
|
});
|
|||
|
|
this.reassembler = new ChunkReassembler((msg) => this.handleRelayMessage(msg), !!this.config.logNetworkActivity);
|
|||
|
|
this.myPubkey = unwrap(secp256k13.derivePublicKeyCompressed(this.config.signerPrivateKey));
|
|||
|
|
this.myPubkeyHex = deriveNostrPublicKey(this.config.signerPrivateKey);
|
|||
|
|
if (this.config.pairedPublicKey) {
|
|||
|
|
const pairedNostrPubkey = this.config.pairedPublicKey.length === 33 ? this.config.pairedPublicKey.slice(1) : this.config.pairedPublicKey;
|
|||
|
|
this.pairedPubkeyHex = binToHex2(pairedNostrPubkey);
|
|||
|
|
} else {
|
|||
|
|
this.pairedPubkeyHex = "";
|
|||
|
|
}
|
|||
|
|
const saved = this.sessionKey ? _RelayClient.sessionTimestamps.get(this.sessionKey) : void 0;
|
|||
|
|
if (saved)
|
|||
|
|
this.lastProcessedTimestamp = saved;
|
|||
|
|
}
|
|||
|
|
setPairedPublicKey(pairedPublicKey) {
|
|||
|
|
this.config.pairedPublicKey = pairedPublicKey;
|
|||
|
|
const pairedNostrPubkey = pairedPublicKey.length === 33 ? pairedPublicKey.slice(1) : pairedPublicKey;
|
|||
|
|
this.pairedPubkeyHex = binToHex2(pairedNostrPubkey);
|
|||
|
|
const saved = _RelayClient.sessionTimestamps.get(this.sessionKey);
|
|||
|
|
if (saved && saved > this.lastProcessedTimestamp) {
|
|||
|
|
this.lastProcessedTimestamp = saved;
|
|||
|
|
}
|
|||
|
|
this.emit("paired");
|
|||
|
|
}
|
|||
|
|
/// Set transport-level capability flags based on the peer's advertisement in
|
|||
|
|
/// its dapp_ready / wallet_ready `extensions` field. Called by the connection
|
|||
|
|
/// manager after the handshake. New capability keys are additive — callers
|
|||
|
|
/// may omit any they don't set.
|
|||
|
|
setPeerCapabilities(caps) {
|
|||
|
|
if (caps.chunk !== void 0) {
|
|||
|
|
this.peerSupportsChunk = caps.chunk;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
getPublicKey() {
|
|||
|
|
return this.myPubkey;
|
|||
|
|
}
|
|||
|
|
getPublicKeyHex() {
|
|||
|
|
return this.myPubkeyHex;
|
|||
|
|
}
|
|||
|
|
isKeyExchangeComplete() {
|
|||
|
|
if (!this.config.pairedPublicKey) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
return !this.config.pairedPublicKey.every((byte) => byte === 0);
|
|||
|
|
}
|
|||
|
|
emitDisconnect(error2) {
|
|||
|
|
if (this.disconnecting)
|
|||
|
|
return;
|
|||
|
|
this.disconnecting = true;
|
|||
|
|
this.emit("disconnect", error2);
|
|||
|
|
}
|
|||
|
|
async connect() {
|
|||
|
|
if (this.config.logNetworkActivity) {
|
|||
|
|
debug(Scope.Relay, `Connecting to relay...`);
|
|||
|
|
}
|
|||
|
|
this.disconnecting = false;
|
|||
|
|
this.reassembler.start();
|
|||
|
|
if (this.lastProcessedTimestamp === 0) {
|
|||
|
|
this.lastProcessedTimestamp = Math.floor(Date.now() / 1e3) - 2;
|
|||
|
|
}
|
|||
|
|
try {
|
|||
|
|
this.subscription = this.pool.subscribeMany(this.config.explicitRelayUrls, { kinds: [KIND_GIFT_WRAP], "#p": [this.myPubkeyHex] }, {
|
|||
|
|
onevent: (event) => this.handleWrappedEvent(event),
|
|||
|
|
oneose: () => {
|
|||
|
|
if (this.readyTimeoutId) {
|
|||
|
|
clearTimeout(this.readyTimeoutId);
|
|||
|
|
this.readyTimeoutId = null;
|
|||
|
|
}
|
|||
|
|
if (this.config.logNetworkActivity) {
|
|||
|
|
debug(Scope.Relay, `EOSE received, relay connected`);
|
|||
|
|
}
|
|||
|
|
this.messageQueue.setReady((msg) => this.publishMessage(msg));
|
|||
|
|
this.emit("connection");
|
|||
|
|
},
|
|||
|
|
onclose: (reasons) => {
|
|||
|
|
if (this.config.logNetworkActivity) {
|
|||
|
|
debug(Scope.Relay, `Subscription closed: ${reasons.join(", ")}`);
|
|||
|
|
}
|
|||
|
|
this.emitDisconnect(new Error("Subscription closed"));
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
this.readyTimeoutId = setTimeout(() => {
|
|||
|
|
if (!this.messageQueue.getReady()) {
|
|||
|
|
if (this.config.logNetworkActivity) {
|
|||
|
|
debug(Scope.Relay, `EOSE timeout, assuming ready`);
|
|||
|
|
}
|
|||
|
|
this.messageQueue.setReady((msg) => this.publishMessage(msg));
|
|||
|
|
this.emit("connection");
|
|||
|
|
}
|
|||
|
|
this.readyTimeoutId = null;
|
|||
|
|
}, 5e3);
|
|||
|
|
if (this.config.logNetworkActivity) {
|
|||
|
|
debug(Scope.Relay, `Subscription created, waiting for relay connection`);
|
|||
|
|
}
|
|||
|
|
} catch (error2) {
|
|||
|
|
if (this.config.logNetworkActivity) {
|
|||
|
|
error(Scope.Relay, `Connection failed:`, error2);
|
|||
|
|
}
|
|||
|
|
throw error2;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
async disconnect() {
|
|||
|
|
this.lastProcessedTimestamp = Math.floor(Date.now() / 1e3);
|
|||
|
|
const key = this.sessionKey;
|
|||
|
|
if (key)
|
|||
|
|
_RelayClient.sessionTimestamps.set(key, this.lastProcessedTimestamp);
|
|||
|
|
this.messageQueue.setNotReady();
|
|||
|
|
this.reassembler.stop();
|
|||
|
|
if (this.readyTimeoutId) {
|
|||
|
|
clearTimeout(this.readyTimeoutId);
|
|||
|
|
this.readyTimeoutId = null;
|
|||
|
|
}
|
|||
|
|
if (this.subscription) {
|
|||
|
|
this.subscription.close();
|
|||
|
|
this.subscription = null;
|
|||
|
|
}
|
|||
|
|
if (!this.sharedPool) {
|
|||
|
|
this.pool.close(this.config.explicitRelayUrls);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
getLastProcessedTimestamp() {
|
|||
|
|
return this.lastProcessedTimestamp;
|
|||
|
|
}
|
|||
|
|
setLastProcessedTimestamp(timestamp) {
|
|||
|
|
this.lastProcessedTimestamp = timestamp;
|
|||
|
|
}
|
|||
|
|
async relay(message) {
|
|||
|
|
if (!this.config.pairedPublicKey) {
|
|||
|
|
throw new Error("Cannot relay message: paired public key not set. Call setPairedPublicKey() first.");
|
|||
|
|
}
|
|||
|
|
if (!this.messageQueue.getReady()) {
|
|||
|
|
return this.messageQueue.enqueue(message);
|
|||
|
|
}
|
|||
|
|
return this.publishMessage(message);
|
|||
|
|
}
|
|||
|
|
async publishMessage(message) {
|
|||
|
|
const serialized = JSON.stringify(message);
|
|||
|
|
if (!needsChunking(serialized)) {
|
|||
|
|
return this.publishSerialized(message.action, serialized);
|
|||
|
|
}
|
|||
|
|
if (!this.peerSupportsChunk) {
|
|||
|
|
const err = new Error(`Cannot send ${message.action}: message is larger than NIP-44's 65,535-byte ceiling and the peer does not advertise the 'chunk' transport extension. Please update the connected wallet/dapp to a version that supports chunked messages.`);
|
|||
|
|
if (this.config.logNetworkActivity) {
|
|||
|
|
error(Scope.Relay, err.message);
|
|||
|
|
}
|
|||
|
|
throw err;
|
|||
|
|
}
|
|||
|
|
const chunks = splitIntoChunks(serialized);
|
|||
|
|
if (this.config.logNetworkActivity) {
|
|||
|
|
debug(Scope.Relay, `Chunking ${message.action}: ${chunks.length} chunks (serialized ~${serialized.length} bytes)`);
|
|||
|
|
}
|
|||
|
|
for (const chunk of chunks) {
|
|||
|
|
await this.publishSerialized(`${message.action}[chunk ${chunk.index + 1}/${chunk.total}]`, JSON.stringify(chunk));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// Wrap and publish one gift-wrap event. Used for both unchunked messages
|
|||
|
|
/// and individual chunks. `displayAction` is only used for logs.
|
|||
|
|
async publishSerialized(displayAction, serialized) {
|
|||
|
|
this.netlog("send", displayAction);
|
|||
|
|
const wrapped = wrapEvent({
|
|||
|
|
kind: KIND_PRIVATE_DIRECT_MESSAGE,
|
|||
|
|
content: serialized,
|
|||
|
|
created_at: Math.floor(Date.now() / 1e3),
|
|||
|
|
tags: [["p", this.pairedPubkeyHex]]
|
|||
|
|
}, this.config.signerPrivateKey, this.pairedPubkeyHex);
|
|||
|
|
const results = await Promise.allSettled(this.pool.publish(this.config.explicitRelayUrls, wrapped));
|
|||
|
|
const fulfilled = results.filter((r) => r.status === "fulfilled");
|
|||
|
|
const rejected = results.filter((r) => r.status === "rejected");
|
|||
|
|
if (rejected.length > 0 && this.config.logNetworkActivity) {
|
|||
|
|
for (const r of rejected) {
|
|||
|
|
error(Scope.Relay, `Failed to publish ${displayAction} to a relay:`, r.reason);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (fulfilled.length === 0) {
|
|||
|
|
const error2 = new Error(`Failed to publish ${displayAction} to all relays`);
|
|||
|
|
if (this.config.logNetworkActivity) {
|
|||
|
|
error(Scope.Relay, error2.message);
|
|||
|
|
}
|
|||
|
|
this.emitDisconnect(error2);
|
|||
|
|
throw error2;
|
|||
|
|
}
|
|||
|
|
if (this.config.logNetworkActivity) {
|
|||
|
|
debug(Scope.Relay, `Published message ${displayAction} to ${fulfilled.length}/${results.length} relay(s)`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
handleWrappedEvent(wrappedEvent) {
|
|||
|
|
try {
|
|||
|
|
const rumor = unwrapEvent(wrappedEvent, this.config.signerPrivateKey);
|
|||
|
|
if (rumor.kind !== KIND_PRIVATE_DIRECT_MESSAGE) {
|
|||
|
|
if (this.config.logNetworkActivity) {
|
|||
|
|
debug(Scope.Relay, `Ignoring non-PrivateDirectMessage, kind: ${rumor.kind}`);
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
let payload;
|
|||
|
|
try {
|
|||
|
|
payload = JSON.parse(rumor.content);
|
|||
|
|
} catch (e) {
|
|||
|
|
if (this.config.logNetworkActivity) {
|
|||
|
|
error(Scope.Relay, "Failed to parse message content as JSON:", e);
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
this.routeIncoming(payload, rumor.pubkey);
|
|||
|
|
} catch (error2) {
|
|||
|
|
if (this.config.logNetworkActivity) {
|
|||
|
|
error(Scope.Relay, "Error handling incoming message:", error2);
|
|||
|
|
}
|
|||
|
|
this.emitError(error2);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// Apply timestamp dedup + peer filter, then dispatch to chunk reassembly
|
|||
|
|
/// or the application-level handler. Called from handleWrappedEvent (one
|
|||
|
|
/// path: unwrap → route). Kept separate to keep handleWrappedEvent focused
|
|||
|
|
/// on decryption and to allow future transport-layer transforms to invoke
|
|||
|
|
/// this path with already-decoded payloads.
|
|||
|
|
routeIncoming(payload, fromPubkey) {
|
|||
|
|
if (!payload.time || this.lastProcessedTimestamp > 0 && payload.time < this.lastProcessedTimestamp) {
|
|||
|
|
if (this.config.logNetworkActivity) {
|
|||
|
|
debug(Scope.Relay, `Ignoring already-processed message (time: ${payload.time}, action: ${payload.action}, last processed: ${this.lastProcessedTimestamp})`);
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (payload.time > this.lastProcessedTimestamp) {
|
|||
|
|
this.lastProcessedTimestamp = payload.time;
|
|||
|
|
const key = this.sessionKey;
|
|||
|
|
if (key)
|
|||
|
|
_RelayClient.sessionTimestamps.set(key, this.lastProcessedTimestamp);
|
|||
|
|
}
|
|||
|
|
const isKeyExchangeMessage = payload.action === RelayMsgAction.WalletReady;
|
|||
|
|
if (!isKeyExchangeMessage && this.config.pairedPublicKey) {
|
|||
|
|
const pairedNostrPubkey = this.config.pairedPublicKey.length === 33 ? binToHex2(this.config.pairedPublicKey.slice(1)) : binToHex2(this.config.pairedPublicKey);
|
|||
|
|
if (fromPubkey !== pairedNostrPubkey) {
|
|||
|
|
if (this.config.logNetworkActivity) {
|
|||
|
|
debug(Scope.Relay, `Ignoring '${payload.action}' message from unknown peer: ${fromPubkey} (expected: ${pairedNostrPubkey})`);
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (isChunkMessage(payload)) {
|
|||
|
|
if (this.config.logNetworkActivity) {
|
|||
|
|
debug(Scope.Relay, `Received chunk ${payload.index + 1}/${payload.total} (msgId=${payload.msgId})`);
|
|||
|
|
}
|
|||
|
|
this.reassembler.ingest(payload);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (this.config.logNetworkActivity) {
|
|||
|
|
debug(Scope.Relay, `Received message ${payload.action} from relay`);
|
|||
|
|
}
|
|||
|
|
this.handleRelayMessage(payload);
|
|||
|
|
}
|
|||
|
|
isConnected() {
|
|||
|
|
return this.subscription !== null;
|
|||
|
|
}
|
|||
|
|
netlog(direction, what, sequence) {
|
|||
|
|
if (!this.config.logNetworkActivity) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
const us = binToHex2(hash256(this.config.signerPrivateKey)).slice(-6);
|
|||
|
|
const them = this.config.pairedPublicKey ? binToHex2(this.config.pairedPublicKey).slice(-6) : "??????";
|
|||
|
|
const pending = `c${this.pendingCalls.size} d${this.pendingDeliveries.size}`;
|
|||
|
|
if (direction === "send") {
|
|||
|
|
debug(Scope.Relay, `net [${sequence ?? "?"} ${pending}] ${us} -> ${them}: ${what}`);
|
|||
|
|
} else {
|
|||
|
|
debug(Scope.Relay, `net [${sequence ?? "?"} ${pending}] ${us} <- ${them}: ${what}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
async handleRelayMessage(message) {
|
|||
|
|
throwUnless(isProtocolMessage(message), `Invalid protocol message: ${message}`);
|
|||
|
|
this.emit("message", message);
|
|||
|
|
}
|
|||
|
|
nextSequence() {
|
|||
|
|
const current = this.sequence;
|
|||
|
|
this.sequence += 2;
|
|||
|
|
return current;
|
|||
|
|
}
|
|||
|
|
emitError(error2) {
|
|||
|
|
this.emit("error", error2);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@wizardconnect/core/dist/connection-manager.js
|
|||
|
|
async function sleep(ms) {
|
|||
|
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|||
|
|
}
|
|||
|
|
var createConnectionManager;
|
|||
|
|
var init_connection_manager = __esm({
|
|||
|
|
"node_modules/@wizardconnect/core/dist/connection-manager.js"() {
|
|||
|
|
init_log();
|
|||
|
|
createConnectionManager = (client, callbacks, events, options = {}) => {
|
|||
|
|
const { reconnectInterval = 5e3, maxReconnectAttempts = Infinity, enableVisibilityHandling = true, scope = Scope.Network, onVisibilityChange } = options;
|
|||
|
|
let isPaused = false;
|
|||
|
|
let reconnectLoop = null;
|
|||
|
|
let visibilityChangeHandler = null;
|
|||
|
|
const triggerReconnect = (reason) => {
|
|||
|
|
if (!isPaused) {
|
|||
|
|
callbacks.onReconnecting(client, reason);
|
|||
|
|
reconnectLoop = null;
|
|||
|
|
startConnectionLoop();
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
const onConnected = () => callbacks.onConnected(client);
|
|||
|
|
client.on(events.connected, onConnected);
|
|||
|
|
const onDisconnected = (...args) => {
|
|||
|
|
const err = args[0] instanceof Error ? args[0].message : null;
|
|||
|
|
debug(scope, "Disconnected event received", err);
|
|||
|
|
triggerReconnect(err);
|
|||
|
|
};
|
|||
|
|
const onError = (err) => {
|
|||
|
|
const errorMsg = err?.message || String(err);
|
|||
|
|
debug(scope, "Error event received", errorMsg);
|
|||
|
|
triggerReconnect(errorMsg);
|
|||
|
|
if (callbacks.onError) {
|
|||
|
|
callbacks.onError(client, err);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
client.on(events.disconnected, onDisconnected);
|
|||
|
|
if (events.error) {
|
|||
|
|
client.on(events.error, onError);
|
|||
|
|
}
|
|||
|
|
const setupVisibilityHandling = () => {
|
|||
|
|
if (typeof document === "undefined") {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
visibilityChangeHandler = () => {
|
|||
|
|
const state = document.visibilityState;
|
|||
|
|
if (onVisibilityChange) {
|
|||
|
|
const context = {
|
|||
|
|
client,
|
|||
|
|
state,
|
|||
|
|
setPaused: (value) => {
|
|||
|
|
isPaused = value;
|
|||
|
|
},
|
|||
|
|
isPaused: () => isPaused,
|
|||
|
|
startConnectionLoop
|
|||
|
|
};
|
|||
|
|
(async () => {
|
|||
|
|
try {
|
|||
|
|
await onVisibilityChange(context);
|
|||
|
|
} catch (err) {
|
|||
|
|
debug(scope, "Error in custom visibility change handler:", err);
|
|||
|
|
}
|
|||
|
|
})();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (state === "hidden") {
|
|||
|
|
isPaused = true;
|
|||
|
|
(async () => {
|
|||
|
|
try {
|
|||
|
|
await client.disconnect();
|
|||
|
|
callbacks.onDisconnected(client);
|
|||
|
|
} catch (err) {
|
|||
|
|
debug(scope, "Error disconnecting on visibility change:", err);
|
|||
|
|
}
|
|||
|
|
})();
|
|||
|
|
} else if (state === "visible") {
|
|||
|
|
if (isPaused) {
|
|||
|
|
isPaused = false;
|
|||
|
|
startConnectionLoop();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
document.addEventListener("visibilitychange", visibilityChangeHandler);
|
|||
|
|
};
|
|||
|
|
const startConnectionLoop = () => {
|
|||
|
|
if (reconnectLoop) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
reconnectLoop = (async () => {
|
|||
|
|
let reconnectAttempts = 0;
|
|||
|
|
let wasConnected = false;
|
|||
|
|
while (true) {
|
|||
|
|
if (isPaused) {
|
|||
|
|
await sleep(1e3);
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
try {
|
|||
|
|
await client.connect();
|
|||
|
|
reconnectAttempts = 0;
|
|||
|
|
wasConnected = true;
|
|||
|
|
reconnectLoop = null;
|
|||
|
|
return;
|
|||
|
|
} catch (e) {
|
|||
|
|
reconnectAttempts++;
|
|||
|
|
if (wasConnected || reconnectAttempts === 1) {
|
|||
|
|
callbacks.onReconnecting(client, `${e}`);
|
|||
|
|
wasConnected = false;
|
|||
|
|
}
|
|||
|
|
if (reconnectAttempts > maxReconnectAttempts) {
|
|||
|
|
callbacks.onDisconnected(client);
|
|||
|
|
reconnectLoop = null;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
try {
|
|||
|
|
await client.disconnect();
|
|||
|
|
} catch (disconnectError) {
|
|||
|
|
debug(scope, "Failed to disconnect client", disconnectError);
|
|||
|
|
}
|
|||
|
|
await sleep(reconnectInterval);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
})();
|
|||
|
|
};
|
|||
|
|
if (enableVisibilityHandling) {
|
|||
|
|
setupVisibilityHandling();
|
|||
|
|
}
|
|||
|
|
const cleanup = async () => {
|
|||
|
|
isPaused = true;
|
|||
|
|
if (typeof document !== "undefined" && visibilityChangeHandler !== null) {
|
|||
|
|
document.removeEventListener("visibilitychange", visibilityChangeHandler);
|
|||
|
|
}
|
|||
|
|
client.off(events.connected, onConnected);
|
|||
|
|
client.off(events.disconnected, onDisconnected);
|
|||
|
|
if (events.error) {
|
|||
|
|
client.off(events.error, onError);
|
|||
|
|
}
|
|||
|
|
callbacks.onDisconnected(client);
|
|||
|
|
try {
|
|||
|
|
await client.disconnect();
|
|||
|
|
} catch (e) {
|
|||
|
|
debug(scope, "Failed to disconnect client during cleanup", e);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
return {
|
|||
|
|
cleanup,
|
|||
|
|
startConnectionLoop
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@wizardconnect/core/dist/relay-handler.js
|
|||
|
|
var RelayStatus, initiateRelay;
|
|||
|
|
var init_relay_handler = __esm({
|
|||
|
|
"node_modules/@wizardconnect/core/dist/relay-handler.js"() {
|
|||
|
|
init_relay_client();
|
|||
|
|
init_connection_manager();
|
|||
|
|
init_log();
|
|||
|
|
RelayStatus = class _RelayStatus {
|
|||
|
|
status;
|
|||
|
|
error;
|
|||
|
|
sessionId;
|
|||
|
|
constructor(status, error2, sessionId = null) {
|
|||
|
|
this.status = status;
|
|||
|
|
this.error = error2;
|
|||
|
|
this.sessionId = sessionId;
|
|||
|
|
}
|
|||
|
|
static connected(sessionId) {
|
|||
|
|
return new _RelayStatus("connected", null, sessionId || null);
|
|||
|
|
}
|
|||
|
|
static reconnecting(reason, sessionId) {
|
|||
|
|
return new _RelayStatus("reconnecting", reason, sessionId || null);
|
|||
|
|
}
|
|||
|
|
static disconnected() {
|
|||
|
|
return new _RelayStatus("disconnected", null, null);
|
|||
|
|
}
|
|||
|
|
static sessionDeleted() {
|
|||
|
|
return new _RelayStatus("session_deleted", null, null);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
initiateRelay = (dispatchCallback, signerPrivateKey, pairPublicKey, options) => {
|
|||
|
|
const client = new RelayClient({
|
|||
|
|
explicitRelayUrls: options?.explicitRelayUrls ?? [],
|
|||
|
|
signerPrivateKey,
|
|||
|
|
pairedPublicKey: pairPublicKey
|
|||
|
|
});
|
|||
|
|
let lastProcessedTimestamp = 0;
|
|||
|
|
const connectionManager = createConnectionManager(client, {
|
|||
|
|
onConnected: () => {
|
|||
|
|
if (lastProcessedTimestamp > 0) {
|
|||
|
|
client.setLastProcessedTimestamp(lastProcessedTimestamp);
|
|||
|
|
} else {
|
|||
|
|
lastProcessedTimestamp = client.getLastProcessedTimestamp();
|
|||
|
|
}
|
|||
|
|
dispatchCallback({
|
|||
|
|
client,
|
|||
|
|
status: RelayStatus.connected()
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
onReconnecting: (_client, reason) => {
|
|||
|
|
dispatchCallback({
|
|||
|
|
client,
|
|||
|
|
status: RelayStatus.reconnecting(reason)
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
onDisconnected: () => {
|
|||
|
|
dispatchCallback({
|
|||
|
|
client,
|
|||
|
|
status: RelayStatus.disconnected()
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
onError: (_client, _error) => {
|
|||
|
|
}
|
|||
|
|
}, {
|
|||
|
|
connected: "connection",
|
|||
|
|
disconnected: "disconnect",
|
|||
|
|
error: "error"
|
|||
|
|
}, {
|
|||
|
|
reconnectInterval: options?.reconnectInterval,
|
|||
|
|
maxReconnectAttempts: options?.maxReconnectAttempts,
|
|||
|
|
enableVisibilityHandling: options?.enableVisibilityHandling ?? true,
|
|||
|
|
scope: Scope.Relay,
|
|||
|
|
onVisibilityChange: async (context) => {
|
|||
|
|
if (context.state === "hidden") {
|
|||
|
|
debug(Scope.Relay, "Page hidden, disconnecting relay");
|
|||
|
|
context.setPaused(true);
|
|||
|
|
try {
|
|||
|
|
await client.disconnect();
|
|||
|
|
lastProcessedTimestamp = client.getLastProcessedTimestamp();
|
|||
|
|
debug(Scope.Relay, `Disconnected, last processed timestamp: ${lastProcessedTimestamp}`);
|
|||
|
|
dispatchCallback({
|
|||
|
|
client,
|
|||
|
|
status: RelayStatus.disconnected()
|
|||
|
|
});
|
|||
|
|
} catch (error2) {
|
|||
|
|
error(Scope.Relay, "Error disconnecting on visibility change:", error2);
|
|||
|
|
}
|
|||
|
|
} else if (context.state === "visible") {
|
|||
|
|
if (context.isPaused()) {
|
|||
|
|
debug(Scope.Relay, "Page visible, reconnecting relay");
|
|||
|
|
context.setPaused(false);
|
|||
|
|
context.startConnectionLoop();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
connectionManager.startConnectionLoop();
|
|||
|
|
return () => {
|
|||
|
|
dispatchCallback({
|
|||
|
|
client,
|
|||
|
|
status: RelayStatus.disconnected()
|
|||
|
|
});
|
|||
|
|
(async () => {
|
|||
|
|
try {
|
|||
|
|
await connectionManager.cleanup();
|
|||
|
|
} catch {
|
|||
|
|
}
|
|||
|
|
})();
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@wizardconnect/core/dist/key-exchange.js
|
|||
|
|
import { generatePrivateKey, binToHex as binToHex3, hexToBin, binToBech32Padded, bech32PaddedToBin } from "@bitauth/libauth";
|
|||
|
|
function generateKeyExchangeCredentials() {
|
|||
|
|
const privateKey = generatePrivateKey();
|
|||
|
|
const privateKeyHex = binToHex3(privateKey);
|
|||
|
|
const publicKeyHex = deriveNostrPublicKey(privateKey);
|
|||
|
|
const secretBytes = generatePrivateKey();
|
|||
|
|
const secretShort = secretBytes.slice(0, 8);
|
|||
|
|
const secret = binToHex3(secretShort);
|
|||
|
|
return {
|
|||
|
|
privateKey: privateKeyHex,
|
|||
|
|
publicKey: publicKeyHex,
|
|||
|
|
secret
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
function encodeKeyExchangeURI(publicKey, secret, options = {}) {
|
|||
|
|
const publicKeyBin = hexToBin(publicKey);
|
|||
|
|
const secretBin = hexToBin(secret);
|
|||
|
|
if (publicKeyBin.length !== 32) {
|
|||
|
|
throw new Error(`Invalid public key length: expected 32 bytes, got ${publicKeyBin.length}`);
|
|||
|
|
}
|
|||
|
|
if (secretBin.length !== 8) {
|
|||
|
|
throw new Error(`Invalid secret length: expected 8 bytes, got ${secretBin.length}`);
|
|||
|
|
}
|
|||
|
|
const publicKeyBech32 = binToBech32Padded(publicKeyBin).toLowerCase();
|
|||
|
|
const secretBech32 = binToBech32Padded(secretBin).toLowerCase();
|
|||
|
|
const hostname = options.hostname || DEFAULT_RELAY_HOSTNAME;
|
|||
|
|
const port = options.port ?? DEFAULT_RELAY_PORT;
|
|||
|
|
const protocol = options.protocol || DEFAULT_RELAY_PROTOCOL;
|
|||
|
|
const isDefaultHostname = hostname === DEFAULT_RELAY_HOSTNAME;
|
|||
|
|
const defaultPort = protocol === "wss" ? 443 : 80;
|
|||
|
|
const isDefaultPort = port === defaultPort;
|
|||
|
|
const isDefaultProtocol = protocol === DEFAULT_RELAY_PROTOCOL;
|
|||
|
|
let uri;
|
|||
|
|
if (isDefaultHostname && isDefaultPort && isDefaultProtocol) {
|
|||
|
|
uri = `wiz://?p=${publicKeyBech32}&s=${secretBech32}`;
|
|||
|
|
} else {
|
|||
|
|
const portPart = isDefaultPort ? "" : `:${port}`;
|
|||
|
|
const authority = `${hostname}${portPart}`;
|
|||
|
|
uri = `wiz://${authority}?p=${publicKeyBech32}&s=${secretBech32}`;
|
|||
|
|
if (!isDefaultProtocol) {
|
|||
|
|
uri += `&pr=${protocol}`;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
const qrUri = uri.toUpperCase().replace("?", "%3F").replace(/=/g, "%3D").replace(/&/g, "%26");
|
|||
|
|
return { uri, qrUri };
|
|||
|
|
}
|
|||
|
|
function decodeKeyExchangeURI(uri) {
|
|||
|
|
let url;
|
|||
|
|
try {
|
|||
|
|
const lower = uri.toLowerCase();
|
|||
|
|
const isQr = lower.includes("%3f") && !lower.includes("?");
|
|||
|
|
const toParse = isQr ? lower.replace("%3f", "?").replace(/%3d/g, "=").replace(/%26/g, "&") : lower;
|
|||
|
|
url = new URL(toParse);
|
|||
|
|
} catch (error2) {
|
|||
|
|
throw new Error(`Invalid URI format: ${error2 instanceof Error ? error2.message : "unknown error"}`, { cause: error2 });
|
|||
|
|
}
|
|||
|
|
if (url.protocol !== "wiz:") {
|
|||
|
|
throw new Error("Invalid URI scheme. Expected: wiz://");
|
|||
|
|
}
|
|||
|
|
let hostname = url.hostname || DEFAULT_RELAY_HOSTNAME;
|
|||
|
|
let port;
|
|||
|
|
if (url.port) {
|
|||
|
|
const parsedPort = parseInt(url.port, 10);
|
|||
|
|
if (isNaN(parsedPort) || parsedPort < 1 || parsedPort > 65535) {
|
|||
|
|
throw new Error(`Invalid port number: ${url.port}`);
|
|||
|
|
}
|
|||
|
|
port = parsedPort;
|
|||
|
|
} else {
|
|||
|
|
port = DEFAULT_RELAY_PORT;
|
|||
|
|
}
|
|||
|
|
const publicKeyBech32 = url.searchParams.get("p");
|
|||
|
|
const secretBech32 = url.searchParams.get("s");
|
|||
|
|
if (!publicKeyBech32 || !secretBech32) {
|
|||
|
|
throw new Error("Invalid URI format. Missing required parameters: p (public key) or s (secret)");
|
|||
|
|
}
|
|||
|
|
const protocol = url.searchParams.get("pr");
|
|||
|
|
const publicKeyBech32Normalized = publicKeyBech32.toLowerCase();
|
|||
|
|
const secretBech32Normalized = secretBech32.toLowerCase();
|
|||
|
|
let publicKeyBin;
|
|||
|
|
let secretBin;
|
|||
|
|
try {
|
|||
|
|
const pubkeyResult = bech32PaddedToBin(publicKeyBech32Normalized);
|
|||
|
|
const secretResult = bech32PaddedToBin(secretBech32Normalized);
|
|||
|
|
if (pubkeyResult instanceof Uint8Array && secretResult instanceof Uint8Array) {
|
|||
|
|
publicKeyBin = pubkeyResult;
|
|||
|
|
secretBin = secretResult;
|
|||
|
|
} else {
|
|||
|
|
const pubkeyError = typeof pubkeyResult === "string" ? pubkeyResult : "Unknown error";
|
|||
|
|
const secretError = typeof secretResult === "string" ? secretResult : "Unknown error";
|
|||
|
|
throw new Error(`Bech32 decoding failed: pubkey=${pubkeyError}, secret=${secretError}`);
|
|||
|
|
}
|
|||
|
|
} catch (error2) {
|
|||
|
|
throw new Error(`Invalid bech32 encoding: ${error2 instanceof Error ? error2.message : "unknown error"}`, { cause: error2 });
|
|||
|
|
}
|
|||
|
|
if (publicKeyBin.length !== 32) {
|
|||
|
|
throw new Error(`Invalid public key length: expected 32 bytes, got ${publicKeyBin.length}`);
|
|||
|
|
}
|
|||
|
|
if (secretBin.length !== 8) {
|
|||
|
|
throw new Error(`Invalid secret length: expected 8 bytes, got ${secretBin.length}`);
|
|||
|
|
}
|
|||
|
|
if (protocol && protocol !== "ws" && protocol !== "wss") {
|
|||
|
|
throw new Error(`Invalid protocol: ${protocol}. Must be 'ws' or 'wss'`);
|
|||
|
|
}
|
|||
|
|
if (!url.port && protocol === "ws") {
|
|||
|
|
port = 80;
|
|||
|
|
}
|
|||
|
|
return {
|
|||
|
|
publicKey: binToHex3(publicKeyBin),
|
|||
|
|
secret: binToHex3(secretBin),
|
|||
|
|
hostname,
|
|||
|
|
port,
|
|||
|
|
protocol: protocol || DEFAULT_RELAY_PROTOCOL
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
var DEFAULT_RELAY_HOSTNAME, DEFAULT_RELAY_PORT, DEFAULT_RELAY_PROTOCOL, DEFAULT_RELAY_URLS;
|
|||
|
|
var init_key_exchange = __esm({
|
|||
|
|
"node_modules/@wizardconnect/core/dist/key-exchange.js"() {
|
|||
|
|
init_utilnostr();
|
|||
|
|
DEFAULT_RELAY_HOSTNAME = "relay.riften.net";
|
|||
|
|
DEFAULT_RELAY_PORT = 443;
|
|||
|
|
DEFAULT_RELAY_PROTOCOL = "wss";
|
|||
|
|
DEFAULT_RELAY_URLS = [
|
|||
|
|
"wss://relay.riften.net:443",
|
|||
|
|
"wss://relay.cauldron.quest:443"
|
|||
|
|
];
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@wizardconnect/core/dist/dapp-relay.js
|
|||
|
|
import { hexToBin as hexToBin2, binToHex as binToHex4 } from "@bitauth/libauth";
|
|||
|
|
function initiateDappRelay(statusCallback, options = {}) {
|
|||
|
|
const events = new import_index.default();
|
|||
|
|
let credentials;
|
|||
|
|
let dappPrivateKey;
|
|||
|
|
if (options.existingCredentials) {
|
|||
|
|
const privateKeyHex = options.existingCredentials.privateKey;
|
|||
|
|
if (privateKeyHex.length !== 64) {
|
|||
|
|
throw new Error("Private key must be 64 hex characters (32 bytes)");
|
|||
|
|
}
|
|||
|
|
dappPrivateKey = hexToBin2(privateKeyHex);
|
|||
|
|
const dappPublicKeyHex = deriveNostrPublicKey(dappPrivateKey);
|
|||
|
|
credentials = {
|
|||
|
|
privateKey: privateKeyHex,
|
|||
|
|
publicKey: dappPublicKeyHex,
|
|||
|
|
secret: options.existingCredentials.secret
|
|||
|
|
};
|
|||
|
|
} else {
|
|||
|
|
credentials = generateKeyExchangeCredentials();
|
|||
|
|
dappPrivateKey = hexToBin2(credentials.privateKey);
|
|||
|
|
}
|
|||
|
|
let uriOptions = {};
|
|||
|
|
if (options.explicitRelayUrls && options.explicitRelayUrls.length > 0) {
|
|||
|
|
const relayUrl = options.explicitRelayUrls[0];
|
|||
|
|
const hostMatch = relayUrl.match(/^wss?:\/\/([^:/]+)/);
|
|||
|
|
const portMatch = relayUrl.match(/:(\d+)/);
|
|||
|
|
if (hostMatch) {
|
|||
|
|
uriOptions.hostname = hostMatch[1];
|
|||
|
|
}
|
|||
|
|
if (portMatch) {
|
|||
|
|
uriOptions.port = parseInt(portMatch[1], 10);
|
|||
|
|
}
|
|||
|
|
if (relayUrl.startsWith("wss://")) {
|
|||
|
|
uriOptions.protocol = "wss";
|
|||
|
|
} else if (relayUrl.startsWith("ws://")) {
|
|||
|
|
uriOptions.protocol = "ws";
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
const { uri, qrUri } = encodeKeyExchangeURI(credentials.publicKey, credentials.secret, uriOptions);
|
|||
|
|
let relayClient = null;
|
|||
|
|
let keyExchanged = false;
|
|||
|
|
let walletPublicKeyNostr = null;
|
|||
|
|
if (options.existingCredentials) {
|
|||
|
|
walletPublicKeyNostr = hexToBin2(options.existingCredentials.walletPublicKey);
|
|||
|
|
keyExchanged = true;
|
|||
|
|
}
|
|||
|
|
const wrappedCallback = (payload) => {
|
|||
|
|
if (!relayClient) {
|
|||
|
|
relayClient = payload.client;
|
|||
|
|
relayClient.on("message", async (message) => {
|
|||
|
|
if (message.action === RelayMsgAction.WalletReady) {
|
|||
|
|
const walletReady = message;
|
|||
|
|
if (walletReady.secret !== credentials.secret) {
|
|||
|
|
if (!keyExchanged) {
|
|||
|
|
error(Scope.Relay, "Key exchange failed: secret mismatch");
|
|||
|
|
}
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
const receivedWalletKey = hexToBin2(walletReady.public_key);
|
|||
|
|
if (receivedWalletKey.length !== 32) {
|
|||
|
|
error(Scope.Relay, "Invalid wallet public key length");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (keyExchanged && walletPublicKeyNostr) {
|
|||
|
|
if (binToHex4(receivedWalletKey) !== binToHex4(walletPublicKeyNostr)) {
|
|||
|
|
warn(Scope.Relay, "Different wallet connected (different public key)");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
walletPublicKeyNostr = receivedWalletKey;
|
|||
|
|
relayClient.setPairedPublicKey(receivedWalletKey);
|
|||
|
|
if (!keyExchanged) {
|
|||
|
|
keyExchanged = true;
|
|||
|
|
events.emit("keyexchangecomplete", receivedWalletKey);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
if (payload.status.status === "connected") {
|
|||
|
|
if (walletPublicKeyNostr && relayClient) {
|
|||
|
|
relayClient.setPairedPublicKey(walletPublicKeyNostr);
|
|||
|
|
keyExchanged = true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
statusCallback(payload);
|
|||
|
|
};
|
|||
|
|
const relayUrls = options.explicitRelayUrls && options.explicitRelayUrls.length > 0 ? options.explicitRelayUrls : [...DEFAULT_RELAY_URLS];
|
|||
|
|
const cleanup = initiateRelay(wrappedCallback, dappPrivateKey, walletPublicKeyNostr ?? new Uint8Array(33), {
|
|||
|
|
explicitRelayUrls: relayUrls,
|
|||
|
|
reconnectInterval: options.reconnectInterval,
|
|||
|
|
maxReconnectAttempts: options.maxReconnectAttempts
|
|||
|
|
});
|
|||
|
|
return {
|
|||
|
|
client: relayClient,
|
|||
|
|
uri,
|
|||
|
|
qrUri,
|
|||
|
|
credentials,
|
|||
|
|
events,
|
|||
|
|
cleanup
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
var init_dapp_relay = __esm({
|
|||
|
|
"node_modules/@wizardconnect/core/dist/dapp-relay.js"() {
|
|||
|
|
init_relay_handler();
|
|||
|
|
init_hdwalletv1();
|
|||
|
|
init_key_exchange();
|
|||
|
|
init_eventemitter3();
|
|||
|
|
init_utilnostr();
|
|||
|
|
init_log();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@wizardconnect/core/dist/wallet-relay.js
|
|||
|
|
import { hexToBin as hexToBin3 } from "@bitauth/libauth";
|
|||
|
|
function initiateWalletRelay(statusCallback, options) {
|
|||
|
|
let decoded;
|
|||
|
|
try {
|
|||
|
|
decoded = decodeKeyExchangeURI(options.uri);
|
|||
|
|
} catch (err) {
|
|||
|
|
throw new Error(`Failed to decode connection URI: ${err.message}`, {
|
|||
|
|
cause: err
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
const dappPublicKeyHex = decoded.publicKey;
|
|||
|
|
const secret = decoded.secret;
|
|||
|
|
const dappPublicKeyNostr = hexToBin3(dappPublicKeyHex);
|
|||
|
|
const hostname = decoded.hostname;
|
|||
|
|
const protocol = decoded.protocol;
|
|||
|
|
const port = decoded.port;
|
|||
|
|
const relayUrl = `${protocol}://${hostname}:${port}`;
|
|||
|
|
const walletPublicKeyNostr = deriveNostrPublicKeyBytes(options.walletPrivateKey);
|
|||
|
|
let relayClient = null;
|
|||
|
|
const wrappedCallback = (payload) => {
|
|||
|
|
if (!relayClient) {
|
|||
|
|
relayClient = payload.client;
|
|||
|
|
}
|
|||
|
|
if (payload.status.status === "connected") {
|
|||
|
|
relayClient.setPairedPublicKey(dappPublicKeyNostr);
|
|||
|
|
}
|
|||
|
|
statusCallback(payload);
|
|||
|
|
};
|
|||
|
|
const relayUrls = [relayUrl];
|
|||
|
|
const isDefaultRelay = DEFAULT_RELAY_URLS.includes(relayUrl);
|
|||
|
|
if (isDefaultRelay) {
|
|||
|
|
for (const defaultUrl of DEFAULT_RELAY_URLS) {
|
|||
|
|
if (!relayUrls.includes(defaultUrl)) {
|
|||
|
|
relayUrls.push(defaultUrl);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (options.explicitRelayUrls && options.explicitRelayUrls.length > 0) {
|
|||
|
|
for (const explicitUrl of options.explicitRelayUrls) {
|
|||
|
|
if (!relayUrls.includes(explicitUrl)) {
|
|||
|
|
relayUrls.push(explicitUrl);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
const cleanup = initiateRelay(wrappedCallback, options.walletPrivateKey, dappPublicKeyNostr, {
|
|||
|
|
explicitRelayUrls: relayUrls,
|
|||
|
|
reconnectInterval: options.reconnectInterval,
|
|||
|
|
maxReconnectAttempts: options.maxReconnectAttempts
|
|||
|
|
});
|
|||
|
|
const result = {
|
|||
|
|
get client() {
|
|||
|
|
if (!relayClient) {
|
|||
|
|
throw new Error("Relay client not yet initialized. Wait for connection status callback.");
|
|||
|
|
}
|
|||
|
|
return relayClient;
|
|||
|
|
},
|
|||
|
|
dappPublicKey: dappPublicKeyNostr,
|
|||
|
|
walletPublicKey: walletPublicKeyNostr,
|
|||
|
|
secret,
|
|||
|
|
cleanup
|
|||
|
|
};
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
var init_wallet_relay = __esm({
|
|||
|
|
"node_modules/@wizardconnect/core/dist/wallet-relay.js"() {
|
|||
|
|
init_relay_handler();
|
|||
|
|
init_key_exchange();
|
|||
|
|
init_utilnostr();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@wizardconnect/core/dist/protocols/base.js
|
|||
|
|
function isDappReadyMessage(msg) {
|
|||
|
|
return isProtocolMessage(msg) && msg.action === RelayMsgAction.DappReady && Array.isArray(msg.supported_protocols) && typeof msg.wallet_discovered === "boolean";
|
|||
|
|
}
|
|||
|
|
function isWalletReadyMessage(msg) {
|
|||
|
|
const m = msg;
|
|||
|
|
return isProtocolMessage(msg) && msg.action === RelayMsgAction.WalletReady && typeof m.wallet_name === "string" && typeof m.wallet_icon === "string" && typeof m.dapp_discovered === "boolean" && Array.isArray(m.supported_protocols) && m.session !== null && typeof m.session === "object" && typeof m.public_key === "string" && typeof m.secret === "string";
|
|||
|
|
}
|
|||
|
|
function isDisconnectMessage(msg) {
|
|||
|
|
return isProtocolMessage(msg) && msg.action === RelayMsgAction.Disconnect && typeof msg.reason === "string";
|
|||
|
|
}
|
|||
|
|
var DisconnectReason;
|
|||
|
|
var init_base2 = __esm({
|
|||
|
|
"node_modules/@wizardconnect/core/dist/protocols/base.js"() {
|
|||
|
|
init_hdwalletv1();
|
|||
|
|
(function(DisconnectReason2) {
|
|||
|
|
DisconnectReason2["ProtocolMismatch"] = "protocol_mismatch";
|
|||
|
|
DisconnectReason2["UserDisconnect"] = "user_disconnect";
|
|||
|
|
})(DisconnectReason || (DisconnectReason = {}));
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@wizardconnect/core/dist/serialize.js
|
|||
|
|
import { hexToBin as hexToBin4 } from "@bitauth/libauth";
|
|||
|
|
function parseExtendedJson(jsonString) {
|
|||
|
|
return JSON.parse(jsonString, (_key, value) => {
|
|||
|
|
if (typeof value === "string") {
|
|||
|
|
const bigintMatch = value.match(BIGINT_RE);
|
|||
|
|
if (bigintMatch)
|
|||
|
|
return BigInt(bigintMatch[1]);
|
|||
|
|
const uint8Match = value.match(UINT8_RE);
|
|||
|
|
if (uint8Match)
|
|||
|
|
return hexToBin4(uint8Match[1]);
|
|||
|
|
}
|
|||
|
|
return value;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
function isExtendedJsonFormat(str) {
|
|||
|
|
return UINT8_RE.test(str) || BIGINT_RE.test(str);
|
|||
|
|
}
|
|||
|
|
function parseExtendedJsonValue(value) {
|
|||
|
|
const bigintMatch = value.match(BIGINT_RE);
|
|||
|
|
if (bigintMatch)
|
|||
|
|
return BigInt(bigintMatch[1]);
|
|||
|
|
const uint8Match = value.match(UINT8_RE);
|
|||
|
|
if (uint8Match)
|
|||
|
|
return hexToBin4(uint8Match[1]);
|
|||
|
|
return value;
|
|||
|
|
}
|
|||
|
|
function toUint8Array(value) {
|
|||
|
|
if (value instanceof Uint8Array)
|
|||
|
|
return value;
|
|||
|
|
if (isExtendedJsonFormat(value))
|
|||
|
|
return parseExtendedJsonValue(value);
|
|||
|
|
return hexToBin4(value);
|
|||
|
|
}
|
|||
|
|
function toBigInt(value) {
|
|||
|
|
if (typeof value === "bigint")
|
|||
|
|
return value;
|
|||
|
|
if (typeof value === "string") {
|
|||
|
|
if (isExtendedJsonFormat(value))
|
|||
|
|
return parseExtendedJsonValue(value);
|
|||
|
|
return BigInt(value);
|
|||
|
|
}
|
|||
|
|
return BigInt(value);
|
|||
|
|
}
|
|||
|
|
var BIGINT_RE, UINT8_RE;
|
|||
|
|
var init_serialize = __esm({
|
|||
|
|
"node_modules/@wizardconnect/core/dist/serialize.js"() {
|
|||
|
|
BIGINT_RE = /^<bigint: (?<bigint>[0-9]*)n>$/;
|
|||
|
|
UINT8_RE = /^<Uint8Array: 0x(?<hex>[0-9a-f]*)>$/u;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@wizardconnect/core/dist/index.js
|
|||
|
|
var dist_exports = {};
|
|||
|
|
__export(dist_exports, {
|
|||
|
|
CHUNK_EXTENSION_NAME: () => CHUNK_EXTENSION_NAME,
|
|||
|
|
CHUNK_EXTENSION_VERSION: () => CHUNK_EXTENSION_VERSION,
|
|||
|
|
DEFAULT_RELAY_HOSTNAME: () => DEFAULT_RELAY_HOSTNAME,
|
|||
|
|
DEFAULT_RELAY_PORT: () => DEFAULT_RELAY_PORT,
|
|||
|
|
DEFAULT_RELAY_PROTOCOL: () => DEFAULT_RELAY_PROTOCOL,
|
|||
|
|
DEFAULT_RELAY_URLS: () => DEFAULT_RELAY_URLS,
|
|||
|
|
DisconnectReason: () => DisconnectReason,
|
|||
|
|
PATH_CHANGE: () => PATH_CHANGE,
|
|||
|
|
PATH_DEFI: () => PATH_DEFI,
|
|||
|
|
PATH_RECEIVE: () => PATH_RECEIVE,
|
|||
|
|
PROTOCOL_NAME: () => PROTOCOL_NAME,
|
|||
|
|
RelayClient: () => RelayClient,
|
|||
|
|
RelayMsgAction: () => RelayMsgAction,
|
|||
|
|
RelayStatus: () => RelayStatus,
|
|||
|
|
SimplePool: () => SimplePool,
|
|||
|
|
bech32PaddedToBin: () => bech32PaddedToBin2,
|
|||
|
|
binToBech32Padded: () => binToBech32Padded2,
|
|||
|
|
binToHex: () => binToHex5,
|
|||
|
|
childIndexOfPathName: () => childIndexOfPathName,
|
|||
|
|
chunkExtensionAdvertisement: () => chunkExtensionAdvertisement,
|
|||
|
|
decodeKeyExchangeURI: () => decodeKeyExchangeURI,
|
|||
|
|
encodeKeyExchangeURI: () => encodeKeyExchangeURI,
|
|||
|
|
generateKeyExchangeCredentials: () => generateKeyExchangeCredentials,
|
|||
|
|
hexToBin: () => hexToBin5,
|
|||
|
|
initiateDappRelay: () => initiateDappRelay,
|
|||
|
|
initiateRelay: () => initiateRelay,
|
|||
|
|
initiateWalletRelay: () => initiateWalletRelay,
|
|||
|
|
isChunkMessage: () => isChunkMessage,
|
|||
|
|
isDappReadyMessage: () => isDappReadyMessage,
|
|||
|
|
isDisconnectMessage: () => isDisconnectMessage,
|
|||
|
|
isErrorMessage: () => isErrorMessage,
|
|||
|
|
isExtendedJsonFormat: () => isExtendedJsonFormat,
|
|||
|
|
isHdwalletv1Session: () => isHdwalletv1Session,
|
|||
|
|
isPathXpub: () => isPathXpub,
|
|||
|
|
isProtocolMessage: () => isProtocolMessage,
|
|||
|
|
isSignCancelMessage: () => isSignCancelMessage,
|
|||
|
|
isSignTransactionRequest: () => isSignTransactionRequest,
|
|||
|
|
isWalletReadyMessage: () => isWalletReadyMessage,
|
|||
|
|
parseExtendedJson: () => parseExtendedJson,
|
|||
|
|
parseExtendedJsonValue: () => parseExtendedJsonValue,
|
|||
|
|
peerSupportsChunk: () => peerSupportsChunk,
|
|||
|
|
toBigInt: () => toBigInt,
|
|||
|
|
toUint8Array: () => toUint8Array
|
|||
|
|
});
|
|||
|
|
import { binToHex as binToHex5, hexToBin as hexToBin5, binToBech32Padded as binToBech32Padded2, bech32PaddedToBin as bech32PaddedToBin2 } from "@bitauth/libauth";
|
|||
|
|
var init_dist = __esm({
|
|||
|
|
"node_modules/@wizardconnect/core/dist/index.js"() {
|
|||
|
|
init_relay_client();
|
|||
|
|
init_pool();
|
|||
|
|
init_relay_handler();
|
|||
|
|
init_dapp_relay();
|
|||
|
|
init_wallet_relay();
|
|||
|
|
init_key_exchange();
|
|||
|
|
init_hdwalletv1();
|
|||
|
|
init_base2();
|
|||
|
|
init_chunk();
|
|||
|
|
init_serialize();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@wizardconnect/dapp/dist/pubkey-state-manager.js
|
|||
|
|
import { deriveHdPublicNodeChild } from "@bitauth/libauth";
|
|||
|
|
var DappPubkeyStateManager;
|
|||
|
|
var init_pubkey_state_manager = __esm({
|
|||
|
|
"node_modules/@wizardconnect/dapp/dist/pubkey-state-manager.js"() {
|
|||
|
|
DappPubkeyStateManager = class {
|
|||
|
|
// xpub nodes for on-demand pubkey derivation
|
|||
|
|
xpubNodes = /* @__PURE__ */ new Map();
|
|||
|
|
/** Derive a pubkey on demand from the stored xpub node. */
|
|||
|
|
getPubkey(childIndex, index) {
|
|||
|
|
const xpubNode = this.xpubNodes.get(childIndex);
|
|||
|
|
if (!xpubNode)
|
|||
|
|
return void 0;
|
|||
|
|
const child = deriveHdPublicNodeChild(xpubNode, Number(index));
|
|||
|
|
if (typeof child === "string")
|
|||
|
|
return void 0;
|
|||
|
|
return child.publicKey;
|
|||
|
|
}
|
|||
|
|
/** Returns true if an xpub node is available for this child index. */
|
|||
|
|
hasPath(childIndex) {
|
|||
|
|
return this.xpubNodes.has(childIndex);
|
|||
|
|
}
|
|||
|
|
setXpubNode(childIndex, node) {
|
|||
|
|
this.xpubNodes.set(childIndex, node);
|
|||
|
|
}
|
|||
|
|
getXpubNode(childIndex) {
|
|||
|
|
return this.xpubNodes.get(childIndex);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@wizardconnect/dapp/dist/session.js
|
|||
|
|
function defaultStorage() {
|
|||
|
|
if (typeof localStorage !== "undefined" && typeof localStorage.getItem === "function")
|
|||
|
|
return localStorage;
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
function resolveStorage(storage) {
|
|||
|
|
return storage ?? defaultStorage();
|
|||
|
|
}
|
|||
|
|
function loadSession(key = DEFAULT_SESSION_KEY, storage) {
|
|||
|
|
const s = resolveStorage(storage);
|
|||
|
|
if (!s)
|
|||
|
|
return null;
|
|||
|
|
const raw = s.getItem(key);
|
|||
|
|
if (!raw)
|
|||
|
|
return null;
|
|||
|
|
try {
|
|||
|
|
const parsed = JSON.parse(raw);
|
|||
|
|
if (!parsed.privateKey || !parsed.secret)
|
|||
|
|
return null;
|
|||
|
|
return parsed;
|
|||
|
|
} catch {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
function saveSession(key = DEFAULT_SESSION_KEY, data, storage) {
|
|||
|
|
const s = resolveStorage(storage);
|
|||
|
|
if (!s)
|
|||
|
|
return;
|
|||
|
|
const existing = loadSession(key, s);
|
|||
|
|
const merged = { ...existing, ...data };
|
|||
|
|
s.setItem(key, JSON.stringify(merged));
|
|||
|
|
}
|
|||
|
|
function clearSession(key = DEFAULT_SESSION_KEY, storage) {
|
|||
|
|
const s = resolveStorage(storage);
|
|||
|
|
if (!s)
|
|||
|
|
return;
|
|||
|
|
s.removeItem(key);
|
|||
|
|
}
|
|||
|
|
var DEFAULT_SESSION_KEY;
|
|||
|
|
var init_session = __esm({
|
|||
|
|
"node_modules/@wizardconnect/dapp/dist/session.js"() {
|
|||
|
|
DEFAULT_SESSION_KEY = "wizardconnect-session";
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@wizardconnect/dapp/dist/dapp-connection-manager.js
|
|||
|
|
import { decodeHdPublicKey } from "@bitauth/libauth";
|
|||
|
|
var DappConnectionManager;
|
|||
|
|
var init_dapp_connection_manager = __esm({
|
|||
|
|
"node_modules/@wizardconnect/dapp/dist/dapp-connection-manager.js"() {
|
|||
|
|
init_eventemitter3();
|
|||
|
|
init_dist();
|
|||
|
|
init_pubkey_state_manager();
|
|||
|
|
init_session();
|
|||
|
|
DappConnectionManager = class extends import_index.default {
|
|||
|
|
dappName;
|
|||
|
|
dappIcon;
|
|||
|
|
conn = null;
|
|||
|
|
listenerAttached = false;
|
|||
|
|
/** Pubkey state — exposed for callers that need to query by index. */
|
|||
|
|
pubkeyState;
|
|||
|
|
walletName = null;
|
|||
|
|
walletIcon = null;
|
|||
|
|
protocol = null;
|
|||
|
|
/** Protocols this dapp supports, in preference order. */
|
|||
|
|
supportedProtocols = [PROTOCOL_NAME];
|
|||
|
|
walletDiscovered = false;
|
|||
|
|
disconnectGraceTimer = null;
|
|||
|
|
pingInterval = null;
|
|||
|
|
/** Timestamp (ms) of the last pong or wallet_ready received. Used for liveness detection. */
|
|||
|
|
lastPongTime = 0;
|
|||
|
|
sessionPaths = [];
|
|||
|
|
pendingSignatureRequests = /* @__PURE__ */ new Map();
|
|||
|
|
sessionOptions = null;
|
|||
|
|
/**
|
|||
|
|
* @param dappName Optional display name of the dapp (sent in dapp_ready).
|
|||
|
|
* @param dappIcon Optional icon URL/data-URI of the dapp (sent in dapp_ready).
|
|||
|
|
* @param options Optional configuration. Session persistence is enabled by
|
|||
|
|
* default (key: "wizardconnect-session", storage: localStorage).
|
|||
|
|
* Pass `session: false` to disable.
|
|||
|
|
*/
|
|||
|
|
constructor(dappName, dappIcon, options) {
|
|||
|
|
super();
|
|||
|
|
this.dappName = dappName;
|
|||
|
|
this.dappIcon = dappIcon;
|
|||
|
|
this.pubkeyState = new DappPubkeyStateManager();
|
|||
|
|
if (options?.session !== false) {
|
|||
|
|
const sessionConf = options?.session ?? {};
|
|||
|
|
this.sessionOptions = {
|
|||
|
|
key: sessionConf.key ?? DEFAULT_SESSION_KEY,
|
|||
|
|
storage: sessionConf.storage
|
|||
|
|
};
|
|||
|
|
const stored = loadSession(this.sessionOptions.key, this.sessionOptions.storage);
|
|||
|
|
if (stored) {
|
|||
|
|
if (stored.walletName)
|
|||
|
|
this.walletName = stored.walletName;
|
|||
|
|
if (stored.walletIcon)
|
|||
|
|
this.walletIcon = stored.walletIcon;
|
|||
|
|
if (stored.paths?.length) {
|
|||
|
|
try {
|
|||
|
|
this.restoreSessionPaths(stored.paths);
|
|||
|
|
} catch {
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// --- Session persistence (public API) ----------------------------------------
|
|||
|
|
/**
|
|||
|
|
* Attach a relay result from `initiateDappRelay()`. Automatically:
|
|||
|
|
* - Saves relay credentials (privateKey, secret) to the session
|
|||
|
|
* - Listens for `keyexchangecomplete` and saves the wallet public key
|
|||
|
|
*
|
|||
|
|
* No-op if session persistence is disabled.
|
|||
|
|
*/
|
|||
|
|
attachRelay(relay) {
|
|||
|
|
if (!this.sessionOptions)
|
|||
|
|
return;
|
|||
|
|
saveSession(this.sessionOptions.key, {
|
|||
|
|
privateKey: relay.credentials.privateKey,
|
|||
|
|
secret: relay.credentials.secret
|
|||
|
|
}, this.sessionOptions.storage);
|
|||
|
|
relay.events.on("keyexchangecomplete", (walletPublicKey) => {
|
|||
|
|
if (!this.sessionOptions)
|
|||
|
|
return;
|
|||
|
|
saveSession(this.sessionOptions.key, { walletPublicKey: binToHex5(walletPublicKey) }, this.sessionOptions.storage);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Load the stored session (e.g. for reconnection).
|
|||
|
|
* Returns null if session persistence is disabled or no session exists.
|
|||
|
|
*/
|
|||
|
|
loadStoredSession() {
|
|||
|
|
if (!this.sessionOptions)
|
|||
|
|
return null;
|
|||
|
|
return loadSession(this.sessionOptions.key, this.sessionOptions.storage);
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Clear the stored session. Call on disconnect.
|
|||
|
|
* No-op if session persistence is disabled.
|
|||
|
|
*/
|
|||
|
|
clearStoredSession() {
|
|||
|
|
if (!this.sessionOptions)
|
|||
|
|
return;
|
|||
|
|
clearSession(this.sessionOptions.key, this.sessionOptions.storage);
|
|||
|
|
}
|
|||
|
|
// --- Relay connection -------------------------------------------------------
|
|||
|
|
/**
|
|||
|
|
* Call this from the RelayStatusCallback passed to `initiateDappRelay`.
|
|||
|
|
* Attaches the message listener exactly once and re-sends dapp_ready
|
|||
|
|
* each time the connection is established (handles reconnects).
|
|||
|
|
*/
|
|||
|
|
updateConnection(client, status) {
|
|||
|
|
if (client) {
|
|||
|
|
if (!this.listenerAttached) {
|
|||
|
|
this.listenerAttached = true;
|
|||
|
|
client.on("message", (msg) => this.handleMessage(msg));
|
|||
|
|
}
|
|||
|
|
this.conn = client;
|
|||
|
|
}
|
|||
|
|
if (status.status === "connected" && this.conn) {
|
|||
|
|
this.onConnected();
|
|||
|
|
} else if (status.status === "reconnecting" || status.status === "disconnected") {
|
|||
|
|
this.stopPingInterval();
|
|||
|
|
if (status.status === "reconnecting") {
|
|||
|
|
this.emit("reconnecting");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
isWalletDiscovered() {
|
|||
|
|
return this.walletDiscovered;
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Get a sequence number from the relay client.
|
|||
|
|
* Use this to populate the `sequence` field of a SignTransactionRequest.
|
|||
|
|
*/
|
|||
|
|
nextSequence() {
|
|||
|
|
if (!this.conn)
|
|||
|
|
throw new Error("[wizardconnect/dapp] Not connected");
|
|||
|
|
return this.conn.nextSequence();
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Send a sign transaction request and wait for the wallet's response.
|
|||
|
|
* The caller is responsible for creating the full SignTransactionRequest
|
|||
|
|
* (including sequence from `nextSequence()`).
|
|||
|
|
*/
|
|||
|
|
async sendSignRequest(request) {
|
|||
|
|
if (!this.conn)
|
|||
|
|
throw new Error("[wizardconnect/dapp] Not connected");
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
this.pendingSignatureRequests.set(request.sequence, {
|
|||
|
|
request,
|
|||
|
|
resolve,
|
|||
|
|
reject
|
|||
|
|
});
|
|||
|
|
this.conn.relay(request).then(() => {
|
|||
|
|
this.emit("messagesent", request);
|
|||
|
|
}).catch((err) => {
|
|||
|
|
this.pendingSignatureRequests.delete(request.sequence);
|
|||
|
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Cancel an in-flight sign request.
|
|||
|
|
* Immediately rejects the pending Promise and sends sign_cancel to the wallet.
|
|||
|
|
*/
|
|||
|
|
async sendSignCancel(sequence, reason) {
|
|||
|
|
const handlers = this.pendingSignatureRequests.get(sequence);
|
|||
|
|
if (handlers) {
|
|||
|
|
this.pendingSignatureRequests.delete(sequence);
|
|||
|
|
handlers.reject(new Error(reason ?? "Sign request cancelled"));
|
|||
|
|
}
|
|||
|
|
if (!this.conn)
|
|||
|
|
return;
|
|||
|
|
const msg = {
|
|||
|
|
action: RelayMsgAction.SignCancel,
|
|||
|
|
sequence,
|
|||
|
|
...reason !== void 0 && { reason },
|
|||
|
|
time: Math.floor(Date.now() / 1e3)
|
|||
|
|
};
|
|||
|
|
await this.conn.relay(msg);
|
|||
|
|
this.emit("messagesent", msg);
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Send a disconnect message to the wallet (courtesy notification).
|
|||
|
|
* The caller is responsible for calling dappRelay.cleanup() afterwards.
|
|||
|
|
*/
|
|||
|
|
async sendDisconnect(message) {
|
|||
|
|
if (!this.conn)
|
|||
|
|
return;
|
|||
|
|
const msg = {
|
|||
|
|
action: RelayMsgAction.Disconnect,
|
|||
|
|
reason: DisconnectReason.UserDisconnect,
|
|||
|
|
time: Math.floor(Date.now() / 1e3),
|
|||
|
|
...message !== void 0 && { message }
|
|||
|
|
};
|
|||
|
|
await this.conn.relay(msg);
|
|||
|
|
this.emit("messagesent", msg);
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Convenience method: build and send a sign transaction request.
|
|||
|
|
* Auto-fills `action`, `sequence`, and `time`. Supports cancellation via
|
|||
|
|
* AbortSignal — when aborted, sendSignCancel is called automatically.
|
|||
|
|
*/
|
|||
|
|
async signTransaction(request, options) {
|
|||
|
|
const sequence = this.nextSequence();
|
|||
|
|
const fullRequest = {
|
|||
|
|
action: RelayMsgAction.SignTransactionRequest,
|
|||
|
|
time: Math.floor(Date.now() / 1e3),
|
|||
|
|
sequence,
|
|||
|
|
...request
|
|||
|
|
};
|
|||
|
|
const signPromise = this.sendSignRequest(fullRequest);
|
|||
|
|
if (!options?.signal)
|
|||
|
|
return signPromise;
|
|||
|
|
signPromise.catch(() => {
|
|||
|
|
});
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
const onAbort = () => {
|
|||
|
|
const reason = options.signal.reason instanceof Error ? options.signal.reason.message : typeof options.signal.reason === "string" ? options.signal.reason : "Sign request cancelled";
|
|||
|
|
this.sendSignCancel(sequence, reason).catch(() => {
|
|||
|
|
});
|
|||
|
|
reject(new DOMException(reason, "AbortError"));
|
|||
|
|
};
|
|||
|
|
if (options.signal.aborted) {
|
|||
|
|
onAbort();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
options.signal.addEventListener("abort", onAbort, { once: true });
|
|||
|
|
signPromise.then(resolve).catch(reject).finally(() => {
|
|||
|
|
options.signal.removeEventListener("abort", onAbort);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
// --- Pubkey state delegation ------------------------------------------------
|
|||
|
|
// Convenience methods that forward to pubkeyState.
|
|||
|
|
getPubkey(childIndex, index) {
|
|||
|
|
return this.pubkeyState.getPubkey(childIndex, index);
|
|||
|
|
}
|
|||
|
|
/** Returns true if an xpub node is available for this child index. */
|
|||
|
|
hasPath(childIndex) {
|
|||
|
|
return this.pubkeyState.hasPath(childIndex);
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Returns the stored xpub node for the given child index.
|
|||
|
|
* Available after wallet_ready is received.
|
|||
|
|
*/
|
|||
|
|
getXpubNode(childIndex) {
|
|||
|
|
return this.pubkeyState.getXpubNode(childIndex);
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Returns the raw PathXpub entries received from the wallet.
|
|||
|
|
* Available after wallet_ready is received.
|
|||
|
|
*/
|
|||
|
|
getSessionPaths() {
|
|||
|
|
return [...this.sessionPaths];
|
|||
|
|
}
|
|||
|
|
/**
|
|||
|
|
* Restore session paths from a previous session (e.g. from localStorage).
|
|||
|
|
* Decodes xpub strings and populates pubkeyState so getPubkey() works
|
|||
|
|
* without waiting for wallet_ready.
|
|||
|
|
*/
|
|||
|
|
restoreSessionPaths(paths) {
|
|||
|
|
this.sessionPaths = [...paths];
|
|||
|
|
for (const pathInfo of paths) {
|
|||
|
|
const decoded = decodeHdPublicKey(pathInfo.xpub);
|
|||
|
|
if (typeof decoded === "string") {
|
|||
|
|
throw new Error(`[wizardconnect/dapp] Invalid xpub for path "${pathInfo.name}": ${decoded}`);
|
|||
|
|
}
|
|||
|
|
const ci = childIndexOfPathName(pathInfo.name);
|
|||
|
|
if (ci !== void 0) {
|
|||
|
|
this.pubkeyState.setXpubNode(ci, decoded.node);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// --- Private protocol handling -------------------------------------------
|
|||
|
|
onConnected() {
|
|||
|
|
(async () => {
|
|||
|
|
const deadline = Date.now() + 3e4;
|
|||
|
|
while (this.conn && !this.conn.isKeyExchangeComplete()) {
|
|||
|
|
if (Date.now() >= deadline) {
|
|||
|
|
console.error("[wizardconnect/dapp] Key exchange timed out");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
await new Promise((r) => setTimeout(r, 100));
|
|||
|
|
}
|
|||
|
|
if (!this.conn)
|
|||
|
|
return;
|
|||
|
|
await this.pushDappReady();
|
|||
|
|
})().catch((e) => console.error("[wizardconnect/dapp] Error in onConnected:", e));
|
|||
|
|
}
|
|||
|
|
startPingInterval() {
|
|||
|
|
this.stopPingInterval();
|
|||
|
|
this.lastPongTime = Date.now();
|
|||
|
|
this.pingInterval = setInterval(() => {
|
|||
|
|
if (!this.conn || !this.walletDiscovered)
|
|||
|
|
return;
|
|||
|
|
if (Date.now() - this.lastPongTime > 75e3) {
|
|||
|
|
this.stopPingInterval();
|
|||
|
|
this.emit("reconnecting");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
const ping = {
|
|||
|
|
action: RelayMsgAction.Ping,
|
|||
|
|
time: Math.floor(Date.now() / 1e3)
|
|||
|
|
};
|
|||
|
|
this.conn.relay(ping).catch(() => {
|
|||
|
|
});
|
|||
|
|
}, 1e4);
|
|||
|
|
}
|
|||
|
|
stopPingInterval() {
|
|||
|
|
if (this.pingInterval !== null) {
|
|||
|
|
clearInterval(this.pingInterval);
|
|||
|
|
this.pingInterval = null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
destroy() {
|
|||
|
|
this.stopPingInterval();
|
|||
|
|
if (this.disconnectGraceTimer !== null) {
|
|||
|
|
clearTimeout(this.disconnectGraceTimer);
|
|||
|
|
this.disconnectGraceTimer = null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
async pushDappReady() {
|
|||
|
|
if (!this.conn)
|
|||
|
|
return;
|
|||
|
|
const msg = {
|
|||
|
|
action: RelayMsgAction.DappReady,
|
|||
|
|
supported_protocols: this.supportedProtocols,
|
|||
|
|
wallet_discovered: this.walletDiscovered,
|
|||
|
|
time: Math.floor(Date.now() / 1e3),
|
|||
|
|
// Include selected_protocol on the reactive send (after the dapp has seen the wallet)
|
|||
|
|
...this.walletDiscovered && this.protocol && { selected_protocol: this.protocol },
|
|||
|
|
...this.dappName !== void 0 && { dapp_name: this.dappName },
|
|||
|
|
...this.dappIcon !== void 0 && { dapp_icon: this.dappIcon },
|
|||
|
|
// Transport-level: advertise chunking so the wallet can send large
|
|||
|
|
// SignTransactionResponses (signed tx hex can reach ~2 MB) that exceed
|
|||
|
|
// NIP-44's plaintext ceiling.
|
|||
|
|
extensions: { chunk: chunkExtensionAdvertisement() }
|
|||
|
|
};
|
|||
|
|
await this.conn.relay(msg);
|
|||
|
|
this.emit("messagesent", msg);
|
|||
|
|
}
|
|||
|
|
handleMessage(msg) {
|
|||
|
|
this.emit("messagereceived", msg);
|
|||
|
|
switch (msg.action) {
|
|||
|
|
case RelayMsgAction.WalletReady:
|
|||
|
|
this.handleWalletReady(msg).catch((e) => console.error("[wizardconnect/dapp] Error handling wallet_ready:", e));
|
|||
|
|
break;
|
|||
|
|
case RelayMsgAction.SignTransactionResponse:
|
|||
|
|
this.handleSignTransactionResponse(msg);
|
|||
|
|
break;
|
|||
|
|
case RelayMsgAction.Disconnect:
|
|||
|
|
this.handleRemoteDisconnect(msg);
|
|||
|
|
break;
|
|||
|
|
case RelayMsgAction.Pong:
|
|||
|
|
this.lastPongTime = Date.now();
|
|||
|
|
break;
|
|||
|
|
case RelayMsgAction.DappReady:
|
|||
|
|
break;
|
|||
|
|
default:
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
handleRemoteDisconnect(msg) {
|
|||
|
|
if (this.disconnectGraceTimer !== null) {
|
|||
|
|
clearTimeout(this.disconnectGraceTimer);
|
|||
|
|
}
|
|||
|
|
this.disconnectGraceTimer = setTimeout(() => {
|
|||
|
|
this.disconnectGraceTimer = null;
|
|||
|
|
this.stopPingInterval();
|
|||
|
|
this.emit("disconnect", msg.reason, msg.message);
|
|||
|
|
}, 8e3);
|
|||
|
|
}
|
|||
|
|
async handleWalletReady(msg) {
|
|||
|
|
if (this.disconnectGraceTimer !== null) {
|
|||
|
|
clearTimeout(this.disconnectGraceTimer);
|
|||
|
|
this.disconnectGraceTimer = null;
|
|||
|
|
}
|
|||
|
|
this.lastPongTime = Date.now();
|
|||
|
|
this.walletDiscovered = true;
|
|||
|
|
this.walletName = msg.wallet_name;
|
|||
|
|
this.walletIcon = msg.wallet_icon;
|
|||
|
|
const agreed = this.supportedProtocols.find((p) => msg.supported_protocols.includes(p));
|
|||
|
|
if (!agreed) {
|
|||
|
|
const detail = `No protocol overlap. Wallet: [${msg.supported_protocols}], Dapp: [${this.supportedProtocols}]`;
|
|||
|
|
const disconnectMsg = {
|
|||
|
|
action: RelayMsgAction.Disconnect,
|
|||
|
|
reason: DisconnectReason.ProtocolMismatch,
|
|||
|
|
message: detail,
|
|||
|
|
time: Math.floor(Date.now() / 1e3)
|
|||
|
|
};
|
|||
|
|
this.conn?.relay(disconnectMsg).catch(() => {
|
|||
|
|
});
|
|||
|
|
this.stopPingInterval();
|
|||
|
|
this.emit("disconnect", DisconnectReason.ProtocolMismatch, detail);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
this.protocol = agreed;
|
|||
|
|
const sessionData = msg.session[agreed];
|
|||
|
|
if (!isHdwalletv1Session(sessionData)) {
|
|||
|
|
console.error("[wizardconnect/dapp] Invalid hdwalletv1 session data:", sessionData);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (this.conn) {
|
|||
|
|
this.conn.setPeerCapabilities({
|
|||
|
|
chunk: peerSupportsChunk(msg.extensions)
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
this.sessionPaths = [...sessionData.paths];
|
|||
|
|
for (const pathInfo of sessionData.paths) {
|
|||
|
|
const decoded = decodeHdPublicKey(pathInfo.xpub);
|
|||
|
|
if (typeof decoded === "string") {
|
|||
|
|
console.warn("[wizardconnect/dapp] Bad xpub for path", pathInfo.name, decoded);
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
const ci = childIndexOfPathName(pathInfo.name);
|
|||
|
|
if (ci !== void 0) {
|
|||
|
|
this.pubkeyState.setXpubNode(ci, decoded.node);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (!msg.dapp_discovered) {
|
|||
|
|
await this.pushDappReady().catch((e) => console.error("[wizardconnect/dapp] Error pushing dapp_ready:", e));
|
|||
|
|
}
|
|||
|
|
this.emit("walletready", msg);
|
|||
|
|
if (this.pendingSignatureRequests.size > 0) {
|
|||
|
|
const now2 = Math.floor(Date.now() / 1e3);
|
|||
|
|
for (const [, entry] of this.pendingSignatureRequests) {
|
|||
|
|
const refreshed = { ...entry.request, time: now2 };
|
|||
|
|
this.conn.relay(refreshed).then(() => this.emit("messagesent", refreshed)).catch((err) => {
|
|||
|
|
this.pendingSignatureRequests.delete(entry.request.sequence);
|
|||
|
|
entry.reject(err instanceof Error ? err : new Error(String(err)));
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (this.sessionOptions) {
|
|||
|
|
const sessionUpdate = {
|
|||
|
|
walletName: this.walletName ?? void 0,
|
|||
|
|
walletIcon: this.walletIcon ?? void 0,
|
|||
|
|
paths: this.getSessionPaths()
|
|||
|
|
};
|
|||
|
|
saveSession(this.sessionOptions.key, sessionUpdate, this.sessionOptions.storage);
|
|||
|
|
}
|
|||
|
|
this.startPingInterval();
|
|||
|
|
}
|
|||
|
|
handleSignTransactionResponse(response) {
|
|||
|
|
const handlers = this.pendingSignatureRequests.get(response.sequence);
|
|||
|
|
if (!handlers) {
|
|||
|
|
console.warn("[wizardconnect/dapp] No pending request for sequence:", response.sequence);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
this.pendingSignatureRequests.delete(response.sequence);
|
|||
|
|
if (response.error) {
|
|||
|
|
handlers.reject(new Error(response.error));
|
|||
|
|
} else {
|
|||
|
|
handlers.resolve(response);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// node_modules/@wizardconnect/dapp/dist/index.js
|
|||
|
|
var dist_exports2 = {};
|
|||
|
|
__export(dist_exports2, {
|
|||
|
|
DEFAULT_SESSION_KEY: () => DEFAULT_SESSION_KEY,
|
|||
|
|
DappConnectionManager: () => DappConnectionManager,
|
|||
|
|
DappPubkeyStateManager: () => DappPubkeyStateManager,
|
|||
|
|
clearSession: () => clearSession,
|
|||
|
|
loadSession: () => loadSession,
|
|||
|
|
saveSession: () => saveSession
|
|||
|
|
});
|
|||
|
|
var init_dist2 = __esm({
|
|||
|
|
"node_modules/@wizardconnect/dapp/dist/index.js"() {
|
|||
|
|
init_dapp_connection_manager();
|
|||
|
|
init_pubkey_state_manager();
|
|||
|
|
init_session();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// src/lib/connect/wizardconnect.js
|
|||
|
|
var PATHS = { receive: 0, change: 1, defi: 7 };
|
|||
|
|
var ADDRESS_WINDOW = 20;
|
|||
|
|
async function openWizardSession({ dappName, dappIcon, prefix = "bchtest", crypto } = {}) {
|
|||
|
|
const { pubkeyToAddress, pubkeyToLockingBytecode, binToHex: binToHex6, txidOfHex } = crypto ?? {};
|
|||
|
|
if (!pubkeyToAddress || !pubkeyToLockingBytecode || !binToHex6 || !txidOfHex) {
|
|||
|
|
throw new Error(
|
|||
|
|
"openWizardSession needs `crypto: { pubkeyToAddress, pubkeyToLockingBytecode, binToHex, txidOfHex }` injected from the main bundle \u2014 see the note at the top of this file"
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
const [{ initiateDappRelay: initiateDappRelay2 }, { DappConnectionManager: DappConnectionManager2 }] = await Promise.all([
|
|||
|
|
Promise.resolve().then(() => (init_dist(), dist_exports)),
|
|||
|
|
Promise.resolve().then(() => (init_dist2(), dist_exports2))
|
|||
|
|
]);
|
|||
|
|
const manager = new DappConnectionManager2(dappName, dappIcon);
|
|||
|
|
const relay = initiateDappRelay2((payload) => {
|
|||
|
|
manager.updateConnection(payload.client, payload.status);
|
|||
|
|
});
|
|||
|
|
manager.attachRelay(relay);
|
|||
|
|
let resolveReady, rejectReady;
|
|||
|
|
const ready = new Promise((res, rej) => {
|
|||
|
|
resolveReady = res;
|
|||
|
|
rejectReady = rej;
|
|||
|
|
});
|
|||
|
|
manager.once("walletready", (msg) => resolveReady({ walletName: msg?.wallet_name ?? null }));
|
|||
|
|
manager.once("disconnect", (reason, message) => rejectReady(new Error(`wallet disconnected: ${message ?? reason}`)));
|
|||
|
|
const addressAt = (pathName, index) => {
|
|||
|
|
const pubkey = manager.getPubkey(PATHS[pathName], BigInt(index));
|
|||
|
|
if (!pubkey) return null;
|
|||
|
|
return {
|
|||
|
|
pathName,
|
|||
|
|
index,
|
|||
|
|
publicKey: pubkey,
|
|||
|
|
address: pubkeyToAddress(pubkey, prefix, false),
|
|||
|
|
tokenAddress: pubkeyToAddress(pubkey, prefix, true),
|
|||
|
|
lockingBytecode: pubkeyToLockingBytecode(pubkey)
|
|||
|
|
};
|
|||
|
|
};
|
|||
|
|
const derivedAddresses = (window2 = ADDRESS_WINDOW) => {
|
|||
|
|
const out = [];
|
|||
|
|
for (const pathName of ["receive", "change"]) {
|
|||
|
|
if (!manager.hasPath(PATHS[pathName])) continue;
|
|||
|
|
for (let i3 = 0; i3 < window2; i3++) {
|
|||
|
|
const a = addressAt(pathName, i3);
|
|||
|
|
if (a) out.push(a);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return out;
|
|||
|
|
};
|
|||
|
|
return {
|
|||
|
|
label: "WizardConnect",
|
|||
|
|
protocol: "hdwalletv1",
|
|||
|
|
uri: relay.uri,
|
|||
|
|
// wiz://?p=…&s=…
|
|||
|
|
qrUri: relay.qrUri,
|
|||
|
|
// QR-alphanumeric-safe variant
|
|||
|
|
ready,
|
|||
|
|
manager,
|
|||
|
|
// hdwalletv1's `sign_transaction_response` carries ONLY `signedTransaction`
|
|||
|
|
// (+ optional `error`) — there is no `signedTransactionHash`, and the wallet
|
|||
|
|
// SDK has no broadcast step at all. So the dApp broadcasts, and we ask for
|
|||
|
|
// `broadcast: false` to stop a wallet that reads the WC2-shaped payload's
|
|||
|
|
// flag from doing it too. The registrar tolerates a duplicate anyway.
|
|||
|
|
walletBroadcasts: false,
|
|||
|
|
async getAddresses() {
|
|||
|
|
if (!manager.isWalletDiscovered()) await ready;
|
|||
|
|
return derivedAddresses().map((a) => a.address);
|
|||
|
|
},
|
|||
|
|
/** The full derived set, with the HD metadata `inputPaths` needs. */
|
|||
|
|
async getDerivedAddresses(window2) {
|
|||
|
|
if (!manager.isWalletDiscovered()) await ready;
|
|||
|
|
return derivedAddresses(window2);
|
|||
|
|
},
|
|||
|
|
/**
|
|||
|
|
* Sign a transaction built by register-tx.js.
|
|||
|
|
*
|
|||
|
|
* WizardConnect's differentiator is `inputPaths`: rather than the wallet
|
|||
|
|
* guessing which key owns each input, we state it — [inputIndex, pathName,
|
|||
|
|
* addressIndex]. The wallet re-derives each child key and checks it really
|
|||
|
|
* does produce the source output's locking bytecode before signing, so a
|
|||
|
|
* wrong triplet is a hard reject rather than a silent mis-sign.
|
|||
|
|
*
|
|||
|
|
* Note the wallet signs with SIGHASH_ALL|FORKID|UTXOS (stricter than WC2's
|
|||
|
|
* ALL|FORKID). That is the wallet's business — our transaction is unchanged.
|
|||
|
|
*/
|
|||
|
|
async signTransaction(built, { userPrompt } = {}) {
|
|||
|
|
if (!manager.isWalletDiscovered()) await ready;
|
|||
|
|
const known = new Map(derivedAddresses().map((a) => [binToHex6(a.lockingBytecode), a]));
|
|||
|
|
const inputPaths = built.sourceOutputs.map((src, inputIndex) => {
|
|||
|
|
const a = known.get(binToHex6(src.lockingBytecode));
|
|||
|
|
if (!a) {
|
|||
|
|
throw new Error(
|
|||
|
|
`input ${inputIndex} is not from this wallet \u2014 refusing to ask it to sign a coin it does not own`
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
return [inputIndex, a.pathName, a.index];
|
|||
|
|
});
|
|||
|
|
const response = await manager.signTransaction({
|
|||
|
|
transaction: {
|
|||
|
|
transaction: built.transaction,
|
|||
|
|
sourceOutputs: built.sourceOutputs,
|
|||
|
|
broadcast: false,
|
|||
|
|
// we broadcast — see `walletBroadcasts` above
|
|||
|
|
...userPrompt ? { userPrompt } : {}
|
|||
|
|
},
|
|||
|
|
inputPaths
|
|||
|
|
});
|
|||
|
|
if (response?.error) throw new Error(`wallet refused to sign: ${response.error}`);
|
|||
|
|
if (!response?.signedTransaction) throw new Error("wallet returned no signed transaction");
|
|||
|
|
return {
|
|||
|
|
signedTransaction: response.signedTransaction,
|
|||
|
|
// Derived locally: hdwalletv1 does not send the txid back.
|
|||
|
|
signedTransactionHash: txidOfHex(response.signedTransaction)
|
|||
|
|
};
|
|||
|
|
},
|
|||
|
|
async disconnect() {
|
|||
|
|
try {
|
|||
|
|
manager.clearStoredSession();
|
|||
|
|
} catch {
|
|||
|
|
}
|
|||
|
|
try {
|
|||
|
|
await manager.sendDisconnect("registration finished");
|
|||
|
|
} catch {
|
|||
|
|
}
|
|||
|
|
try {
|
|||
|
|
manager.destroy();
|
|||
|
|
} catch {
|
|||
|
|
}
|
|||
|
|
try {
|
|||
|
|
relay.cleanup();
|
|||
|
|
} catch {
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
export {
|
|||
|
|
ADDRESS_WINDOW,
|
|||
|
|
PATHS,
|
|||
|
|
openWizardSession
|
|||
|
|
};
|