Normalize NIP-71 participant pubkeys

This commit is contained in:
thePR0M3TH3AN committed 2025-10-29 21:41:30 -04:00
1 parent 7556d8d265
commit d1ec1dcb7b
2 files changed
+99 -4

No files matched your search

+55 -1
View File
@@ -2,6 +2,7 @@ import { ACCEPT_LEGACY_V1 } from "../constants.js";
import { deriveTitleFromEvent, magnetFromText } from "../videoEventUtils.js";
import { extractMagnetHints } from "../magnet.js";
import { devLogger } from "../utils/logger.js";
import { getCachedNostrTools } from "./toolkit.js";
function stringFromInput(value) {
if (value === null || value === undefined) {
@@ -16,6 +17,50 @@ function stringFromInput(value) {
return String(value).trim();
}
const HEX_32_BYTE_REGEX = /^[0-9a-f]{64}$/i;
function decodeNpubToHex(candidate) {
const globalDecoder =
typeof window !== "undefined" ? window?.NostrTools?.nip19?.decode : null;
const toolkit = getCachedNostrTools();
const toolkitDecoder = toolkit?.nip19?.decode;
const decoder =
typeof globalDecoder === "function" ? globalDecoder : toolkitDecoder;
if (typeof decoder !== "function") {
return null;
}
try {
const decoded = decoder(candidate);
if (decoded?.type === "npub" && typeof decoded.data === "string") {
return decoded.data.toLowerCase();
}
if (decoded?.type === "npub" && typeof decoded.data?.pubkey === "string") {
return decoded.data.pubkey.toLowerCase();
}
} catch (error) {
devLogger.warn("[nostr] Failed to decode npub participant pubkey", error);
}
return null;
}
function normalizeNostrPubkey(candidate) {
const value = stringFromInput(candidate);
if (!value) {
return null;
}
if (HEX_32_BYTE_REGEX.test(value)) {
return value.toLowerCase();
}
if (value.toLowerCase().startsWith("npub1")) {
const decoded = decodeNpubToHex(value);
if (decoded && HEX_32_BYTE_REGEX.test(decoded)) {
return decoded;
}
return null;
}
return null;
}
function normalizeUnixSeconds(value) {
if (value === null || value === undefined || value === "") {
return "";
@@ -224,11 +269,20 @@ function buildParticipantTag(participant) {
if (!participant || typeof participant !== "object") {
return null;
}
const pubkey = stringFromInput(participant.pubkey);
const rawPubkey = participant.pubkey;
const pubkey = normalizeNostrPubkey(rawPubkey);
if (!pubkey) {
const candidate = stringFromInput(rawPubkey);
if (candidate) {
devLogger.warn(
"[nostr] Dropping NIP-71 participant tag with invalid pubkey",
{ pubkey: candidate }
);
}
return null;
}
const relay = stringFromInput(participant.relay);
// NIP-71 "Other tags" mandates 32-byte hex participant pubkeys.
const values = ["p", pubkey];
if (relay) {
values.push(relay);
+44 -3
View File
@@ -6,6 +6,47 @@ import {
extractNip71MetadataFromTags,
} from "../js/nostr.js";
const participantNpub = "npub1bitvidparticipantfixture";
const participantHexFromNpub = "c".repeat(64);
const hadWindow = typeof globalThis.window !== "undefined";
const windowRef = hadWindow ? globalThis.window : {};
if (!hadWindow) {
globalThis.window = windowRef;
}
const hadNostrTools = typeof windowRef.NostrTools !== "undefined";
const nostrToolsRef = hadNostrTools ? windowRef.NostrTools : {};
if (!hadNostrTools) {
windowRef.NostrTools = nostrToolsRef;
}
const previousNip19 = nostrToolsRef.nip19;
nostrToolsRef.nip19 = {
...previousNip19,
decode: (value) => {
if (value === participantNpub) {
return { type: "npub", data: participantHexFromNpub };
}
if (typeof previousNip19?.decode === "function") {
return previousNip19.decode(value);
}
throw new Error("Unsupported npub fixture");
},
};
test.after(() => {
if (previousNip19) {
nostrToolsRef.nip19 = previousNip19;
} else {
delete nostrToolsRef.nip19;
}
if (!hadNostrTools) {
delete windowRef.NostrTools;
}
if (!hadWindow) {
delete globalThis.window;
}
});
test("buildNip71VideoEvent assembles rich metadata", () => {
const metadata = {
kind: 21,
@@ -53,8 +94,8 @@ test("buildNip71VideoEvent assembles rich metadata", () => {
],
hashtags: ["bitvid", "nostr"],
participants: [
{ pubkey: "a".repeat(64), relay: "wss://relay.example" },
{ pubkey: "b".repeat(64) },
{ pubkey: "A".repeat(64), relay: "wss://relay.example" },
{ pubkey: ` ${participantNpub} ` },
],
references: [
"https://example.com/info",
@@ -135,7 +176,7 @@ test("buildNip71VideoEvent assembles rich metadata", () => {
const participantTags = event.tags.filter((tag) => tag[0] === "p");
assert.deepEqual(participantTags, [
["p", "a".repeat(64), "wss://relay.example"],
["p", "b".repeat(64)],
["p", participantHexFromNpub],
]);
const referenceTags = event.tags.filter((tag) => tag[0] === "r");