Wait for nostr pool before platform lookup

This commit is contained in:
thePR0M3TH3AN committed 2025-10-05 22:37:23 -04:00
1 parent 908435f476
commit 1c805b053e
3 files changed
+175 -3

No files matched your search

+50 -1
View File
@@ -1731,6 +1731,7 @@ export { convertEventToVideo };
class NostrClient {
constructor() {
this.pool = null;
this.poolPromise = null;
this.pubkey = null;
this.relays = sanitizeRelayList(Array.from(DEFAULT_RELAY_URLS));
this.readRelays = Array.from(this.relays);
@@ -4096,7 +4097,7 @@ class NostrClient {
this.restoreLocalData();
try {
this.pool = new window.NostrTools.SimplePool();
await this.ensurePool();
const results = await this.connectToRelays();
const successfulRelays = results
.filter((r) => r.success)
@@ -4113,6 +4114,54 @@ class NostrClient {
}
}
ensurePool() {
if (this.pool) {
return Promise.resolve(this.pool);
}
if (this.poolPromise) {
return this.poolPromise;
}
const scope =
typeof window !== "undefined"
? window
: typeof globalThis !== "undefined"
? globalThis
: null;
const tools =
scope?.NostrTools ||
scope?.__BITVID_CANONICAL_NOSTR_TOOLS__ ||
(typeof globalThis !== "undefined" ? globalThis?.NostrTools : null);
const SimplePool = tools?.SimplePool;
if (typeof SimplePool !== "function") {
const error = new Error("NostrTools SimplePool is unavailable.");
error.code = "nostr-simplepool-unavailable";
return Promise.reject(error);
}
const creation = Promise.resolve().then(() => {
const instance = new SimplePool();
this.pool = instance;
return instance;
});
const shared = creation
.then((instance) => {
this.poolPromise = Promise.resolve(instance);
return instance;
})
.catch((error) => {
this.poolPromise = null;
throw error;
});
this.poolPromise = shared;
return shared;
}
// We subscribe to kind `0` purely as a liveness probe because almost every
// relay can answer it quickly. Either an `event` or `eose` signals success,
// while the 5s timer guards against relays that never respond. We immediately
+17 -2
View File
@@ -88,13 +88,28 @@ async function fetchAdminMetadata(adminPubkey) {
return null;
}
if (!nostrClient?.pool || !Array.isArray(nostrClient?.relays)) {
let pool;
try {
pool = await nostrClient.ensurePool();
} catch (error) {
console.warn("[platformAddress] Failed to initialize Nostr pool", error);
return null;
}
if (!pool) {
return null;
}
const relayUrls = Array.isArray(nostrClient?.relays)
? nostrClient.relays
: [];
if (relayUrls.length === 0) {
console.warn("[platformAddress] Nostr client is not ready.");
return null;
}
try {
const events = await nostrClient.pool.list(nostrClient.relays, [
const events = await pool.list(relayUrls, [
{ kinds: [0], authors: [adminPubkey], limit: 1 },
]);
+108
View File
@@ -49,6 +49,23 @@ const nostrToolsStub = {
return null;
},
},
SimplePool: class {
async list() {
return [];
}
sub() {
return {
on() {},
unsub() {},
};
}
async get() {
return null;
}
async ensureRelay() {
return { close() {}, url: "" };
}
},
};
window.NostrTools = nostrToolsStub;
@@ -59,6 +76,9 @@ globalThis.fetch = async () => {
};
const { splitAndZap } = await import("../js/payments/zapSplit.js");
const platformAddressModule = await import("../js/payments/platformAddress.js");
const { __resetPlatformAddressCache } = platformAddressModule;
const { nostrClient } = await import("../js/nostr.js");
function createDeps({
commentAllowed = 120,
@@ -180,6 +200,93 @@ async function testSplitMath() {
delete globalThis.__BITVID_PLATFORM_FEE_OVERRIDE__;
}
async function testWaitsForPoolBeforePlatformLookup() {
const previousOverride = globalThis.__BITVID_PLATFORM_FEE_OVERRIDE__;
globalThis.__BITVID_PLATFORM_FEE_OVERRIDE__ = 10;
__resetPlatformAddressCache();
const originalEnsurePool = nostrClient.ensurePool;
const originalPool = nostrClient.pool;
const originalPoolPromise = nostrClient.poolPromise;
nostrClient.pool = null;
nostrClient.poolPromise = null;
let poolReady = false;
let listCalls = 0;
let ensureCalls = 0;
const poolStub = {
async list(relays, filters) {
assert(poolReady, "should wait for ensurePool to resolve before listing metadata");
listCalls += 1;
return [
{
pubkey: "f".repeat(64),
content: JSON.stringify({ lud16: "[email protected]" }),
created_at: Math.floor(Date.now() / 1000),
},
];
},
};
nostrClient.ensurePool = () => {
ensureCalls += 1;
if (nostrClient.poolPromise) {
return nostrClient.poolPromise;
}
const promise = new Promise((resolve) => {
setTimeout(() => {
poolReady = true;
nostrClient.pool = poolStub;
nostrClient.poolPromise = Promise.resolve(poolStub);
resolve(poolStub);
}, 10);
});
nostrClient.poolPromise = promise;
return promise;
};
const { deps: baseDeps } = createDeps();
const deps = {
lnurl: baseDeps.lnurl,
wallet: baseDeps.wallet,
platformAddress: platformAddressModule,
};
const videoEvent = {
id: "event-id",
pubkey: "c".repeat(64),
lightningAddress: "[email protected]",
tags: [["d", "pointer"]],
kind: 30078,
};
try {
const result = await splitAndZap(
{ videoEvent, amountSats: 1000, comment: "Delayed pool" },
deps
);
assert.equal(ensureCalls, 1, "should initialize the pool once");
assert.equal(listCalls, 1, "should fetch platform metadata once the pool is ready");
assert.equal(result.platformShare, 100);
assert.equal(result.receipts.length, 2);
} finally {
nostrClient.ensurePool = originalEnsurePool;
nostrClient.pool = originalPool;
nostrClient.poolPromise = originalPoolPromise;
__resetPlatformAddressCache();
if (previousOverride === undefined) {
delete globalThis.__BITVID_PLATFORM_FEE_OVERRIDE__;
} else {
globalThis.__BITVID_PLATFORM_FEE_OVERRIDE__ = previousOverride;
}
}
}
async function testStringFeeOverride() {
const videoEvent = {
id: "event-id",
@@ -298,6 +405,7 @@ async function testWalletFailure() {
}
await testSplitMath();
await testWaitsForPoolBeforePlatformLookup();
await testLnurlBounds();
await testMissingAddress();
await testWalletFailure();