mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Refactor decryptLegacyDm to use ordered parallel race for decryptors.
Replaces Promise.any with raceOrdered utility to respect decryptor preference order while maintaining parallel execution. Adds regression test for order preference. Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
9913e484ab
commit
56ffdb693d
2 files changed
+108
-1
No files matched your search
+51
-1
@@ -467,6 +467,56 @@ async function decryptGiftWrap(event, decryptors, actorPubkey) {
|
||||
});
|
||||
}
|
||||
|
||||
function raceOrdered(promises) {
|
||||
if (!promises || promises.length === 0) {
|
||||
return Promise.reject(new AggregateError([], "No promises to race"));
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let pendingCount = promises.length;
|
||||
const errors = new Array(promises.length);
|
||||
const results = new Array(promises.length);
|
||||
const states = new Array(promises.length).fill("pending");
|
||||
let scanIndex = 0;
|
||||
|
||||
promises.forEach((p, i) => {
|
||||
Promise.resolve(p).then(
|
||||
(val) => {
|
||||
states[i] = "fulfilled";
|
||||
results[i] = val;
|
||||
|
||||
if (i === scanIndex) {
|
||||
resolve(val);
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
states[i] = "rejected";
|
||||
errors[i] = err;
|
||||
pendingCount--;
|
||||
|
||||
if (i === scanIndex) {
|
||||
while (scanIndex < promises.length) {
|
||||
if (states[scanIndex] === "fulfilled") {
|
||||
resolve(results[scanIndex]);
|
||||
return;
|
||||
}
|
||||
if (states[scanIndex] === "rejected") {
|
||||
scanIndex++;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pendingCount === 0 && scanIndex >= promises.length) {
|
||||
reject(new AggregateError(errors, "All promises rejected"));
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function decryptLegacyDm(event, decryptors, actorPubkey) {
|
||||
const ciphertext = typeof event?.content === "string" ? event.content : "";
|
||||
const senderPubkey = normalizeHex(event?.pubkey);
|
||||
@@ -564,7 +614,7 @@ async function decryptLegacyDm(event, decryptors, actorPubkey) {
|
||||
// Race all decryption attempts to return the first successful result
|
||||
// This significantly reduces latency compared to sequential attempts
|
||||
try {
|
||||
return await Promise.any(attempts);
|
||||
return await raceOrdered(attempts);
|
||||
} catch (aggregateError) {
|
||||
const aggregatedErrors = aggregateError.errors || [];
|
||||
errors.push(...aggregatedErrors);
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { decryptDM } from "../js/dmDecryptor.js";
|
||||
|
||||
const SENDER_HEX = "a".repeat(64);
|
||||
const RECIPIENT_HEX = "b".repeat(64);
|
||||
|
||||
test("decryptDM prefers higher priority decryptor even if slower", async () => {
|
||||
// Mock event
|
||||
const event = {
|
||||
kind: 4,
|
||||
content: "ciphertext",
|
||||
pubkey: SENDER_HEX,
|
||||
tags: [["p", RECIPIENT_HEX]]
|
||||
};
|
||||
|
||||
const context = {
|
||||
actorPubkey: RECIPIENT_HEX,
|
||||
decryptors: [
|
||||
{
|
||||
scheme: "preferred",
|
||||
priority: 0,
|
||||
source: "slow-preferred",
|
||||
decrypt: async (remote, cipher) => {
|
||||
// Normalize remote key might be done inside decryptDM
|
||||
if (remote === SENDER_HEX) {
|
||||
await new Promise(r => setTimeout(r, 100));
|
||||
return "preferred-plaintext";
|
||||
}
|
||||
throw new Error("wrong remote: " + remote);
|
||||
}
|
||||
},
|
||||
{
|
||||
scheme: "fallback",
|
||||
priority: 10,
|
||||
source: "fast-fallback",
|
||||
decrypt: async (remote, cipher) => {
|
||||
if (remote === SENDER_HEX) {
|
||||
// Fast!
|
||||
return "fallback-plaintext";
|
||||
}
|
||||
throw new Error("wrong remote: " + remote);
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const result = await decryptDM(event, context);
|
||||
|
||||
assert.equal(result.ok, true, "Result should be ok. Errors: " + JSON.stringify(result.errors));
|
||||
|
||||
// If Promise.any is used, it will likely pick "fallback-plaintext" (fast).
|
||||
// We WANT "preferred-plaintext".
|
||||
assert.equal(result.plaintext, "preferred-plaintext",
|
||||
`Expected preferred-plaintext but got ${result.plaintext}. Current implementation likely uses Promise.any which is sensitive to race conditions.`);
|
||||
});
|
||||
Reference in new issue
Block a user