From 9ee0d7d335ae29aad7831978989e82a2d2d19b84 Mon Sep 17 00:00:00 2001 From: arthurfranca Date: Fri, 2 Jun 2023 14:40:46 -0300 Subject: [PATCH] Add NIP-34 --- 34.md | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 34a.md | 51 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 34.md create mode 100644 34a.md diff --git a/34.md b/34.md new file mode 100644 index 00000000..f8a63648 --- /dev/null +++ b/34.md @@ -0,0 +1,72 @@ +NIP-34 +====== + +Algorithmic Filter +------------------ + +`draft` `optional` `author:arthurfranca` + +According to [NIP-01](01.md), filters with `limit` attribute are replied with events +sorted in **descending** order by the `created_at` event field (newest events first). + +For filters containing `limit` attribute, this NIP-34 adds `nip34` as a new filter attribute +whose value indicates the algorithm the `client` wishes to use for sorting events. + +For instance, if a `client` sends the message `["REQ", , { kinds: [1], ..., limit: 5, nip34: 'a' }]`, +it is asking the `relay` to sort five kind 1 events in **descending** order by the `nip34a` event field instead of by `created_at`. + +In the above example, `relays` that want to support the NIP-34a algorithm must add the `nip34a` event field to each event saved to their internal databases. The new field is populated following rules described at the NIP-34a NIP extension. + +Besides that, upon replying to such requests, the supporting `relay` MUST add `nip34: { score: '' }` field to each returned event JSON. + +It is a backward-compatible proposal because non-supporting `relays` ignore the `nip34` filter attribute and sort by `created_at`, as usual. + +`Relays` should advertise support using both [NIP-11](11.md) `supported_nips` and `supported_nip_extensions` fields. + +## Motivation + +Different algorithms are required to support diverse apps' event sorting needs. + +However, they should be presented as optional and backward-compatible features that respect +dumb `relays`/smart `clients` nostr philosophy so that any `relay` can easily adopt it. + +This NIP also takes into account the `relay` database software diversity, as explained ahead, +so to not rule out as many `relays` as possible. + +Considering the request example above, we expect most `relays` to simply swap the `created_at` field +on a regular query for the `nip34a` one. If a `relay` implementation uses a SQL DB, the above filter would turn this: + +`SELECT * FROM events WHERE kind in (1) ORDER BY created_at DESC LIMIT 5` + +Into this: + +`SELECT * FROM events WHERE kind in (1) ORDER BY nip34a DESC LIMIT 5` + +## How Clients Make Custom Requests for a Specific User + +The algorithms are global, meaning the "score" represented by the new event field isn't considering +specific user preferences. + +However, smart `clients` may keep track of pubkeys from whom the user is consuming content, also hashtags, for instance, and use such pubkeys/hashtags as a way to tailor the query to that specific user. For example: + +`["REQ", , { kinds: [1], limit: 5, nip34: 'b', authors: ["pubkey1 user follows", "pubkey2 user has read content from recently", "pubkey3 that user has liked content recently"] }, { kinds: [1], limit: 5, nip34: 'b', '#t': ['cat', 'bitcoin'] }]` + +## Rules for Submitting a NIP-34 Algorithm + +Each algorithm section describes **how** and **when** to compute the corresponding database field for each event. + +For example, [NIP-34a](34a.md) extension teaches `relays` the math used to update the `nip34a` event field. + +The NIP extension MUST have atleast one example in any programming language, preferably with inline comments. + +All algorithms' field values MUST be **unique strings** for each event. +This is needed to support the most rudimentary databases that `relays` may be using such as file DBs. +The event id may be appended to the string to make it unique, similar to NIP-34a solution. + +Bugfixes and small updates to embrace new event kinds may be submitted at any time by the algorithm author(s). + +## Available Algorithms + +| Extension | Name | Description | Modification Date | +| ------------- | ------------ | -------------------------------------------------- | ----------------- | +| [34a](34a.md) | Oldest First | Events with older `created_at` are retrieved first | 12 / jun / 2023 | diff --git a/34a.md b/34a.md new file mode 100644 index 00000000..99d1424d --- /dev/null +++ b/34a.md @@ -0,0 +1,51 @@ +NIP-34a +======= + +Oldest First +------------ + +`draft` `optional` `author:arthurfranca` + +Events with older `created_at` are retrieved first. + +## Implementation + +`Relay` computes `nip34a` field once upon receiving the event. + +The lower the `created_at`, the higher `nip34a` will be. + +### Javascript + +```js +// Use this same value even if your programming language allows a higher one +const maxDateNowSeconds = 8640000000000 // 8.64e15 ms / 1000 +// Equals 17280000000000. It is ok because it is lower than Number.MAX_SAFE_INTEGER +// We multiply by 2 because of "ts = seconds + maxDateNowSeconds" below +const maxTs = maxDateNowSeconds * 2 +const maxSecondsLength = maxTs.toString().length + +function getNip34a (createdAt, id = '') { + // The id length must always be the same to not affect sorting + if (id.length !== 64) { throw new Error('Wrong id length') } + let seconds = Math.trunc(createdAt) // Make sure it is int instead of float + seconds = Math.max(seconds, -maxDateNowSeconds) // Keep min limit + seconds = Math.min(seconds, maxDateNowSeconds) // Keep max limit + // Allow negative createdAt but remove minus sign + // because '-2' string timestamp is wrongly higher than '-1' + let ts = seconds + maxDateNowSeconds + // Make it lower the greater the ts is (the newer the createdAt is) + ts = maxTs - ts + // '10' is wrongly lower then '2' while '10' is higher than padded '02' + const paddedTsSeconds = ts.toString().padStart(maxSecondsLength, '0') + + // Event id is used as a fixed length unique identifier + return `${paddedTsSeconds}${id}` +} + +event.nip34a = getNip34a(event.created_at, event.id) +``` + +## Notes + +This implementation should be updated if we find out a popular programming +language doesn't support 17280000000000 int.