diff options
| author | Adam Soltys <[email protected]> | 2026-09-26 22:51:52 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-09-26 22:51:52 -0700 |
| commit | b3df6e629a8d165ac2d6f9fb6c4962d47da0f0cf (patch) | |
| tree | 72aeae840b025804248e2d0de33f1c488a501f7a /src/features/nwc.js | |
| parent | dd395c511973fc240a00a4e9295f4255da9b4935 (diff) | |
| download | coinosv3-b3df6e629a8d165ac2d6f9fb6c4962d47da0f0cf.tar.gz coinosv3-b3df6e629a8d165ac2d6f9fb6c4962d47da0f0cf.zip | |
Console: NWC chatter behind a debug switch; dead relays remembered longer and dropped from the pool.
`coinosDebug(true)` in the console (or ?debug=1) turns the nwc: listener
logs back on; they are off by default. Warnings and errors stay.
The red WebSocket lines were the pool's own reconnect loop redialling relays
from other people's relay lists that have been dead for weeks — the sick
list only stopped NEW dials, for ten minutes, and forgot on reload. A dead
relay is now dropped from the pool (no reconnect loop), skipped twice as
long each time it fails (up to a day), keyed by normalized URL, and the
verdict survives a reload. A relay the pool has failed to reconnect to
three times counts as dead.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
Diffstat (limited to 'src/features/nwc.js')
| -rw-r--r-- | src/features/nwc.js | 35 |
1 files changed, 18 insertions, 17 deletions
diff --git a/src/features/nwc.js b/src/features/nwc.js index b72f206..5b95975 100644 --- a/src/features/nwc.js +++ b/src/features/nwc.js @@ -27,6 +27,7 @@ import { subscribeOn, publishOn, queryOn, } from '../nostr.js'; import { t } from '../i18n.js'; +import { dlog } from '../debug.js'; import { qrSvg } from '../qr.js'; import { getNetwork } from '../api.js'; import { loadBg, saveBg, clearBg } from '../nwc-bg.js'; @@ -287,30 +288,30 @@ export function nwcFeature(ctx) { async function onRequest(c, ev) { // TEMP instrumentation for the Amethyst-spinner bug: every event that // reaches this handler logs, and every silent guard names itself. - console.log(`nwc: onRequest ev=${ev.id.slice(0, 8)} kind=${ev.kind} age=${nowSec() - ev.created_at}s from=${ev.pubkey.slice(0, 8)} conn=${c.id}`); - if (ev.kind !== REQ_KIND) return console.log('nwc: guard drop — wrong kind'); - if (handled.has(ev.id)) return console.log('nwc: guard drop — already handled', ev.id.slice(0, 8)); + dlog(`nwc: onRequest ev=${ev.id.slice(0, 8)} kind=${ev.kind} age=${nowSec() - ev.created_at}s from=${ev.pubkey.slice(0, 8)} conn=${c.id}`); + if (ev.kind !== REQ_KIND) return dlog('nwc: guard drop — wrong kind'); + if (handled.has(ev.id)) return dlog('nwc: guard drop — already handled', ev.id.slice(0, 8)); handled.add(ev.id); if (handled.size > 500) handled.clear(); if (ev.created_at && nowSec() - ev.created_at > MAX_AGE_SEC) { - return console.log('nwc: guard drop — too old', nowSec() - ev.created_at, 's'); + return dlog('nwc: guard drop — too old', nowSec() - ev.created_at, 's'); } // Same wallet, several tabs: the first to claim the request answers it. return withRequestLock(ev.id, () => serve(c, ev), - () => console.log('nwc: skip — another tab or the worker holds', ev.id.slice(0, 8))); + () => dlog('nwc: skip — another tab or the worker holds', ev.id.slice(0, 8))); } async function serve(c, ev) { // only the client this connection was issued to if (ev.pubkey !== c.clientPk) { - return console.log(`nwc: guard drop — pubkey mismatch got=${ev.pubkey.slice(0, 8)} want=${c.clientPk.slice(0, 8)}`); + return dlog(`nwc: guard drop — pubkey mismatch got=${ev.pubkey.slice(0, 8)} want=${c.clientPk.slice(0, 8)}`); } // Re-resolve the connection from stored state: the closure's `c` is a // snapshot from listen() time, so budget math computed from it restarts // from a stale value on every request (spends never accumulate), and a // connection revoked on another device would keep being served here. const cur = (load().conns || []).find((x) => x.id === c.id); - if (!cur || cur.revoked) return console.log('nwc: guard drop — connection gone or revoked', c.id); + if (!cur || cur.revoked) return dlog('nwc: guard drop — connection gone or revoked', c.id); c = cur; const scheme = ev.tags?.find((x) => x[0] === 'encryption')?.[1] === 'nip44_v2' @@ -356,13 +357,13 @@ export function nwcFeature(ctx) { const prior = await query(NWC_RELAYS, { kinds: [RES_KIND], '#e': [ev.id] }, 1500); return !!(prior && prior.length); }; - if (age > 5 && await lookBack()) return console.log('nwc: skip — request already answered elsewhere', ev.id.slice(0, 8)); + if (age > 5 && await lookBack()) return dlog('nwc: skip — request already answered elsewhere', ev.id.slice(0, 8)); if (mobile && age <= 5) await new Promise((r) => setTimeout(r, 1000)); claimed = await claim(ev.id); - if (claimed === false) return console.log('nwc: standing down — another device claimed', ev.id.slice(0, 8)); + if (claimed === false) return dlog('nwc: standing down — another device claimed', ev.id.slice(0, 8)); if (claimed === null && age <= 5) { await new Promise((r) => setTimeout(r, 300 + Math.random() * 900)); - if (await lookBack()) return console.log('nwc: skip — request already answered elsewhere', ev.id.slice(0, 8)); + if (await lookBack()) return dlog('nwc: skip — request already answered elsewhere', ev.id.slice(0, 8)); } } updateConn(c.id, { lastUsed: Date.now() }); @@ -380,7 +381,7 @@ export function nwcFeature(ctx) { await new Promise((r) => setTimeout(r, errGraceMs)); const prior = await query(NWC_RELAYS, { kinds: [RES_KIND], '#e': [ev.id] }, 1500).catch(() => []); if (prior && prior.length) { - return console.log('nwc: staying quiet — request answered elsewhere', ev.id.slice(0, 8)); + return dlog('nwc: staying quiet — request answered elsewhere', ev.id.slice(0, 8)); } } delete payload.sibling; @@ -598,7 +599,7 @@ export function nwcFeature(ctx) { const infoPublished = new Set(); // connection ids whose 13194 went out this session let listenGen = 0; // TEMP instrumentation: which listen() generation is live function stop() { - if (unsubs.length) console.log(`nwc: stop() closing ${unsubs.length} sub(s) from gen ${listenGen}`); + if (unsubs.length) dlog(`nwc: stop() closing ${unsubs.length} sub(s) from gen ${listenGen}`); for (const u of unsubs) { try { u(); } catch {} } unsubs = []; } @@ -617,17 +618,17 @@ export function nwcFeature(ctx) { )); } const list = conns(); - console.log(`nwc: listen() gen ${gen} — ${list.length} connection(s)`); + dlog(`nwc: listen() gen ${gen} — ${list.length} connection(s)`); for (const c of list) { - console.log(`nwc: gen ${gen} subscribing for conn=${c.id} service=${c.servicePk.slice(0, 8)}`); + dlog(`nwc: gen ${gen} subscribing for conn=${c.id} service=${c.servicePk.slice(0, 8)}`); unsubs.push(subscribe( NWC_RELAYS, { kinds: [REQ_KIND], '#p': [c.servicePk], since: nowSec() - MAX_AGE_SEC }, (ev) => { - console.log(`nwc: gen ${gen} event arrived ev=${ev.id.slice(0, 8)} for conn=${c.id}`); + dlog(`nwc: gen ${gen} event arrived ev=${ev.id.slice(0, 8)} for conn=${c.id}`); onRequest(c, ev).catch((e) => console.error('nwc: onRequest threw', e)); }, - { onclose: (reasons) => console.log(`nwc: gen ${gen} sub CLOSED for conn=${c.id}:`, JSON.stringify(reasons)) }, + { onclose: (reasons) => dlog(`nwc: gen ${gen} sub CLOSED for conn=${c.id}:`, JSON.stringify(reasons)) }, )); // Republish the capability event once per session per connection: a // client that can't find kind 13194 just spins, and a single publish at @@ -745,7 +746,7 @@ export function nwcFeature(ctx) { await enableBackground(); await reconcileBg(); render(); - } catch (e) { console.log('nwc: background answering not armed:', e.message); } + } catch (e) { dlog('nwc: background answering not armed:', e.message); } } // A connection that only answers while a tab happens to be open is a broken |
