Skip to content

[BUG] Missing NIP-01 tie-breaker in eventRepository.upsert causes identically-timestamped events to be dropped #411

@YashIIT0909

Description

@YashIIT0909

Describe the bug
Nostream fails to adhere to the core NIP-01 tie-breaking rule for replaceable events (this behavior was originally defined in the now-deprecated NIP-16). The NIP-01 specification explicitly states: "In case of replaceable events with the same timestamp, the event with the lowest id (first in lexical order) should be retained, and the other discarded."

Currently, in src/repositories/event-repository.ts, the upsert method handles state updates correctly for differing timestamps, but strictly enforces .where('events.event_created_at', '<', row.event_created_at). If two replaceable events with the exact same created_at timestamp are processed concurrently (or sequentially), whichever event evaluates against the database row second will have the < condition resolve to false. It will silently fail with rowCount=0 and be discarded as a duplicate—even if it possesses a lower lexical id that should have won the NIP-01 tie-breaker.

To Reproduce
Steps to reproduce the behavior:

  1. Construct two valid replaceable events (e.g., kind: 10000, or other replaceable kinds) from the same pubkey with the exact same created_at integer timestamp.
  2. Ensure Event A has a lexicographically higher id than Event B.
  3. Publish Event A to the relay first. Wait for it to be accepted.
  4. Publish Event B to the relay.
  5. See error: Event B is silently dropped as a duplicate, and Event A remains persisted, violating the NIP-01 tie-breaker requirement.

Expected behavior
The relay should correctly identify that both events have an identical timestamp and evaluate their ids lexicographically. Event B (having the lower id) should successfully overwrite Event A, and should be broadcasted to subscribers as an update.

Screenshots
N/A

System (please complete the following information):

  • OS: [e.g. Ubuntu]
  • Platform: [e.g. docker, standalone]
  • Version: Latest / Master

Logs
N/A

Additional context
This bug lives in the Knex query builder logic under src/repositories/event-repository.ts on line 234:

.merge(omit(['event_pubkey', 'event_kind', 'event_deduplication'])(row))
.where('events.event_created_at', '<', row.event_created_at)

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions