mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-30 01:12:39 +00:00
fix(zap): receipt validation works with nostr-tools 2.x SimplePool
After a successful split zap, validation errored: "Unable to initialize a relay pool for receipt validation." The validator built a fresh `new SimplePool()` and required `pool.list()`, but nostr-tools 2.x removed SimplePool.list() (the rest of the app only has it because js/nostr/toolkit.js shims it). So validation always failed even though the payment went through. Fix: resolveReceiptListPool() applies the app's legacy .list shim (shimLegacySimplePoolMethods) to the freshly created pool, and the gate now accepts a listEvents override instead of hard-requiring pool.list. tests/zap-receipt-pool.test.mjs: 2.x pool gets .list shimmed; createPool override shimmed; listEvents override leaves the pool as-is; null when neither available. Mutation-verified (removing the shim fails it). Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
bb33b12649
commit
0ec9db70e8
2 files changed
+102
-14
No files matched your search
@@ -1,7 +1,11 @@
|
||||
// js/payments/zapReceiptValidator.js
|
||||
|
||||
import { nostrToolsReady } from "../nostrToolsBootstrap.js";
|
||||
import { normalizeToolkitCandidate, readToolkitFromScope } from "../nostr/toolkit.js";
|
||||
import {
|
||||
normalizeToolkitCandidate,
|
||||
readToolkitFromScope,
|
||||
shimLegacySimplePoolMethods,
|
||||
} from "../nostr/toolkit.js";
|
||||
import { userLogger } from "../utils/logger.js";
|
||||
import { bech32, bytesToHex, sha256 } from "../../vendor/crypto-helpers.bundle.min.js";
|
||||
|
||||
@@ -212,6 +216,37 @@ function buildValidationResult({
|
||||
};
|
||||
}
|
||||
|
||||
// Build the relay pool used to fetch the zap receipt. nostr-tools 2.x SimplePool
|
||||
// no longer exposes .list(), so a freshly constructed pool can't query — which
|
||||
// made receipt validation always fail with "Unable to initialize a relay pool"
|
||||
// after an otherwise-successful zap. Apply the app's legacy .list shim (unless a
|
||||
// listEvents override supplies its own fetch path).
|
||||
export function resolveReceiptListPool(tools, overrides = {}) {
|
||||
let pool = null;
|
||||
try {
|
||||
if (typeof overrides.createPool === "function") {
|
||||
pool = overrides.createPool(tools);
|
||||
} else if (tools && typeof tools.SimplePool === "function") {
|
||||
pool = new tools.SimplePool();
|
||||
}
|
||||
} catch (error) {
|
||||
userLogger.warn("[zapReceiptValidator] Failed to create SimplePool instance.", error);
|
||||
return null;
|
||||
}
|
||||
if (
|
||||
pool &&
|
||||
typeof pool.list !== "function" &&
|
||||
typeof overrides.listEvents !== "function"
|
||||
) {
|
||||
try {
|
||||
shimLegacySimplePoolMethods(pool);
|
||||
} catch (error) {
|
||||
userLogger.warn("[zapReceiptValidator] Failed to shim SimplePool.list.", error);
|
||||
}
|
||||
}
|
||||
return pool;
|
||||
}
|
||||
|
||||
export async function validateZapReceipt(context = {}, overrides = {}) {
|
||||
const {
|
||||
zapRequest,
|
||||
@@ -330,19 +365,11 @@ export async function validateZapReceipt(context = {}, overrides = {}) {
|
||||
});
|
||||
}
|
||||
|
||||
let pool;
|
||||
try {
|
||||
if (typeof overrides.createPool === "function") {
|
||||
pool = overrides.createPool(tools);
|
||||
} else if (typeof tools.SimplePool === "function") {
|
||||
pool = new tools.SimplePool();
|
||||
}
|
||||
} catch (error) {
|
||||
userLogger.warn("[zapReceiptValidator] Failed to create SimplePool instance.", error);
|
||||
pool = null;
|
||||
}
|
||||
|
||||
if (!pool || typeof pool.list !== "function") {
|
||||
const pool = resolveReceiptListPool(tools, overrides);
|
||||
const canListEvents =
|
||||
typeof overrides.listEvents === "function" ||
|
||||
(pool && typeof pool.list === "function");
|
||||
if (!pool || !canListEvents) {
|
||||
return buildValidationResult({
|
||||
status: "failed",
|
||||
reason: "Unable to initialize a relay pool for receipt validation.",
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// Regression: after a successful zap, receipt validation failed with "Unable to
|
||||
// initialize a relay pool for receipt validation." nostr-tools 2.x SimplePool no
|
||||
// longer has .list(), and the validator constructed a fresh raw pool and required
|
||||
// pool.list. resolveReceiptListPool must apply the app's legacy .list shim so a
|
||||
// 2.x pool can query — and honor a listEvents override without needing .list.
|
||||
|
||||
import "./test-helpers/setup-localstorage.mjs";
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { resolveReceiptListPool } from "../js/payments/zapReceiptValidator.js";
|
||||
|
||||
// Mimics a nostr-tools 2.x SimplePool: instances have subscribeMany/close but NO
|
||||
// .list() (the method was removed in 2.x).
|
||||
function makeV2Tools() {
|
||||
function SimplePool() {
|
||||
this.subscribeMany = () => ({ close() {} });
|
||||
this.querySync = async () => [];
|
||||
this.close = () => {};
|
||||
}
|
||||
return { SimplePool };
|
||||
}
|
||||
|
||||
test("a freshly constructed 2.x pool (no .list) gets the legacy .list shim", () => {
|
||||
const tools = makeV2Tools();
|
||||
const bare = new tools.SimplePool();
|
||||
assert.equal(typeof bare.list, "undefined", "2.x pool starts without .list");
|
||||
|
||||
const pool = resolveReceiptListPool(tools, {});
|
||||
assert.ok(pool, "a pool must be returned");
|
||||
assert.equal(
|
||||
typeof pool.list,
|
||||
"function",
|
||||
"the shim must add .list so receipt validation can query relays",
|
||||
);
|
||||
});
|
||||
|
||||
test("createPool override is respected and shimmed when it lacks .list", () => {
|
||||
const created = { subscribeMany: () => ({ close() {} }), close: () => {} };
|
||||
const pool = resolveReceiptListPool({}, { createPool: () => created });
|
||||
assert.equal(pool, created, "must use the override-created pool");
|
||||
assert.equal(typeof pool.list, "function", "and shim it with .list");
|
||||
});
|
||||
|
||||
test("a listEvents override means the pool is left as-is (no shim required)", () => {
|
||||
const created = { close: () => {} }; // no list, no subscribeMany
|
||||
const pool = resolveReceiptListPool(
|
||||
{},
|
||||
{ createPool: () => created, listEvents: async () => [] },
|
||||
);
|
||||
assert.equal(pool, created);
|
||||
assert.equal(
|
||||
typeof pool.list,
|
||||
"undefined",
|
||||
"with a listEvents override the validator uses it, so .list isn't forced",
|
||||
);
|
||||
});
|
||||
|
||||
test("returns null when there is no SimplePool and no override", () => {
|
||||
const pool = resolveReceiptListPool({}, {});
|
||||
assert.equal(pool, null);
|
||||
});
|
||||
Reference in new issue
Block a user