From a784792bb438bad152b00bbef2af66896d0a11a8 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Fri, 13 Sep 2024 12:56:50 -0400 Subject: [PATCH] read permissions --- 76.md | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 76.md diff --git a/76.md b/76.md new file mode 100644 index 00000000..97f761d9 --- /dev/null +++ b/76.md @@ -0,0 +1,81 @@ +NIP-76 +====== + +Relay Read Permissions +---------------------- + +`draft` `optional` + +Tag names `rp` (read permission) and `prp` (probabilistic read permission) define which keys are authorized to download an event from the relay. + +Events that include an `rp` or `prp` require AUTH to be downloaded. + +## Read Permission + +The `rp` tag accepts a list of pubkeys + +```json +["rp", "", "", ""] +``` + +Relays MUST check if the authed user is one of the keys in the `rp` if present. + +## Probabilistic Read Permissions + +Bloom filters are bit arrays that encode keys `n` times. They are represented by a base64 encoded tag value with the `n` as the third element. + +```json +["prp", "::"] +``` + +Bloom filters MUST use `SHA-256` functions of the key + iterating index as the psedocode below: + +```js +class BloomFilter(size: Int, n: Int, buffer: ByteArray) { + val bits = BitArray(buffer) + + fun bitIndex(value: ByteArray, index: Byte) { + return BigInt(sha256(value || index)) % size + } + + fun add(pubkey: HexKey) { + val value = pubkey.hexToByteArray() + + for (index in 0..n) { + bits[bitIndex(value, index)] = true + } + } + + fun mightContains(pubkey: HexKey): Boolean { + val value = pubkey.hexToByteArray() + + for (index in 0..n) { + if (!bits[bitIndex(value, index)]) { + return false + } + } + + return true + } + + fun encode() = size + ":" + rounds + ":" + base64Enc(bits.toByteArray()) // base64 might include extra 0 bits to fill the last byte + + fun decode(str: String): BloomFilter { + val parts = str.split(":") + return BloomFilter(parts[0].toInt(), parts[1].toInt(), base64Decode(bits.toByteArray())) + } +} +``` + +Relays MUST check if the authed user is in the filter before returning the event. + +### Test cases + +The filter below has 100 bits, with 10 rounds or hashes that should be able to match 10,000,000 keys without a single false positive. + +```json +["prp", "100:10:QGKCgBEBAAhIAApO"] +``` + +It includes keys `ca29c211f1c72d5b6622268ff43d2288ea2b2cb5b9aa196ff9f1704fc914b71b` and `460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c` +