diff --git a/13.md b/13.md new file mode 100644 index 00000000..63e51699 --- /dev/null +++ b/13.md @@ -0,0 +1,93 @@ +NIP-13 +====== + +Proof of Work +------------- + +`draft` `optional` `author:jb55` `author:cameri` + +This NIP defines a way to generate and interpret Proof of Work for nostr notes. Proof of Work (PoW) is a way to add a proof of computational work to a note. This is a bearer proof which all relays and clients can universally validate with a small amount of code. This proof can be used as a means of spam deterrence. + +`difficulty` is defined to be the number of leading zero bits in the `NIP-01` id. For example, an id of `000000000e9d97a1ab09fc381030b346cdd7a142ad57e6df0b46dc9bef6c7e2d` has a difficulty of `36` with `36` leading 0 bits. + +Mining +------ + +To generate PoW for a `NIP-01` note, a `nonce` tag is used: + +```json +{"content": "It's just me mining my own business", "tags": [["nonce", "1", "20"]]} +``` + +When mining, the second entry to the nonce tag is updated, and then the id is recalculated (see [NIP-01](./01.md)). If the id has the desired number of leading zero bits, the note has been mined. It is recommended to update the `created_at` as well during this process. + +The third entry to the nonce tag `SHOULD` contain the target difficulty. This allows clients to protect against situations where bulk spammers targeting a lower difficulty get lucky and match a higher difficulty. For example, if you require 40 bits to reply to your thread and see a committed target of 30, you can safely reject it even if the note has 40 bits difficulty. Without a committed target difficulty you could not reject it. Committing to a target difficulty is something all honest miners should be ok with, and clients `MAY` reject a note matching a target difficulty if it is missing a difficulty commitment. + +Example mined note +------------------ + +```json +{ + "id": "000006d8c378af1779d2feebc7603a125d99eca0ccf1085959b307f64e5dd358", + "pubkey": "a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243", + "created_at": 1651794653, + "kind": 1, + "tags": [ + [ + "nonce", + "776797", + "20" + ] + ], + "content": "It's just me mining my own business", + "sig": "284622fc0a3f4f1303455d5175f7ba962a3300d136085b9566801bc2e0699de0c7e31e44c81fb40ad9049173742e904713c3594a1da0fc5d2382a25c11aba977" +} +``` + +Validating +---------- + +Here is some reference C code for calculating the difficulty (aka number of leading zero bits) in a nostr note id: + +```c +int zero_bits(unsigned char b) +{ + int n = 0; + + if (b == 0) + return 8; + + while (b >>= 1) + n++; + + return 7-n; +} + +/* find the number of leading zero bits in a hash */ +int count_leading_zero_bits(unsigned char *hash) +{ + int bits, total, i; + for (i = 0, total = 0; i < 32; i++) { + bits = zero_bits(hash[i]); + total += bits; + if (bits != 8) + break; + } + return total; +} +``` + +Querying relays for PoW notes +----------------------------- + +Since relays allow searching on prefixes, you can use this as a way to filter notes of a certain difficulty: + +``` +$ echo '["REQ", "subid", {"ids": ["000000000"]}]' | websocat wss://some-relay.com | jq -c '.[2]' +{"id":"000000000121637feeb68a06c8fa7abd25774bdedfa9b6ef648386fb3b70c387", ...} +``` + +Delegated Proof of Work +----------------------- + +Since the `NIP-01` note id does not commit to any signature, PoW can be outsourced to PoW providers, perhaps for a fee. This provides a way for clients to get their messages out to PoW restricted relays without having to do any work themselves, which is useful for energy constrained devices like on mobile diff --git a/README.md b/README.md index 3c4eb30a..8f703e34 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ NIPs stand for **Nostr Implementation Possibilities**. They exist to document wh - [NIP-10: Conventions for clients' use of `e` and `p` tags in text events.](10.md) - [NIP-11: Relay Information Document](11.md) - [NIP-12: Generic Tag Queries](12.md) +- [NIP-13: Proof of Work](13.md) ## Event Kinds