mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Adding a second hashtag interest/disinterest quickly showed "didn't work" yet the change persisted after a refresh. Cause: the replaceable preferences event used created_at = floor(now/1000), so two edits in the same wall-clock second produced the SAME created_at. Relays reject the second as "not newer" (client shows an error) while relays that break created_at ties by id still store it (so it appears after a refresh) — an inconsistent false-negative. Fix: nextReplaceableCreatedAt(lastCreatedAt) (js/nostr/replaceableCreatedAt.js) — always advance to max(now, lastCreatedAt + 1), so each publish is strictly newer than the last and every relay accepts it unambiguously. Reusable for the other replaceable lists (subscriptions/blocks) that have the same latent collision. Tests: tests/replaceable-created-at.test.mjs (5 — same-second bump, clock-ahead, no-prior, rapid successive strictly-increasing, floors fractional). Lint + build green. Co-Authored-By: Claude Opus 4.8 <[email protected]>
22 lines
1.2 KiB
JavaScript
22 lines
1.2 KiB
JavaScript
// Monotonic created_at for replaceable events (NIP-01: kind 30000 subscriptions,
|
|
// 30015/30078 preference notes, 10000 blocks, etc). Relays keep only the newest
|
|
// event per (kind, pubkey, d-tag) and reject/ignore an update whose created_at is
|
|
// not STRICTLY greater than the stored one. Two quick edits within the same
|
|
// wall-clock second otherwise collide: the second is rejected as "not newer" (the
|
|
// client shows an error) yet lands on relays that break same-created_at ties by id
|
|
// (so it "failed but works after refresh"). Always advance past the last-published
|
|
// created_at to remove the collision.
|
|
//
|
|
// @param {number} lastCreatedAt - created_at of the last event we published (0 if none).
|
|
// @param {number} [nowSeconds] - current unix seconds (injectable for tests).
|
|
// @returns {number} A created_at strictly greater than lastCreatedAt.
|
|
export function nextReplaceableCreatedAt(lastCreatedAt, nowSeconds) {
|
|
const now = Number.isFinite(nowSeconds)
|
|
? Math.floor(nowSeconds)
|
|
: Math.floor(Date.now() / 1000);
|
|
const last = Number.isFinite(lastCreatedAt) ? Math.floor(lastCreatedAt) : 0;
|
|
return Math.max(now, last + 1);
|
|
}
|
|
|
|
export default nextReplaceableCreatedAt;
|