From 19547396241dc8f16e90a4704880ea0cc4df0828 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 2 Feb 2026 19:59:24 +0000 Subject: [PATCH] Fix NIP-07 decryption fallback and storage unlock issues - Updated `StorageService` to check `signer.capabilities` before calling encryption methods, ensuring graceful fallback from NIP-44 to NIP-04. - Updated `UserBlockList` to check `signer.capabilities` before registering decryptors, preventing "Decryption timed out" errors caused by queueing unsupported method calls. - Fixed flakiness in `tests/user-blocks.test.mjs` by adding delays to allow background promises to settle between tests. - Verified changes with reproduction test case and existing test suite. Co-authored-by: PR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> --- js/services/storageService.js | 15 +++++++++------ js/userBlocks.js | 7 +++++-- tests/user-blocks.test.mjs | 4 ++++ 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/js/services/storageService.js b/js/services/storageService.js index 177ac829..aa742adc 100644 --- a/js/services/storageService.js +++ b/js/services/storageService.js @@ -276,7 +276,9 @@ export class StorageService { // 2. Encrypt hex key with signer (encrypt to self) // We prefer NIP-44 if available, else NIP-04 - if (typeof signer.nip44Encrypt === "function") { + const caps = signer.capabilities || { nip04: true, nip44: true }; + + if (caps.nip44 && typeof signer.nip44Encrypt === "function") { try { const ciphertext = await signer.nip44Encrypt(pubkey, hexKey); return { method: "nip44", ciphertext }; @@ -285,10 +287,10 @@ export class StorageService { } } - if (typeof signer.nip04Encrypt === "function") { + if (caps.nip04 && typeof signer.nip04Encrypt === "function") { const ciphertext = await signer.nip04Encrypt(pubkey, hexKey); return { method: "nip04", ciphertext }; - } else if (typeof signer.encrypt === "function") { + } else if (caps.nip04 && typeof signer.encrypt === "function") { // Legacy NIP-04 direct method on some signer objects const ciphertext = await signer.encrypt(pubkey, hexKey); return { method: "nip04", ciphertext }; @@ -306,15 +308,16 @@ export class StorageService { */ async _decryptMasterKey(encryptedData, signer, pubkey) { const { method, ciphertext } = encryptedData; + const caps = signer.capabilities || { nip04: true, nip44: true }; let hexKey = null; - if (method === "nip44" && typeof signer.nip44Decrypt === "function") { + if (method === "nip44" && caps.nip44 && typeof signer.nip44Decrypt === "function") { hexKey = await signer.nip44Decrypt(pubkey, ciphertext); } else if (method === "nip04" || !method) { // Default to NIP-04 if method not specified (legacy) - if (typeof signer.nip04Decrypt === "function") { + if (caps.nip04 && typeof signer.nip04Decrypt === "function") { hexKey = await signer.nip04Decrypt(pubkey, ciphertext); - } else if (typeof signer.decrypt === "function") { + } else if (caps.nip04 && typeof signer.decrypt === "function") { hexKey = await signer.decrypt(pubkey, ciphertext); } } diff --git a/js/userBlocks.js b/js/userBlocks.js index 1e3b9730..54a0b2e6 100644 --- a/js/userBlocks.js +++ b/js/userBlocks.js @@ -983,8 +983,11 @@ class UserBlockListManager { signer = await nostrClient.ensureActiveSignerForPubkey(normalized); } - const signerHasNip04 = typeof signer?.nip04Decrypt === "function"; - const signerHasNip44 = typeof signer?.nip44Decrypt === "function"; + const caps = signer?.capabilities || {}; + const signerHasNip04 = + caps.nip04 !== false && typeof signer?.nip04Decrypt === "function"; + const signerHasNip44 = + caps.nip44 !== false && typeof signer?.nip44Decrypt === "function"; const signerAvailable = Boolean(signer); const signerHasDecryptors = signerHasNip04 || signerHasNip44; const signerStatus = signerAvailable diff --git a/tests/user-blocks.test.mjs b/tests/user-blocks.test.mjs index 39dbdef8..55a7bdd8 100644 --- a/tests/user-blocks.test.mjs +++ b/tests/user-blocks.test.mjs @@ -166,6 +166,8 @@ await (async () => { } })(); +await new Promise((resolve) => setTimeout(resolve, 100)); + await (async () => { const actor = "a".repeat(64); const initialBlocked = "b".repeat(64); @@ -328,6 +330,8 @@ await (async () => { } })(); +await new Promise((resolve) => setTimeout(resolve, 100)); + await (async () => { const actor = "1".repeat(64); const seedOneHex = "2".repeat(64);