mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 16:02:26 +00:00
Two replaceable-event (NIP-33/kind 30078) lifecycle fixes found auditing edit/delete/tombstone: 1. created_at monotonicity. Edits and delete tombstones used bare Date.now(), with no guarantee of being newer than the version they replace. NIP-01 keeps the highest created_at (ties -> lowest id), so a device clock that's behind — or a second edit within the same second — left relays serving the OLD version: edits silently didn't apply, and (worse) deletions didn't actually remove the video for other clients (the local in-memory tombstone masked the failure locally). The edit and revert/delete builders now use max(now, baseEvent.created_at + 1). created_at is threaded through revertVideo and _softDeleteVersions so the bulk-delete path keeps it too. 2. Legacy tombstone key mismatch. The deletion builder synthesizes videoRootId = "LEGACY:<pubkey>:<dTag>" for legacy videos, so its tombstone keyed as "ROOT:LEGACY:..." while the original legacy event (no videoRootId) keyed as "<pubkey>:<dTag>" — the tombstone never matched the zombie it was meant to suppress. getActiveKey now treats a "LEGACY:"-prefixed root as non-distinct and falls through to the pubkey:dTag identity, so both match. Verified sound and unchanged: d-tag + videoRootId preservation on edit, ownership/version checks, dual delete (NIP-09 kind-5 + per-d-tag soft-delete), and the tombstone guard wired across all ingestion paths. Tests: edit/tombstone created_at is bumped above a future-dated base (clock-skew) and uses wall-clock for a past base; a legacy deletion shares the original event's key. Both fixes mutation-verified. Co-Authored-By: Claude Opus 4.8 <[email protected]>
20 lines
824 B
JavaScript
20 lines
824 B
JavaScript
// js/nostr/utils.js
|
|
|
|
export function getActiveKey(video) {
|
|
// A "LEGACY:<pubkey>:<dTag>" videoRootId is synthesized by the deletion
|
|
// builder for legacy videos that have no real root. It must NOT be treated as
|
|
// a distinct root, otherwise a deletion tombstone keys as
|
|
// "ROOT:LEGACY:<pubkey>:<dTag>" while the original legacy event (which carries
|
|
// no videoRootId) keys as "<pubkey>:<dTag>", and the tombstone never matches
|
|
// the zombie it is meant to suppress. Fall through to the pubkey:dTag key so
|
|
// both map to the same identity.
|
|
if (video.videoRootId && !String(video.videoRootId).startsWith("LEGACY:")) {
|
|
return `ROOT:${video.videoRootId}`;
|
|
}
|
|
const dTag = video.tags?.find((t) => t[0] === "d");
|
|
if (dTag) {
|
|
return `${video.pubkey}:${dTag[1]}`;
|
|
}
|
|
return `LEGACY:${video.id}`;
|
|
}
|