Merge pull request #2128 from PR0M3TH3AN/perf/dm-decrypt-parallel-16182845594481363623

⚡ Parallelize DM Gift Wrap Decryption
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2026-02-05 15:57:34 -05:00
commit 81c329b87d
2 files changed
+105 -8

No files matched your search

+85
View File
@@ -0,0 +1,85 @@
import { decryptDM } from "../js/dmDecryptor.js";
// Valid 64-char hex strings
const WRAP_PUBKEY = "0000000000000000000000000000000000000000000000000000000000000001";
const SEAL_PUBKEY = "0000000000000000000000000000000000000000000000000000000000000002";
const SENDER_PUBKEY = "0000000000000000000000000000000000000000000000000000000000000003";
const RECIPIENT_PUBKEY = "0000000000000000000000000000000000000000000000000000000000000004";
const sealEvent = {
id: "seal-event",
pubkey: SEAL_PUBKEY,
content: "seal-ciphertext",
created_at: Math.floor(Date.now() / 1000),
};
const rumorEvent = {
id: "rumor-event",
pubkey: SENDER_PUBKEY,
content: "Hello World",
created_at: Math.floor(Date.now() / 1000),
tags: [["p", RECIPIENT_PUBKEY]],
};
const wrapEvent = {
kind: 1059,
pubkey: WRAP_PUBKEY,
content: "wrap-ciphertext",
created_at: Math.floor(Date.now() / 1000),
tags: [["p", RECIPIENT_PUBKEY]],
};
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const createDecryptor = (name, shouldFail, delayMs) => {
return {
scheme: "nip44",
supportsGiftWrap: true,
decrypt: async (pubkey, ciphertext, context) => {
await delay(delayMs);
if (shouldFail) {
throw new Error(`${name} failed`);
}
// If we are unwrapping the outer wrap, return the seal
if (context.stage === "wrap") {
return JSON.stringify(sealEvent);
}
// If we are unwrapping the seal, return the rumor
if (context.stage === "seal") {
return JSON.stringify(rumorEvent);
}
return "";
},
};
};
async function runBenchmark() {
console.log("Running DM Decrypt Benchmark...");
const decryptors = [
createDecryptor("D1", true, 100),
createDecryptor("D2", true, 100),
createDecryptor("D3", false, 50),
];
const context = {
actorPubkey: RECIPIENT_PUBKEY,
decryptors: decryptors,
};
const start = performance.now();
const result = await decryptDM(wrapEvent, context);
const end = performance.now();
console.log(`Result OK: ${result.ok}`);
if (!result.ok) {
const errorMessages = result.errors.map(e => ({stage: e.stage, message: e.error.message}));
console.log("Errors:", JSON.stringify(errorMessages, null, 2));
}
console.log(`Time taken: ${(end - start).toFixed(2)}ms`);
}
runBenchmark();
+20 -8
View File
@@ -388,7 +388,7 @@ async function decryptGiftWrap(event, decryptors, actorPubkey) {
const errors = [];
for (const decryptor of decryptors) {
const attemptUnwrap = async (decryptor) => {
try {
const sealSerialized = await decryptor.decrypt(wrapPubkey, ciphertext, {
event,
@@ -396,7 +396,8 @@ async function decryptGiftWrap(event, decryptors, actorPubkey) {
});
const seal = parseEventJson(sealSerialized, "wrap");
const sealCiphertext = typeof seal?.content === "string" ? seal.content : "";
const sealCiphertext =
typeof seal?.content === "string" ? seal.content : "";
const sealPubkey = normalizeHex(seal?.pubkey);
if (!sealCiphertext || !sealPubkey) {
const error = new Error(
@@ -407,10 +408,14 @@ async function decryptGiftWrap(event, decryptors, actorPubkey) {
throw error;
}
const rumorSerialized = await decryptor.decrypt(sealPubkey, sealCiphertext, {
event: seal,
stage: "seal",
});
const rumorSerialized = await decryptor.decrypt(
sealPubkey,
sealCiphertext,
{
event: seal,
stage: "seal",
},
);
const rumor = parseEventJson(rumorSerialized, "rumor");
const normalizedRumor = normalizeInnerMessage(rumor);
@@ -437,13 +442,20 @@ async function decryptGiftWrap(event, decryptors, actorPubkey) {
},
});
} catch (error) {
errors.push({
throw {
scheme: decryptor.scheme || "",
source: decryptor.source || "",
stage: error?.stage || "wrap",
error,
});
};
}
};
try {
return await Promise.any(decryptors.map(attemptUnwrap));
} catch (aggregateError) {
const aggregatedErrors = aggregateError.errors || [];
errors.push(...aggregatedErrors);
}
return buildDecryptResult({