mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-28 16:32:28 +00:00
Add session fallback signing to nostr publish flow
This commit is contained in:
1 parent
ebbddf77bc
commit
98dd51509d
2 files changed
+174
-2
No files matched your search
+80
-2
@@ -5293,7 +5293,85 @@ export class NostrClient {
|
||||
rejectionLogLevel = "error",
|
||||
} = {}
|
||||
) {
|
||||
const signedEvent = await window.nostr.signEvent(event);
|
||||
const extension = window?.nostr;
|
||||
const extensionSigner =
|
||||
extension && typeof extension.signEvent === "function"
|
||||
? extension.signEvent.bind(extension)
|
||||
: null;
|
||||
|
||||
const normalizedEventPubkey =
|
||||
event && typeof event.pubkey === "string"
|
||||
? event.pubkey.toLowerCase()
|
||||
: "";
|
||||
const normalizedLogged =
|
||||
typeof this.pubkey === "string" ? this.pubkey.toLowerCase() : "";
|
||||
const usingSessionActor =
|
||||
normalizedEventPubkey &&
|
||||
normalizedLogged &&
|
||||
normalizedEventPubkey !== normalizedLogged;
|
||||
|
||||
let eventToSign = event;
|
||||
let signedEvent = null;
|
||||
let signerPubkey = null;
|
||||
|
||||
if (
|
||||
extensionSigner &&
|
||||
normalizedEventPubkey &&
|
||||
normalizedEventPubkey === normalizedLogged
|
||||
) {
|
||||
signedEvent = await extensionSigner(event);
|
||||
} else {
|
||||
try {
|
||||
const currentSessionPubkey =
|
||||
typeof this.sessionActor?.pubkey === "string"
|
||||
? this.sessionActor.pubkey.toLowerCase()
|
||||
: "";
|
||||
|
||||
if (
|
||||
usingSessionActor &&
|
||||
normalizedEventPubkey &&
|
||||
normalizedEventPubkey !== currentSessionPubkey
|
||||
) {
|
||||
await this.ensureSessionActor(true);
|
||||
} else {
|
||||
await this.ensureSessionActor();
|
||||
}
|
||||
|
||||
const sessionActor = this.sessionActor;
|
||||
if (
|
||||
!sessionActor ||
|
||||
typeof sessionActor.pubkey !== "string" ||
|
||||
!sessionActor.pubkey ||
|
||||
typeof sessionActor.privateKey !== "string" ||
|
||||
!sessionActor.privateKey
|
||||
) {
|
||||
throw new Error("session-actor-unavailable");
|
||||
}
|
||||
|
||||
const normalizedSessionPubkey = sessionActor.pubkey.toLowerCase();
|
||||
if (
|
||||
!normalizedEventPubkey ||
|
||||
normalizedEventPubkey !== normalizedSessionPubkey ||
|
||||
event.pubkey !== sessionActor.pubkey
|
||||
) {
|
||||
eventToSign = { ...event, pubkey: sessionActor.pubkey };
|
||||
}
|
||||
|
||||
signedEvent = signEventWithPrivateKey(
|
||||
eventToSign,
|
||||
sessionActor.privateKey
|
||||
);
|
||||
signerPubkey = sessionActor.pubkey;
|
||||
} catch (error) {
|
||||
console.warn("[nostr] Failed to sign event with session key:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!signerPubkey && signedEvent && typeof signedEvent.pubkey === "string") {
|
||||
signerPubkey = signedEvent.pubkey;
|
||||
}
|
||||
|
||||
if (isDevMode) {
|
||||
console.log(`Signed ${devLogLabel} event:`, signedEvent);
|
||||
}
|
||||
@@ -5343,7 +5421,7 @@ export class NostrClient {
|
||||
});
|
||||
}
|
||||
|
||||
return { signedEvent, summary: publishSummary };
|
||||
return { signedEvent, summary: publishSummary, signerPubkey };
|
||||
}
|
||||
|
||||
async publishVideo(videoPayload, pubkey) {
|
||||
|
||||
@@ -722,6 +722,99 @@ async function testRecordVideoViewEmitsJsonPayload() {
|
||||
);
|
||||
}
|
||||
|
||||
async function testSignAndPublishFallbackUsesSessionActor() {
|
||||
const originalExtension = window.nostr;
|
||||
const originalPool = nostrClient.pool;
|
||||
const originalRelays = nostrClient.relays;
|
||||
const originalPubkey = nostrClient.pubkey;
|
||||
const originalSessionActor = nostrClient.sessionActor;
|
||||
const originalEnsureSessionActor = nostrClient.ensureSessionActor;
|
||||
const ensureCalls = [];
|
||||
const publishCalls = [];
|
||||
|
||||
try {
|
||||
delete window.nostr;
|
||||
|
||||
nostrClient.pool = {
|
||||
publish(urls, event) {
|
||||
publishCalls.push({ urls, event });
|
||||
return {
|
||||
on(eventName, handler) {
|
||||
if (eventName === "ok") {
|
||||
handler();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
nostrClient.relays = ["wss://relay.fallback"];
|
||||
nostrClient.pubkey = "logged-user";
|
||||
nostrClient.sessionActor = {
|
||||
pubkey: "session-actor",
|
||||
privateKey: "session-private",
|
||||
};
|
||||
nostrClient.ensureSessionActor = async (forceRenew = false) => {
|
||||
ensureCalls.push(forceRenew === true);
|
||||
return nostrClient.sessionActor.pubkey;
|
||||
};
|
||||
|
||||
const event = {
|
||||
kind: 1,
|
||||
pubkey: "session-actor",
|
||||
created_at: 123,
|
||||
tags: [],
|
||||
content: "hello fallback",
|
||||
};
|
||||
|
||||
const result = await nostrClient.signAndPublishEvent(event, {
|
||||
context: "fallback",
|
||||
logName: "fallback",
|
||||
});
|
||||
|
||||
assert.ok(result, "expected a publish result when falling back to the session actor");
|
||||
assert.equal(
|
||||
result.signedEvent.pubkey,
|
||||
"session-actor",
|
||||
"fallback signing should use the session actor pubkey",
|
||||
);
|
||||
assert.equal(
|
||||
result.signerPubkey,
|
||||
"session-actor",
|
||||
"signer pubkey should reflect the session actor when falling back",
|
||||
);
|
||||
assert.ok(
|
||||
ensureCalls.length > 0,
|
||||
"fallback signing should attempt to ensure the session actor",
|
||||
);
|
||||
assert.ok(
|
||||
Array.isArray(result.summary?.accepted) && result.summary.accepted.length > 0,
|
||||
"fallback publish should report accepted relays",
|
||||
);
|
||||
assert.equal(
|
||||
publishCalls.length,
|
||||
result.summary.accepted.length,
|
||||
"each accepted relay should correspond to a publish attempt",
|
||||
);
|
||||
assert.equal(
|
||||
result.summary.accepted[0]?.url,
|
||||
"wss://relay.fallback",
|
||||
"publish summary should surface the accepting relay",
|
||||
);
|
||||
} finally {
|
||||
if (typeof originalExtension === "undefined") {
|
||||
delete window.nostr;
|
||||
} else {
|
||||
window.nostr = originalExtension;
|
||||
}
|
||||
nostrClient.pool = originalPool;
|
||||
nostrClient.relays = originalRelays;
|
||||
nostrClient.pubkey = originalPubkey;
|
||||
nostrClient.sessionActor = originalSessionActor;
|
||||
nostrClient.ensureSessionActor = originalEnsureSessionActor;
|
||||
}
|
||||
}
|
||||
|
||||
// Cached totals older than VIEW_COUNT_CACHE_TTL_MS should be discarded and rehydrated from relays.
|
||||
async function testHydrationRefreshesAfterCacheTtl() {
|
||||
harness.reset();
|
||||
@@ -947,6 +1040,7 @@ await testLocalIngestNotifiesImmediately();
|
||||
await testUnsubscribeStopsCallbacks();
|
||||
await testRelayCountAggregationUsesBestEstimate();
|
||||
await testRecordVideoViewEmitsJsonPayload();
|
||||
await testSignAndPublishFallbackUsesSessionActor();
|
||||
await testHydrateHistoryPrefersRootEvent();
|
||||
await testModalPostedTimestampStaysInSync();
|
||||
|
||||
|
||||
Reference in new issue
Block a user