From 6e6adb54cf18f9cdea2009f93d21f0ebfe675936 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Sun, 26 Oct 2025 18:37:35 -0400 Subject: [PATCH] Add nip44 support to nostr tools bootstrap --- js/nostr/toolkit.js | 44 +++++++++++++++++++- js/nostrToolsBootstrap.js | 87 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 129 insertions(+), 2 deletions(-) diff --git a/js/nostr/toolkit.js b/js/nostr/toolkit.js index 30f35015..2cb42138 100644 --- a/js/nostr/toolkit.js +++ b/js/nostr/toolkit.js @@ -32,6 +32,48 @@ const nostrToolsReadySource = ? globalScope.nostrToolsReady : nostrToolsReady; +function ensureCanonicalCrypto(candidate, scope = globalScope) { + if (!candidate || typeof candidate !== "object") { + return candidate; + } + + const canonical = + scope && typeof scope === "object" + ? scope.__BITVID_CANONICAL_NOSTR_TOOLS__ || null + : null; + + if (!canonical || typeof canonical !== "object") { + return candidate; + } + + const needsNip04 = !candidate.nip04 && canonical.nip04; + const needsNip44 = !candidate.nip44 && canonical.nip44; + + if (!needsNip04 && !needsNip44) { + return candidate; + } + + if (Object.isFrozen(candidate)) { + const augmented = { ...candidate }; + if (needsNip04) { + augmented.nip04 = canonical.nip04; + } + if (needsNip44) { + augmented.nip44 = canonical.nip44; + } + return Object.freeze(augmented); + } + + if (needsNip04) { + candidate.nip04 = canonical.nip04; + } + if (needsNip44) { + candidate.nip44 = canonical.nip44; + } + + return candidate; +} + export function normalizeToolkitCandidate(candidate) { if ( candidate && @@ -39,7 +81,7 @@ export function normalizeToolkitCandidate(candidate) { candidate.ok !== false && typeof candidate.then !== "function" ) { - return candidate; + return ensureCanonicalCrypto(candidate); } return null; } diff --git a/js/nostrToolsBootstrap.js b/js/nostrToolsBootstrap.js index 313a3a47..4155f637 100644 --- a/js/nostrToolsBootstrap.js +++ b/js/nostrToolsBootstrap.js @@ -56,6 +56,40 @@ const hasWorkingNip04 = (candidate) => typeof candidate.decrypt === "function" ); +const hasWorkingNip44 = (candidate) => + !!( + candidate && + typeof candidate === "object" && + (( + typeof candidate.encrypt === "function" && + typeof candidate.decrypt === "function" + ) || + (candidate.v2 && + typeof candidate.v2 === "object" && + typeof candidate.v2.encrypt === "function" && + typeof candidate.v2.decrypt === "function")) + ); + +const selectNip44Candidate = (moduleLike) => { + if (!moduleLike || typeof moduleLike !== "object") { + return null; + } + + if (hasWorkingNip44(moduleLike.nip44)) { + return moduleLike.nip44; + } + + if (hasWorkingNip44(moduleLike.default)) { + return moduleLike.default; + } + + if (hasWorkingNip44(moduleLike)) { + return moduleLike; + } + + return null; +}; + const toSerializableError = (error) => { if (!error) { return null; @@ -260,6 +294,11 @@ export function bootstrapNostrTools() { NODE_IMPORT_TIMEOUT, "node-nip04" ), + withTimeout( + () => import("nostr-tools/nip44"), + NODE_IMPORT_TIMEOUT, + "node-nip44" + ), ] : []; @@ -277,6 +316,14 @@ export function bootstrapNostrTools() { REMOTE_IMPORT_TIMEOUT, "esm-nip04" ), + withTimeout( + () => + import( + "https://esm.sh/nostr-tools@1.8.3?target=es2022&exports=nip44" + ), + REMOTE_IMPORT_TIMEOUT, + "esm-nip44" + ), ]; const localEntrypoints = discoverLocalEntrypoints(scope); @@ -296,6 +343,7 @@ export function bootstrapNostrTools() { const resolvedModules = []; let resolvedNip04 = null; + let resolvedNip44 = null; dynamicResults.forEach((settledResult) => { if (settledResult.status !== "fulfilled") { @@ -316,12 +364,25 @@ export function bootstrapNostrTools() { resolvedNip04 = result.value.nip04; return; } + if (!resolvedNip44 && result.label === "esm-nip44") { + const nip44Candidate = selectNip44Candidate(result.value); + if (nip44Candidate) { + resolvedNip44 = nip44Candidate; + return; + } + } const extracted = extractToolsFromModule(result.value); if (extracted && Object.keys(extracted).length > 0) { resolvedModules.push({ source: result.label, tools: extracted }); if (!resolvedNip04 && hasWorkingNip04(extracted?.nip04)) { resolvedNip04 = extracted.nip04; } + if (!resolvedNip44) { + const nip44Candidate = selectNip44Candidate(extracted); + if (nip44Candidate) { + resolvedNip44 = nip44Candidate; + } + } } }); @@ -398,6 +459,12 @@ export function bootstrapNostrTools() { if (!resolvedNip04 && hasWorkingNip04(extracted?.nip04)) { resolvedNip04 = extracted.nip04; } + if (!resolvedNip44) { + const nip44Candidate = selectNip44Candidate(extracted); + if (nip44Candidate) { + resolvedNip44 = nip44Candidate; + } + } break; } } @@ -409,9 +476,15 @@ export function bootstrapNostrTools() { if (!resolvedNip04 && hasWorkingNip04(existingGlobalTools?.nip04)) { resolvedNip04 = existingGlobalTools.nip04; } + if (!resolvedNip44) { + const nip44Candidate = selectNip44Candidate(existingGlobalTools); + if (nip44Candidate) { + resolvedNip44 = nip44Candidate; + } + } } - if (resolvedModules.length === 0 && !resolvedNip04) { + if (resolvedModules.length === 0 && !resolvedNip04 && !resolvedNip44) { const failure = { ok: false, reason: "Failed to resolve any nostr-tools helpers.", @@ -454,6 +527,15 @@ export function bootstrapNostrTools() { ); } + if (resolvedNip44) { + canonicalTools.nip44 = resolvedNip44; + devLogger.info("[bitvid] Initialized nostr nip44 helpers."); + } else { + userLogger.warn( + "[bitvid] NIP-44 helpers unavailable after bootstrap attempts." + ); + } + const frozenCanonical = Object.freeze({ ...canonicalTools }); try { @@ -476,6 +558,9 @@ export function bootstrapNostrTools() { if (!merged.nip04 && frozenCanonical.nip04) { merged.nip04 = frozenCanonical.nip04; } + if (!merged.nip44 && frozenCanonical.nip44) { + merged.nip44 = frozenCanonical.nip44; + } if ( typeof merged.generatePrivateKey !== "function" && typeof merged.generateSecretKey === "function"