diff options
| author | William Casarin <jb55@jb55.com> | 2022-05-05 10:45:27 -0700 |
|---|---|---|
| committer | fiatjaf <fiatjaf@gmail.com> | 2022-05-06 22:04:57 -0300 |
| commit | d7a4aad4a0039285f2bc4723c2898593dca0d6c4 (patch) | |
| tree | a94332ce141580d2d27bb0aeeb81ed11b277fe22 | |
| parent | ad1eb96c2147d30f148480aac32aa98631f1d0b5 (diff) | |
Proof of Work
Thanks-to: Cameri and scsibug for the initial idea of using the id for PoW
Thanks-to: David A. Harding for the difficulty commitment idea
Signed-off-by: William Casarin <jb55@jb55.com>
| -rw-r--r-- | 13.md | 93 | ||||
| -rw-r--r-- | README.md | 1 |
2 files changed, 94 insertions, 0 deletions
| @@ -0,0 +1,93 @@ | |||
| 1 | NIP-13 | ||
| 2 | ====== | ||
| 3 | |||
| 4 | Proof of Work | ||
| 5 | ------------- | ||
| 6 | |||
| 7 | `draft` `optional` `author:jb55` `author:cameri` | ||
| 8 | |||
| 9 | 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. | ||
| 10 | |||
| 11 | `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. | ||
| 12 | |||
| 13 | Mining | ||
| 14 | ------ | ||
| 15 | |||
| 16 | To generate PoW for a `NIP-01` note, a `nonce` tag is used: | ||
| 17 | |||
| 18 | ```json | ||
| 19 | {"content": "It's just me mining my own business", "tags": [["nonce", "1", "20"]]} | ||
| 20 | ``` | ||
| 21 | |||
| 22 | 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. | ||
| 23 | |||
| 24 | 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. | ||
| 25 | |||
| 26 | Example mined note | ||
| 27 | ------------------ | ||
| 28 | |||
| 29 | ```json | ||
| 30 | { | ||
| 31 | "id": "000006d8c378af1779d2feebc7603a125d99eca0ccf1085959b307f64e5dd358", | ||
| 32 | "pubkey": "a48380f4cfcc1ad5378294fcac36439770f9c878dd880ffa94bb74ea54a6f243", | ||
| 33 | "created_at": 1651794653, | ||
| 34 | "kind": 1, | ||
| 35 | "tags": [ | ||
| 36 | [ | ||
| 37 | "nonce", | ||
| 38 | "776797", | ||
| 39 | "20" | ||
| 40 | ] | ||
| 41 | ], | ||
| 42 | "content": "It's just me mining my own business", | ||
| 43 | "sig": "284622fc0a3f4f1303455d5175f7ba962a3300d136085b9566801bc2e0699de0c7e31e44c81fb40ad9049173742e904713c3594a1da0fc5d2382a25c11aba977" | ||
| 44 | } | ||
| 45 | ``` | ||
| 46 | |||
| 47 | Validating | ||
| 48 | ---------- | ||
| 49 | |||
| 50 | Here is some reference C code for calculating the difficulty (aka number of leading zero bits) in a nostr note id: | ||
| 51 | |||
| 52 | ```c | ||
| 53 | int zero_bits(unsigned char b) | ||
| 54 | { | ||
| 55 | int n = 0; | ||
| 56 | |||
| 57 | if (b == 0) | ||
| 58 | return 8; | ||
| 59 | |||
| 60 | while (b >>= 1) | ||
| 61 | n++; | ||
| 62 | |||
| 63 | return 7-n; | ||
| 64 | } | ||
| 65 | |||
| 66 | /* find the number of leading zero bits in a hash */ | ||
| 67 | int count_leading_zero_bits(unsigned char *hash) | ||
| 68 | { | ||
| 69 | int bits, total, i; | ||
| 70 | for (i = 0, total = 0; i < 32; i++) { | ||
| 71 | bits = zero_bits(hash[i]); | ||
| 72 | total += bits; | ||
| 73 | if (bits != 8) | ||
| 74 | break; | ||
| 75 | } | ||
| 76 | return total; | ||
| 77 | } | ||
| 78 | ``` | ||
| 79 | |||
| 80 | Querying relays for PoW notes | ||
| 81 | ----------------------------- | ||
| 82 | |||
| 83 | Since relays allow searching on prefixes, you can use this as a way to filter notes of a certain difficulty: | ||
| 84 | |||
| 85 | ``` | ||
| 86 | $ echo '["REQ", "subid", {"ids": ["000000000"]}]' | websocat wss://some-relay.com | jq -c '.[2]' | ||
| 87 | {"id":"000000000121637feeb68a06c8fa7abd25774bdedfa9b6ef648386fb3b70c387", ...} | ||
| 88 | ``` | ||
| 89 | |||
| 90 | Delegated Proof of Work | ||
| 91 | ----------------------- | ||
| 92 | |||
| 93 | 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 | ||
| @@ -14,6 +14,7 @@ NIPs stand for **Nostr Implementation Possibilities**. They exist to document wh | |||
| 14 | - [NIP-10: Conventions for clients' use of `e` and `p` tags in text events.](10.md) | 14 | - [NIP-10: Conventions for clients' use of `e` and `p` tags in text events.](10.md) |
| 15 | - [NIP-11: Relay Information Document](11.md) | 15 | - [NIP-11: Relay Information Document](11.md) |
| 16 | - [NIP-12: Generic Tag Queries](12.md) | 16 | - [NIP-12: Generic Tag Queries](12.md) |
| 17 | - [NIP-13: Proof of Work](13.md) | ||
| 17 | 18 | ||
| 18 | ## Event Kinds | 19 | ## Event Kinds |
| 19 | 20 | ||