onelobby 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/blueprint.schema.json +8 -1
- package/dist/blueprint.d.ts +4 -0
- package/dist/blueprint.js +5 -0
- package/dist/blueprint.js.map +1 -1
- package/dist/codec.d.ts +23 -0
- package/dist/codec.js +28 -0
- package/dist/codec.js.map +1 -1
- package/dist/direct/browser-host.d.ts +46 -0
- package/dist/direct/browser-host.js +93 -0
- package/dist/direct/browser-host.js.map +1 -0
- package/dist/direct/hub-host.d.ts +34 -23
- package/dist/direct/hub-host.js +75 -59
- package/dist/direct/hub-host.js.map +1 -1
- package/dist/direct/hub-local.d.ts +22 -0
- package/dist/direct/hub-local.js +46 -0
- package/dist/direct/hub-local.js.map +1 -0
- package/dist/direct/hub.d.ts +39 -10
- package/dist/direct/hub.js +134 -24
- package/dist/direct/hub.js.map +1 -1
- package/dist/direct/index.d.ts +36 -3
- package/dist/direct/index.js +146 -20
- package/dist/direct/index.js.map +1 -1
- package/dist/direct/metrics.d.ts +12 -0
- package/dist/direct/metrics.js +45 -0
- package/dist/direct/metrics.js.map +1 -0
- package/dist/direct/portmap.d.ts +9 -2
- package/dist/direct/portmap.js +7 -2
- package/dist/direct/portmap.js.map +1 -1
- package/dist/direct/sha1.d.ts +16 -0
- package/dist/direct/sha1.js +111 -0
- package/dist/direct/sha1.js.map +1 -0
- package/dist/direct-address.d.ts +2 -2
- package/dist/direct-address.js.map +1 -1
- package/dist/friendly.d.ts +24 -0
- package/dist/friendly.js +30 -0
- package/dist/friendly.js.map +1 -1
- package/dist/index.d.ts +8 -4
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/internals.d.ts +9 -0
- package/dist/internals.js +9 -0
- package/dist/internals.js.map +1 -1
- package/dist/join-code-kind.d.ts +10 -0
- package/dist/join-code-kind.js +10 -0
- package/dist/join-code-kind.js.map +1 -0
- package/dist/joincode.d.ts +10 -1
- package/dist/joincode.js +23 -3
- package/dist/joincode.js.map +1 -1
- package/dist/lobby.d.ts +94 -5
- package/dist/lobby.js +177 -18
- package/dist/lobby.js.map +1 -1
- package/dist/lockstep.d.ts +8 -0
- package/dist/lockstep.js +23 -2
- package/dist/lockstep.js.map +1 -1
- package/dist/nostr/crypto.d.ts +20 -0
- package/dist/nostr/crypto.js +78 -0
- package/dist/nostr/crypto.js.map +1 -0
- package/dist/nostr/host.d.ts +66 -0
- package/dist/nostr/host.js +196 -0
- package/dist/nostr/host.js.map +1 -0
- package/dist/nostr/limits.d.ts +33 -0
- package/dist/nostr/limits.js +38 -0
- package/dist/nostr/limits.js.map +1 -0
- package/dist/nostr/link.d.ts +70 -0
- package/dist/nostr/link.js +180 -0
- package/dist/nostr/link.js.map +1 -0
- package/dist/nostr/relays.d.ts +79 -0
- package/dist/nostr/relays.js +325 -0
- package/dist/nostr/relays.js.map +1 -0
- package/dist/nostr/room.d.ts +43 -0
- package/dist/nostr/room.js +104 -0
- package/dist/nostr/room.js.map +1 -0
- package/dist/nostr/secp256k1.d.ts +10 -0
- package/dist/nostr/secp256k1.js +171 -0
- package/dist/nostr/secp256k1.js.map +1 -0
- package/dist/nostr/socket.d.ts +48 -0
- package/dist/nostr/socket.js +141 -0
- package/dist/nostr/socket.js.map +1 -0
- package/dist/onelobby.bundle.js +146 -0
- package/dist/onelobby.bundle.js.map +7 -0
- package/dist/onelobby.bundle.mjs +11 -5
- package/dist/onelobby.bundle.mjs.map +4 -4
- package/dist/onelobby.d.ts +31 -3
- package/dist/onelobby.global.js +2673 -60
- package/dist/onelobby.global.js.map +4 -4
- package/dist/onelobby.global.min.js +11 -5
- package/dist/onelobby.global.min.js.map +4 -4
- package/dist/onelobby.js +77 -14
- package/dist/onelobby.js.map +1 -1
- package/dist/pause.d.ts +85 -0
- package/dist/pause.js +160 -0
- package/dist/pause.js.map +1 -0
- package/dist/signaling.d.ts +2 -0
- package/dist/signaling.js.map +1 -1
- package/dist/socket.d.ts +15 -6
- package/dist/socket.js.map +1 -1
- package/dist/testing.d.ts +2 -0
- package/dist/testing.js +18 -2
- package/dist/testing.js.map +1 -1
- package/dist/transport.d.ts +5 -1
- package/dist/transport.js.map +1 -1
- package/dist/webrtc.d.ts +6 -0
- package/dist/webrtc.js +27 -3
- package/dist/webrtc.js.map +1 -1
- package/package.json +1 -1
- package/dist/deprecated.d.ts +0 -18
- package/dist/deprecated.js +0 -11
- package/dist/deprecated.js.map +0 -1
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BIP340 Schnorr signatures over secp256k1 with BigInt, so the package keeps zero runtime dependencies. Nostr needs
|
|
3
|
+
* nothing else from the curve: a fresh key pair per session and one signature per event (spec/nostr-signaling.md s.3).
|
|
4
|
+
* Not constant-time; the keys are throw-away session keys and never leave the process.
|
|
5
|
+
*/
|
|
6
|
+
import { concatBytes, sha256 } from "./crypto.js";
|
|
7
|
+
const P = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn;
|
|
8
|
+
const N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n;
|
|
9
|
+
const GX = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n;
|
|
10
|
+
const GY = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n;
|
|
11
|
+
const INF = [0n, 1n, 0n];
|
|
12
|
+
const G = [GX, GY, 1n];
|
|
13
|
+
const mod = (a, m = P) => {
|
|
14
|
+
const r = a % m;
|
|
15
|
+
return r >= 0n ? r : r + m;
|
|
16
|
+
};
|
|
17
|
+
function pow(b, e, m) {
|
|
18
|
+
let r = 1n;
|
|
19
|
+
b = mod(b, m);
|
|
20
|
+
while (e > 0n) {
|
|
21
|
+
if (e & 1n)
|
|
22
|
+
r = (r * b) % m;
|
|
23
|
+
b = (b * b) % m;
|
|
24
|
+
e >>= 1n;
|
|
25
|
+
}
|
|
26
|
+
return r;
|
|
27
|
+
}
|
|
28
|
+
const inv = (a, m = P) => pow(a, m - 2n, m);
|
|
29
|
+
function dbl(p) {
|
|
30
|
+
const [X, Y, Z] = p;
|
|
31
|
+
if (Z === 0n || Y === 0n)
|
|
32
|
+
return INF;
|
|
33
|
+
const A = (X * X) % P;
|
|
34
|
+
const B = (Y * Y) % P;
|
|
35
|
+
const C = (B * B) % P;
|
|
36
|
+
const D = mod(2n * ((X + B) * (X + B) - A - C));
|
|
37
|
+
const E = 3n * A;
|
|
38
|
+
const F = (E * E) % P;
|
|
39
|
+
const X3 = mod(F - 2n * D);
|
|
40
|
+
return [X3, mod(E * (D - X3) - 8n * C), mod(2n * Y * Z)];
|
|
41
|
+
}
|
|
42
|
+
function add(p, q) {
|
|
43
|
+
if (p[2] === 0n)
|
|
44
|
+
return q;
|
|
45
|
+
if (q[2] === 0n)
|
|
46
|
+
return p;
|
|
47
|
+
const [X1, Y1, Z1] = p;
|
|
48
|
+
const [X2, Y2, Z2] = q;
|
|
49
|
+
const Z1Z1 = (Z1 * Z1) % P;
|
|
50
|
+
const Z2Z2 = (Z2 * Z2) % P;
|
|
51
|
+
const U1 = (X1 * Z2Z2) % P;
|
|
52
|
+
const U2 = (X2 * Z1Z1) % P;
|
|
53
|
+
const S1 = (Y1 * Z2 * Z2Z2) % P;
|
|
54
|
+
const S2 = (Y2 * Z1 * Z1Z1) % P;
|
|
55
|
+
if (U1 === U2)
|
|
56
|
+
return S1 === S2 ? dbl(p) : INF;
|
|
57
|
+
const H = mod(U2 - U1);
|
|
58
|
+
const I = (4n * H * H) % P;
|
|
59
|
+
const J = (H * I) % P;
|
|
60
|
+
const r = mod(2n * (S2 - S1));
|
|
61
|
+
const V = (U1 * I) % P;
|
|
62
|
+
const X3 = mod(r * r - J - 2n * V);
|
|
63
|
+
const Y3 = mod(r * (V - X3) - 2n * S1 * J);
|
|
64
|
+
const Z3 = mod(((Z1 + Z2) * (Z1 + Z2) - Z1Z1 - Z2Z2) * H);
|
|
65
|
+
return [X3, Y3, Z3];
|
|
66
|
+
}
|
|
67
|
+
function mul(p, k) {
|
|
68
|
+
let r = INF;
|
|
69
|
+
let q = p;
|
|
70
|
+
while (k > 0n) {
|
|
71
|
+
if (k & 1n)
|
|
72
|
+
r = add(r, q);
|
|
73
|
+
q = dbl(q);
|
|
74
|
+
k >>= 1n;
|
|
75
|
+
}
|
|
76
|
+
return r;
|
|
77
|
+
}
|
|
78
|
+
function affine(p) {
|
|
79
|
+
if (p[2] === 0n)
|
|
80
|
+
return null;
|
|
81
|
+
const zi = inv(p[2]);
|
|
82
|
+
const zi2 = (zi * zi) % P;
|
|
83
|
+
return [(p[0] * zi2) % P, (p[1] * zi2 * zi) % P];
|
|
84
|
+
}
|
|
85
|
+
export const bytesToInt = (b) => {
|
|
86
|
+
let n = 0n;
|
|
87
|
+
for (const x of b)
|
|
88
|
+
n = (n << 8n) | BigInt(x);
|
|
89
|
+
return n;
|
|
90
|
+
};
|
|
91
|
+
export function intToBytes(n, len = 32) {
|
|
92
|
+
const out = new Uint8Array(len);
|
|
93
|
+
for (let i = len - 1; i >= 0; i--) {
|
|
94
|
+
out[i] = Number(n & 0xffn);
|
|
95
|
+
n >>= 8n;
|
|
96
|
+
}
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
async function taggedHash(tag, ...msgs) {
|
|
100
|
+
const t = await sha256(new TextEncoder().encode(tag));
|
|
101
|
+
return sha256(concatBytes(t, t, ...msgs));
|
|
102
|
+
}
|
|
103
|
+
function liftX(x) {
|
|
104
|
+
if (x >= P)
|
|
105
|
+
return null;
|
|
106
|
+
const y2 = mod(x * x * x + 7n);
|
|
107
|
+
let y = pow(y2, (P + 1n) / 4n, P);
|
|
108
|
+
if (mod(y * y) !== y2)
|
|
109
|
+
return null;
|
|
110
|
+
if (y & 1n)
|
|
111
|
+
y = P - y;
|
|
112
|
+
return [x, y];
|
|
113
|
+
}
|
|
114
|
+
/** The 32-byte x-only public key for a 32-byte secret key. */
|
|
115
|
+
export function schnorrPublicKey(secret) {
|
|
116
|
+
const d = bytesToInt(secret);
|
|
117
|
+
if (d < 1n || d >= N)
|
|
118
|
+
throw new Error("secret key out of range");
|
|
119
|
+
const pt = affine(mul(G, d));
|
|
120
|
+
return intToBytes(pt[0]);
|
|
121
|
+
}
|
|
122
|
+
/** A fresh random secret key (valid scalar) from the platform CSPRNG. */
|
|
123
|
+
export function randomSecretKey() {
|
|
124
|
+
for (;;) {
|
|
125
|
+
const b = new Uint8Array(32);
|
|
126
|
+
globalThis.crypto.getRandomValues(b);
|
|
127
|
+
const d = bytesToInt(b);
|
|
128
|
+
if (d >= 1n && d < N)
|
|
129
|
+
return b;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/** BIP340 signature (64 bytes) of a 32-byte message. `aux` is 32 bytes of randomness (default: fresh). */
|
|
133
|
+
export async function schnorrSign(msg, secret, aux) {
|
|
134
|
+
const dPrime = bytesToInt(secret);
|
|
135
|
+
if (dPrime < 1n || dPrime >= N)
|
|
136
|
+
throw new Error("secret key out of range");
|
|
137
|
+
const pt = affine(mul(G, dPrime));
|
|
138
|
+
const d = pt[1] & 1n ? N - dPrime : dPrime;
|
|
139
|
+
const px = intToBytes(pt[0]);
|
|
140
|
+
const a = aux ?? globalThis.crypto.getRandomValues(new Uint8Array(32));
|
|
141
|
+
const th = await taggedHash("BIP0340/aux", a);
|
|
142
|
+
const dBytes = intToBytes(d);
|
|
143
|
+
const t = dBytes.map((x, i) => x ^ th[i]);
|
|
144
|
+
const rand = await taggedHash("BIP0340/nonce", t, px, msg);
|
|
145
|
+
const kPrime = bytesToInt(rand) % N;
|
|
146
|
+
if (kPrime === 0n)
|
|
147
|
+
throw new Error("bad nonce");
|
|
148
|
+
const r = affine(mul(G, kPrime));
|
|
149
|
+
const k = r[1] & 1n ? N - kPrime : kPrime;
|
|
150
|
+
const rx = intToBytes(r[0]);
|
|
151
|
+
const e = bytesToInt(await taggedHash("BIP0340/challenge", rx, px, msg)) % N;
|
|
152
|
+
return concatBytes(rx, intToBytes((k + e * d) % N));
|
|
153
|
+
}
|
|
154
|
+
/** BIP340 verification. */
|
|
155
|
+
export async function schnorrVerify(sig, msg, pub) {
|
|
156
|
+
if (sig.length !== 64 || pub.length !== 32)
|
|
157
|
+
return false;
|
|
158
|
+
const pt = liftX(bytesToInt(pub));
|
|
159
|
+
if (!pt)
|
|
160
|
+
return false;
|
|
161
|
+
const r = bytesToInt(sig.subarray(0, 32));
|
|
162
|
+
const s = bytesToInt(sig.subarray(32));
|
|
163
|
+
if (r >= P || s >= N)
|
|
164
|
+
return false;
|
|
165
|
+
const e = bytesToInt(await taggedHash("BIP0340/challenge", sig.subarray(0, 32), pub, msg)) % N;
|
|
166
|
+
const sG = mul(G, s);
|
|
167
|
+
const eP = mul([pt[0], pt[1], 1n], N - e);
|
|
168
|
+
const R = affine(add(sG, eP));
|
|
169
|
+
return !!R && !(R[1] & 1n) && R[0] === r;
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=secp256k1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secp256k1.js","sourceRoot":"","sources":["../../src/nostr/secp256k1.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,CAAC,GAAG,mEAAmE,CAAC;AAC9E,MAAM,CAAC,GAAG,mEAAmE,CAAC;AAC9E,MAAM,EAAE,GAAG,mEAAmE,CAAC;AAC/E,MAAM,EAAE,GAAG,mEAAmE,CAAC;AAG/E,MAAM,GAAG,GAAQ,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AAC9B,MAAM,CAAC,GAAQ,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AAE5B,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,CAAC,GAAG,CAAC,EAAU,EAAE;IACvC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChB,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC,CAAC;AAEF,SAAS,GAAG,CAAC,CAAS,EAAE,CAAS,EAAE,CAAS;IAC1C,IAAI,CAAC,GAAG,EAAE,CAAC;IACX,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACd,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC;QACd,IAAI,CAAC,GAAG,EAAE;YAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QAChB,CAAC,KAAK,EAAE,CAAC;IACX,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,GAAG,GAAG,CAAC,CAAS,EAAE,CAAC,GAAG,CAAC,EAAU,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;AAE5D,SAAS,GAAG,CAAC,CAAM;IACjB,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;IACpB,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE;QAAE,OAAO,GAAG,CAAC;IACrC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IACtB,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IACtB,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IACtB,MAAM,CAAC,GAAG,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAChD,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACjB,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IACtB,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;IAC3B,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,GAAG,CAAC,CAAM,EAAE,CAAM;IACzB,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE;QAAE,OAAO,CAAC,CAAC;IAC1B,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE;QAAE,OAAO,CAAC,CAAC;IAC1B,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;IACvB,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;IACvB,MAAM,IAAI,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IAC3B,MAAM,IAAI,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IAC3B,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3B,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3B,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IAChC,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAC/C,MAAM,CAAC,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;IACvB,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAC3B,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IACtB,MAAM,CAAC,GAAG,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;IAC9B,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IACvB,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;IACnC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;IAC3C,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AACtB,CAAC;AAED,SAAS,GAAG,CAAC,CAAM,EAAE,CAAS;IAC5B,IAAI,CAAC,GAAG,GAAG,CAAC;IACZ,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC;QACd,IAAI,CAAC,GAAG,EAAE;YAAE,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1B,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QACX,CAAC,KAAK,EAAE,CAAC;IACX,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,MAAM,CAAC,CAAM;IACpB,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC7B,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrB,MAAM,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IAC1B,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAa,EAAU,EAAE;IAClD,IAAI,CAAC,GAAG,EAAE,CAAC;IACX,KAAK,MAAM,CAAC,IAAI,CAAC;QAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,CAAC;AACX,CAAC,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,CAAS,EAAE,GAAG,GAAG,EAAE;IAC5C,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;IAChC,KAAK,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;QAC3B,CAAC,KAAK,EAAE,CAAC;IACX,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,GAAW,EAAE,GAAG,IAAkB;IAC1D,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACtD,OAAO,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;AAC5C,CAAC;AAED,SAAS,KAAK,CAAC,CAAS;IACtB,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;IAC/B,IAAI,CAAC,GAAG,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;IAClC,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,CAAC,GAAG,EAAE;QAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACtB,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAChB,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,gBAAgB,CAAC,MAAkB;IACjD,MAAM,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAC7B,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IACjE,MAAM,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAE,CAAC;IAC9B,OAAO,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3B,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,eAAe;IAC7B,SAAS,CAAC;QACR,MAAM,CAAC,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;QAC7B,UAAU,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,CAAC;IACjC,CAAC;AACH,CAAC;AAED,0GAA0G;AAC1G,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAe,EAAE,MAAkB,EAAE,GAAgB;IACrF,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,MAAM,GAAG,EAAE,IAAI,MAAM,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC3E,MAAM,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAE,CAAC;IACnC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IAC3C,MAAM,EAAE,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7B,MAAM,CAAC,GAAG,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;IACvE,MAAM,EAAE,GAAG,MAAM,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;IAC7B,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,eAAe,EAAE,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,MAAM,KAAK,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC;IAChD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAE,CAAC;IAClC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IAC1C,MAAM,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5B,MAAM,CAAC,GAAG,UAAU,CAAC,MAAM,UAAU,CAAC,mBAAmB,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAC7E,OAAO,WAAW,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,2BAA2B;AAC3B,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAe,EAAE,GAAe,EAAE,GAAe;IACnF,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IACzD,MAAM,EAAE,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IAClC,IAAI,CAAC,EAAE;QAAE,OAAO,KAAK,CAAC;IACtB,MAAM,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAC1C,MAAM,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IACvC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACnC,MAAM,CAAC,GAAG,UAAU,CAAC,MAAM,UAAU,CAAC,mBAAmB,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAC/F,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACrB,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1C,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IAC9B,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AAC3C,CAAC","sourcesContent":["/**\n * BIP340 Schnorr signatures over secp256k1 with BigInt, so the package keeps zero runtime dependencies. Nostr needs\n * nothing else from the curve: a fresh key pair per session and one signature per event (spec/nostr-signaling.md s.3).\n * Not constant-time; the keys are throw-away session keys and never leave the process.\n */\nimport { concatBytes, sha256 } from \"./crypto.js\";\n\nconst P = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn;\nconst N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n;\nconst GX = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n;\nconst GY = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n;\n\ntype Jac = [bigint, bigint, bigint]; // Jacobian (X, Y, Z); Z = 0 is the point at infinity\nconst INF: Jac = [0n, 1n, 0n];\nconst G: Jac = [GX, GY, 1n];\n\nconst mod = (a: bigint, m = P): bigint => {\n const r = a % m;\n return r >= 0n ? r : r + m;\n};\n\nfunction pow(b: bigint, e: bigint, m: bigint): bigint {\n let r = 1n;\n b = mod(b, m);\n while (e > 0n) {\n if (e & 1n) r = (r * b) % m;\n b = (b * b) % m;\n e >>= 1n;\n }\n return r;\n}\n\nconst inv = (a: bigint, m = P): bigint => pow(a, m - 2n, m);\n\nfunction dbl(p: Jac): Jac {\n const [X, Y, Z] = p;\n if (Z === 0n || Y === 0n) return INF;\n const A = (X * X) % P;\n const B = (Y * Y) % P;\n const C = (B * B) % P;\n const D = mod(2n * ((X + B) * (X + B) - A - C));\n const E = 3n * A;\n const F = (E * E) % P;\n const X3 = mod(F - 2n * D);\n return [X3, mod(E * (D - X3) - 8n * C), mod(2n * Y * Z)];\n}\n\nfunction add(p: Jac, q: Jac): Jac {\n if (p[2] === 0n) return q;\n if (q[2] === 0n) return p;\n const [X1, Y1, Z1] = p;\n const [X2, Y2, Z2] = q;\n const Z1Z1 = (Z1 * Z1) % P;\n const Z2Z2 = (Z2 * Z2) % P;\n const U1 = (X1 * Z2Z2) % P;\n const U2 = (X2 * Z1Z1) % P;\n const S1 = (Y1 * Z2 * Z2Z2) % P;\n const S2 = (Y2 * Z1 * Z1Z1) % P;\n if (U1 === U2) return S1 === S2 ? dbl(p) : INF;\n const H = mod(U2 - U1);\n const I = (4n * H * H) % P;\n const J = (H * I) % P;\n const r = mod(2n * (S2 - S1));\n const V = (U1 * I) % P;\n const X3 = mod(r * r - J - 2n * V);\n const Y3 = mod(r * (V - X3) - 2n * S1 * J);\n const Z3 = mod(((Z1 + Z2) * (Z1 + Z2) - Z1Z1 - Z2Z2) * H);\n return [X3, Y3, Z3];\n}\n\nfunction mul(p: Jac, k: bigint): Jac {\n let r = INF;\n let q = p;\n while (k > 0n) {\n if (k & 1n) r = add(r, q);\n q = dbl(q);\n k >>= 1n;\n }\n return r;\n}\n\nfunction affine(p: Jac): [bigint, bigint] | null {\n if (p[2] === 0n) return null;\n const zi = inv(p[2]);\n const zi2 = (zi * zi) % P;\n return [(p[0] * zi2) % P, (p[1] * zi2 * zi) % P];\n}\n\nexport const bytesToInt = (b: Uint8Array): bigint => {\n let n = 0n;\n for (const x of b) n = (n << 8n) | BigInt(x);\n return n;\n};\n\nexport function intToBytes(n: bigint, len = 32): Uint8Array {\n const out = new Uint8Array(len);\n for (let i = len - 1; i >= 0; i--) {\n out[i] = Number(n & 0xffn);\n n >>= 8n;\n }\n return out;\n}\n\nasync function taggedHash(tag: string, ...msgs: Uint8Array[]): Promise<Uint8Array> {\n const t = await sha256(new TextEncoder().encode(tag));\n return sha256(concatBytes(t, t, ...msgs));\n}\n\nfunction liftX(x: bigint): [bigint, bigint] | null {\n if (x >= P) return null;\n const y2 = mod(x * x * x + 7n);\n let y = pow(y2, (P + 1n) / 4n, P);\n if (mod(y * y) !== y2) return null;\n if (y & 1n) y = P - y;\n return [x, y];\n}\n\n/** The 32-byte x-only public key for a 32-byte secret key. */\nexport function schnorrPublicKey(secret: Uint8Array): Uint8Array {\n const d = bytesToInt(secret);\n if (d < 1n || d >= N) throw new Error(\"secret key out of range\");\n const pt = affine(mul(G, d))!;\n return intToBytes(pt[0]);\n}\n\n/** A fresh random secret key (valid scalar) from the platform CSPRNG. */\nexport function randomSecretKey(): Uint8Array {\n for (;;) {\n const b = new Uint8Array(32);\n globalThis.crypto.getRandomValues(b);\n const d = bytesToInt(b);\n if (d >= 1n && d < N) return b;\n }\n}\n\n/** BIP340 signature (64 bytes) of a 32-byte message. `aux` is 32 bytes of randomness (default: fresh). */\nexport async function schnorrSign(msg: Uint8Array, secret: Uint8Array, aux?: Uint8Array): Promise<Uint8Array> {\n const dPrime = bytesToInt(secret);\n if (dPrime < 1n || dPrime >= N) throw new Error(\"secret key out of range\");\n const pt = affine(mul(G, dPrime))!;\n const d = pt[1] & 1n ? N - dPrime : dPrime;\n const px = intToBytes(pt[0]);\n const a = aux ?? globalThis.crypto.getRandomValues(new Uint8Array(32));\n const th = await taggedHash(\"BIP0340/aux\", a);\n const dBytes = intToBytes(d);\n const t = dBytes.map((x, i) => x ^ th[i]);\n const rand = await taggedHash(\"BIP0340/nonce\", t, px, msg);\n const kPrime = bytesToInt(rand) % N;\n if (kPrime === 0n) throw new Error(\"bad nonce\");\n const r = affine(mul(G, kPrime))!;\n const k = r[1] & 1n ? N - kPrime : kPrime;\n const rx = intToBytes(r[0]);\n const e = bytesToInt(await taggedHash(\"BIP0340/challenge\", rx, px, msg)) % N;\n return concatBytes(rx, intToBytes((k + e * d) % N));\n}\n\n/** BIP340 verification. */\nexport async function schnorrVerify(sig: Uint8Array, msg: Uint8Array, pub: Uint8Array): Promise<boolean> {\n if (sig.length !== 64 || pub.length !== 32) return false;\n const pt = liftX(bytesToInt(pub));\n if (!pt) return false;\n const r = bytesToInt(sig.subarray(0, 32));\n const s = bytesToInt(sig.subarray(32));\n if (r >= P || s >= N) return false;\n const e = bytesToInt(await taggedHash(\"BIP0340/challenge\", sig.subarray(0, 32), pub, msg)) % N;\n const sG = mul(G, s);\n const eP = mul([pt[0], pt[1], 1n], N - e);\n const R = affine(add(sG, eP));\n return !!R && !(R[1] & 1n) && R[0] === r;\n}\n"]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The joiner's signaling socket over Nostr (spec/nostr-signaling.md s.4): a {@link SocketLike} that carries the hub
|
|
3
|
+
* protocol to the host through a {@link NostrRoom}. `SignalingClient` cannot tell it from a TCP WebSocket.
|
|
4
|
+
*/
|
|
5
|
+
import type { Clock } from "../clock.js";
|
|
6
|
+
import type { SocketLike } from "../transport.js";
|
|
7
|
+
import type { RelayConnect } from "./relays.js";
|
|
8
|
+
export interface NostrSocketOptions {
|
|
9
|
+
code: string;
|
|
10
|
+
relays?: readonly string[];
|
|
11
|
+
connect?: RelayConnect;
|
|
12
|
+
clock: Clock;
|
|
13
|
+
wall?: () => number;
|
|
14
|
+
/** No relay connected within this long: the socket fails (default 10000). */
|
|
15
|
+
relayTimeoutMs?: number;
|
|
16
|
+
/** The host's first `open` did not arrive within this long: the socket fails (default 15000). */
|
|
17
|
+
openTimeoutMs?: number;
|
|
18
|
+
/** Resend interval of `open` (default 2000). */
|
|
19
|
+
openEveryMs?: number;
|
|
20
|
+
}
|
|
21
|
+
export declare class NostrSocket implements SocketLike {
|
|
22
|
+
private readonly o;
|
|
23
|
+
readyState: number;
|
|
24
|
+
onopen: SocketLike["onopen"];
|
|
25
|
+
onmessage: SocketLike["onmessage"];
|
|
26
|
+
onclose: SocketLike["onclose"];
|
|
27
|
+
onerror: SocketLike["onerror"];
|
|
28
|
+
/** Why the socket failed or closed, for messages ("no Nostr relay reachable", "host did not answer"). */
|
|
29
|
+
failure: string;
|
|
30
|
+
private room;
|
|
31
|
+
private link;
|
|
32
|
+
private hostPub;
|
|
33
|
+
private cancelOpen;
|
|
34
|
+
private cancelDeadline;
|
|
35
|
+
private sendOpen;
|
|
36
|
+
private relaysSeen;
|
|
37
|
+
constructor(o: NostrSocketOptions);
|
|
38
|
+
/** Relays connected, for a progress line. */
|
|
39
|
+
get relaysConnected(): number;
|
|
40
|
+
get relaysTotal(): number;
|
|
41
|
+
send(data: string): void;
|
|
42
|
+
close(_code?: number, reason?: string): void;
|
|
43
|
+
private start;
|
|
44
|
+
private relaysChanged;
|
|
45
|
+
private onFrame;
|
|
46
|
+
private fail;
|
|
47
|
+
private finish;
|
|
48
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { ReliableLink } from "./link.js";
|
|
2
|
+
import { NostrRoom } from "./room.js";
|
|
3
|
+
import { GIVE_UP_MS, JOINER_FIRST_RELAY_MS, OPEN_EVERY_MS } from "./limits.js";
|
|
4
|
+
export class NostrSocket {
|
|
5
|
+
o;
|
|
6
|
+
readyState = 0;
|
|
7
|
+
onopen = null;
|
|
8
|
+
onmessage = null;
|
|
9
|
+
onclose = null;
|
|
10
|
+
onerror = null;
|
|
11
|
+
/** Why the socket failed or closed, for messages ("no Nostr relay reachable", "host did not answer"). */
|
|
12
|
+
failure = "";
|
|
13
|
+
room = null;
|
|
14
|
+
link = null;
|
|
15
|
+
hostPub = "";
|
|
16
|
+
cancelOpen = null;
|
|
17
|
+
cancelDeadline = null;
|
|
18
|
+
sendOpen = null;
|
|
19
|
+
relaysSeen = 0;
|
|
20
|
+
constructor(o) {
|
|
21
|
+
this.o = o;
|
|
22
|
+
void this.start();
|
|
23
|
+
}
|
|
24
|
+
/** Relays connected, for a progress line. */
|
|
25
|
+
get relaysConnected() {
|
|
26
|
+
return this.room?.relaysConnected ?? 0;
|
|
27
|
+
}
|
|
28
|
+
get relaysTotal() {
|
|
29
|
+
return this.room?.relaysTotal ?? 0;
|
|
30
|
+
}
|
|
31
|
+
send(data) {
|
|
32
|
+
if (this.readyState !== 1 || !this.link)
|
|
33
|
+
return;
|
|
34
|
+
// The keep-alive ping never crosses Nostr: answer it here (spec s.4)
|
|
35
|
+
if (data.length < 64 && data.startsWith('{"t":"ping"')) {
|
|
36
|
+
try {
|
|
37
|
+
const m = JSON.parse(data);
|
|
38
|
+
queueMicrotask(() => this.readyState === 1 && this.onmessage?.({ data: JSON.stringify({ t: "pong", n: m.n }) }));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
/* send it on */
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
this.link.send(data);
|
|
46
|
+
}
|
|
47
|
+
close(_code, reason) {
|
|
48
|
+
if (this.readyState === 3)
|
|
49
|
+
return;
|
|
50
|
+
const wasOpen = this.readyState === 1;
|
|
51
|
+
if (wasOpen)
|
|
52
|
+
this.link?.close(reason ?? "");
|
|
53
|
+
this.finish(1000, reason ?? "", false);
|
|
54
|
+
}
|
|
55
|
+
async start() {
|
|
56
|
+
const { clock } = this.o;
|
|
57
|
+
try {
|
|
58
|
+
// a relay that comes up after the first `open` gets one at once, not at the next resend (spec s.4)
|
|
59
|
+
this.room = await NostrRoom.open({ ...this.o, onChange: () => this.relaysChanged() });
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
this.fail(String(e));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const room = this.room;
|
|
66
|
+
if (this.readyState === 3) {
|
|
67
|
+
room.close();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
room.onFrame = (f) => this.onFrame(f);
|
|
71
|
+
this.cancelDeadline = clock.setTimeout(() => this.fail("the host did not answer through the Nostr relays"), this.o.openTimeoutMs ?? GIVE_UP_MS);
|
|
72
|
+
if (!(await room.whenUp(this.o.relayTimeoutMs ?? JOINER_FIRST_RELAY_MS))) {
|
|
73
|
+
this.fail("no Nostr relay reachable");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (this.readyState !== 0)
|
|
77
|
+
return;
|
|
78
|
+
const open = () => void room.send("", { type: "open", seq: 0, ack: 0 });
|
|
79
|
+
this.relaysSeen = room.relaysConnected;
|
|
80
|
+
this.sendOpen = open;
|
|
81
|
+
open();
|
|
82
|
+
this.cancelOpen = clock.setInterval(open, this.o.openEveryMs ?? OPEN_EVERY_MS);
|
|
83
|
+
}
|
|
84
|
+
relaysChanged() {
|
|
85
|
+
const up = this.room?.relaysConnected ?? 0;
|
|
86
|
+
if (up > this.relaysSeen && this.readyState === 0)
|
|
87
|
+
this.sendOpen?.();
|
|
88
|
+
this.relaysSeen = up;
|
|
89
|
+
}
|
|
90
|
+
onFrame(f) {
|
|
91
|
+
if (this.readyState === 3 || f.to !== this.room?.pub)
|
|
92
|
+
return;
|
|
93
|
+
if (this.readyState === 0) {
|
|
94
|
+
if (f.type !== "open")
|
|
95
|
+
return;
|
|
96
|
+
this.hostPub = f.from;
|
|
97
|
+
this.sendOpen = null;
|
|
98
|
+
this.cancelOpen?.();
|
|
99
|
+
this.cancelDeadline?.();
|
|
100
|
+
this.cancelOpen = this.cancelDeadline = null;
|
|
101
|
+
const room = this.room;
|
|
102
|
+
this.link = new ReliableLink({
|
|
103
|
+
clock: this.o.clock,
|
|
104
|
+
send: (out) => void room.send(this.hostPub, out),
|
|
105
|
+
deliver: (text) => this.readyState === 1 && this.onmessage?.({ data: text }),
|
|
106
|
+
onPeerClose: (reason) => this.finish(1000, reason, true),
|
|
107
|
+
onDead: (reason) => this.finish(1006, reason, false),
|
|
108
|
+
});
|
|
109
|
+
this.readyState = 1;
|
|
110
|
+
this.onopen?.();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (f.from !== this.hostPub)
|
|
114
|
+
return;
|
|
115
|
+
if (f.type === "open") {
|
|
116
|
+
this.link?.heard();
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
this.link?.receive(f);
|
|
120
|
+
}
|
|
121
|
+
fail(why) {
|
|
122
|
+
if (this.readyState === 3)
|
|
123
|
+
return;
|
|
124
|
+
this.failure = why;
|
|
125
|
+
if (this.readyState === 0)
|
|
126
|
+
this.onerror?.({ message: why });
|
|
127
|
+
this.finish(1006, why, false);
|
|
128
|
+
}
|
|
129
|
+
finish(code, reason, clean) {
|
|
130
|
+
if (this.readyState === 3)
|
|
131
|
+
return;
|
|
132
|
+
this.readyState = 3;
|
|
133
|
+
this.cancelOpen?.();
|
|
134
|
+
this.cancelDeadline?.();
|
|
135
|
+
this.cancelOpen = this.cancelDeadline = null;
|
|
136
|
+
this.link?.stop();
|
|
137
|
+
this.room?.close(true);
|
|
138
|
+
queueMicrotask(() => this.onclose?.({ code, reason, wasClean: clean }));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=socket.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket.js","sourceRoot":"","sources":["../../src/nostr/socket.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,YAAY,EAAc,MAAM,WAAW,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAiB/E,MAAM,OAAO,WAAW;IAiBO;IAhB7B,UAAU,GAAG,CAAC,CAAC;IACf,MAAM,GAAyB,IAAI,CAAC;IACpC,SAAS,GAA4B,IAAI,CAAC;IAC1C,OAAO,GAA0B,IAAI,CAAC;IACtC,OAAO,GAA0B,IAAI,CAAC;IACtC,yGAAyG;IACzG,OAAO,GAAG,EAAE,CAAC;IAEL,IAAI,GAAqB,IAAI,CAAC;IAC9B,IAAI,GAAwB,IAAI,CAAC;IACjC,OAAO,GAAG,EAAE,CAAC;IACb,UAAU,GAAwB,IAAI,CAAC;IACvC,cAAc,GAAwB,IAAI,CAAC;IAC3C,QAAQ,GAAwB,IAAI,CAAC;IACrC,UAAU,GAAG,CAAC,CAAC;IAEvB,YAA6B,CAAqB;QAArB,MAAC,GAAD,CAAC,CAAoB;QAChD,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC;IAED,6CAA6C;IAC7C,IAAI,eAAe;QACjB,OAAO,IAAI,CAAC,IAAI,EAAE,eAAe,IAAI,CAAC,CAAC;IACzC,CAAC;IAED,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,IAAI,EAAE,WAAW,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,CAAC,IAAY;QACf,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;YAAE,OAAO;QAChD,qEAAqE;QACrE,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YACvD,IAAI,CAAC;gBACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAoB,CAAC;gBAC9C,cAAc,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,KAAK,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;gBACjH,OAAO;YACT,CAAC;YAAC,MAAM,CAAC;gBACP,gBAAgB;YAClB,CAAC;QACH,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,KAAc,EAAE,MAAe;QACnC,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC;YAAE,OAAO;QAClC,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,KAAK,CAAC,CAAC;QACtC,IAAI,OAAO;YAAE,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC;IACzC,CAAC;IAEO,KAAK,CAAC,KAAK;QACjB,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;QACzB,IAAI,CAAC;YACH,mGAAmG;YACnG,IAAI,CAAC,IAAI,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;QACxF,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,OAAO;QACT,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACtC,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,kDAAkD,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,aAAa,IAAI,UAAU,CAAC,CAAC;QAChJ,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,IAAI,qBAAqB,CAAC,CAAC,EAAE,CAAC;YACzE,IAAI,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;YACtC,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC;YAAE,OAAO;QAClC,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACxE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,eAAe,CAAC;QACvC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,EAAE,CAAC;QACP,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,WAAW,IAAI,aAAa,CAAC,CAAC;IACjF,CAAC;IAEO,aAAa;QACnB,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,eAAe,IAAI,CAAC,CAAC;QAC3C,IAAI,EAAE,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC;YAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC;QACrE,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;IACvB,CAAC;IAEO,OAAO,CAAC,CAAQ;QACtB,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,IAAI,EAAE,GAAG;YAAE,OAAO;QAC7D,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM;gBAAE,OAAO;YAC9B,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC;YACtB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YACrB,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;YACpB,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;YACxB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;YAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAK,CAAC;YACxB,IAAI,CAAC,IAAI,GAAG,IAAI,YAAY,CAAC;gBAC3B,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK;gBACnB,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC;gBAChD,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,KAAK,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;gBAC5E,WAAW,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC;gBACxD,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC;aACrD,CAAC,CAAC;YACH,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;YACpB,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;YAChB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,OAAO;YAAE,OAAO;QACpC,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACtB,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;YACnB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACxB,CAAC;IAEO,IAAI,CAAC,GAAW;QACtB,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC;YAAE,OAAO;QAClC,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC;QACnB,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC;YAAE,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5D,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IAChC,CAAC;IAEO,MAAM,CAAC,IAAY,EAAE,MAAc,EAAE,KAAc;QACzD,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC;YAAE,OAAO;QAClC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;QACpB,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;QACxB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC7C,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;QAClB,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACvB,cAAc,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IAC1E,CAAC;CACF","sourcesContent":["/**\n * The joiner's signaling socket over Nostr (spec/nostr-signaling.md s.4): a {@link SocketLike} that carries the hub\n * protocol to the host through a {@link NostrRoom}. `SignalingClient` cannot tell it from a TCP WebSocket.\n */\nimport type { Clock } from \"../clock.js\";\nimport type { SocketLike } from \"../transport.js\";\nimport { ReliableLink, type Frame } from \"./link.js\";\nimport { NostrRoom } from \"./room.js\";\nimport { GIVE_UP_MS, JOINER_FIRST_RELAY_MS, OPEN_EVERY_MS } from \"./limits.js\";\nimport type { RelayConnect } from \"./relays.js\";\n\nexport interface NostrSocketOptions {\n code: string;\n relays?: readonly string[];\n connect?: RelayConnect;\n clock: Clock;\n wall?: () => number;\n /** No relay connected within this long: the socket fails (default 10000). */\n relayTimeoutMs?: number;\n /** The host's first `open` did not arrive within this long: the socket fails (default 15000). */\n openTimeoutMs?: number;\n /** Resend interval of `open` (default 2000). */\n openEveryMs?: number;\n}\n\nexport class NostrSocket implements SocketLike {\n readyState = 0;\n onopen: SocketLike[\"onopen\"] = null;\n onmessage: SocketLike[\"onmessage\"] = null;\n onclose: SocketLike[\"onclose\"] = null;\n onerror: SocketLike[\"onerror\"] = null;\n /** Why the socket failed or closed, for messages (\"no Nostr relay reachable\", \"host did not answer\"). */\n failure = \"\";\n\n private room: NostrRoom | null = null;\n private link: ReliableLink | null = null;\n private hostPub = \"\";\n private cancelOpen: (() => void) | null = null;\n private cancelDeadline: (() => void) | null = null;\n private sendOpen: (() => void) | null = null;\n private relaysSeen = 0;\n\n constructor(private readonly o: NostrSocketOptions) {\n void this.start();\n }\n\n /** Relays connected, for a progress line. */\n get relaysConnected(): number {\n return this.room?.relaysConnected ?? 0;\n }\n\n get relaysTotal(): number {\n return this.room?.relaysTotal ?? 0;\n }\n\n send(data: string): void {\n if (this.readyState !== 1 || !this.link) return;\n // The keep-alive ping never crosses Nostr: answer it here (spec s.4)\n if (data.length < 64 && data.startsWith('{\"t\":\"ping\"')) {\n try {\n const m = JSON.parse(data) as { n?: unknown };\n queueMicrotask(() => this.readyState === 1 && this.onmessage?.({ data: JSON.stringify({ t: \"pong\", n: m.n }) }));\n return;\n } catch {\n /* send it on */\n }\n }\n this.link.send(data);\n }\n\n close(_code?: number, reason?: string): void {\n if (this.readyState === 3) return;\n const wasOpen = this.readyState === 1;\n if (wasOpen) this.link?.close(reason ?? \"\");\n this.finish(1000, reason ?? \"\", false);\n }\n\n private async start(): Promise<void> {\n const { clock } = this.o;\n try {\n // a relay that comes up after the first `open` gets one at once, not at the next resend (spec s.4)\n this.room = await NostrRoom.open({ ...this.o, onChange: () => this.relaysChanged() });\n } catch (e) {\n this.fail(String(e));\n return;\n }\n const room = this.room;\n if (this.readyState === 3) {\n room.close();\n return;\n }\n room.onFrame = (f) => this.onFrame(f);\n this.cancelDeadline = clock.setTimeout(() => this.fail(\"the host did not answer through the Nostr relays\"), this.o.openTimeoutMs ?? GIVE_UP_MS);\n if (!(await room.whenUp(this.o.relayTimeoutMs ?? JOINER_FIRST_RELAY_MS))) {\n this.fail(\"no Nostr relay reachable\");\n return;\n }\n if (this.readyState !== 0) return;\n const open = () => void room.send(\"\", { type: \"open\", seq: 0, ack: 0 });\n this.relaysSeen = room.relaysConnected;\n this.sendOpen = open;\n open();\n this.cancelOpen = clock.setInterval(open, this.o.openEveryMs ?? OPEN_EVERY_MS);\n }\n\n private relaysChanged(): void {\n const up = this.room?.relaysConnected ?? 0;\n if (up > this.relaysSeen && this.readyState === 0) this.sendOpen?.();\n this.relaysSeen = up;\n }\n\n private onFrame(f: Frame): void {\n if (this.readyState === 3 || f.to !== this.room?.pub) return;\n if (this.readyState === 0) {\n if (f.type !== \"open\") return;\n this.hostPub = f.from;\n this.sendOpen = null;\n this.cancelOpen?.();\n this.cancelDeadline?.();\n this.cancelOpen = this.cancelDeadline = null;\n const room = this.room!;\n this.link = new ReliableLink({\n clock: this.o.clock,\n send: (out) => void room.send(this.hostPub, out),\n deliver: (text) => this.readyState === 1 && this.onmessage?.({ data: text }),\n onPeerClose: (reason) => this.finish(1000, reason, true),\n onDead: (reason) => this.finish(1006, reason, false),\n });\n this.readyState = 1;\n this.onopen?.();\n return;\n }\n if (f.from !== this.hostPub) return;\n if (f.type === \"open\") {\n this.link?.heard();\n return;\n }\n this.link?.receive(f);\n }\n\n private fail(why: string): void {\n if (this.readyState === 3) return;\n this.failure = why;\n if (this.readyState === 0) this.onerror?.({ message: why });\n this.finish(1006, why, false);\n }\n\n private finish(code: number, reason: string, clean: boolean): void {\n if (this.readyState === 3) return;\n this.readyState = 3;\n this.cancelOpen?.();\n this.cancelDeadline?.();\n this.cancelOpen = this.cancelDeadline = null;\n this.link?.stop();\n this.room?.close(true);\n queueMicrotask(() => this.onclose?.({ code, reason, wasClean: clean }));\n }\n}\n"]}
|