mirror of
https://github.com/nostr-protocol/nips.git
synced 2024-09-19 22:35:50 -04:00
6fb9e54f7b
the example event id had 21 leading zeroes, not 20 I provided new C code that has been tested to work (I couldn't get the original example code to work) and I provided some JavaScript code to test event ids as well. I did not re-compute the event id for the example event; I simply changed the nonce to be 21. Since it is an example, it may not matter that the event id is not correct.
124 lines
4.3 KiB
Markdown
124 lines
4.3 KiB
Markdown
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 that 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.
|
|
|
|
`002f...` is `0000 0000 0010 1111...` in binary, which has 10 leading zeroes. Do not forget to count leading zeroes for hex digits <= `7`.
|
|
|
|
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",
|
|
"21"
|
|
]
|
|
],
|
|
"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 event id:
|
|
|
|
```c
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
int countLeadingZeroes(const char *hex) {
|
|
int count = 0;
|
|
|
|
for (int i = 0; i < strlen(hex); i++) {
|
|
int nibble = (int)strtol((char[]){hex[i], '\0'}, NULL, 16);
|
|
if (nibble == 0) {
|
|
count += 4;
|
|
} else {
|
|
count += __builtin_clz(nibble) - 28;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
if (argc != 2) {
|
|
fprintf(stderr, "Usage: %s <hex_string>\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
const char *hex_string = argv[1];
|
|
int result = countLeadingZeroes(hex_string);
|
|
printf("Leading zeroes in hex string %s: %d\n", hex_string, result);
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
Here is some JavaScript code for doing the same thing:
|
|
|
|
```javascript
|
|
// hex should be a hexadecimal string (with no 0x prefix)
|
|
function countLeadingZeroes(hex) {
|
|
let count = 0;
|
|
|
|
for (let i = 0; i < hex.length; i++) {
|
|
const nibble = parseInt(hex[i], 16);
|
|
if (nibble === 0) {
|
|
count += 4;
|
|
} else {
|
|
count += Math.clz32(nibble) - 28;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
```
|
|
|
|
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 mobile phones.
|