mirror of
https://github.com/nostr-protocol/nips.git
synced 2024-09-20 14:55:49 -04:00
Add NIP-34
This commit is contained in:
parent
fab6a21a77
commit
9ee0d7d335
72
34.md
Normal file
72
34.md
Normal file
|
@ -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", <sub_id>, { 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: '<nip34a value>' }` 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", <sub_id>, { 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 |
|
51
34a.md
Normal file
51
34a.md
Normal file
|
@ -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.
|
Loading…
Reference in New Issue
Block a user