4.0 KiB
NIP-13
Proof of Work
draft
optional
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:
{"content": "It's just me mining my own business", "tags": [["nonce", "1", "21"]]}
When mining, the second entry to the nonce tag is updated, and then the id is recalculated (see NIP-01). 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
{
"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 event id:
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;
}
Here is some JavaScript code for doing the same thing:
// 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;
}
Filter
A new pow
field is introduced for REQ
messages from clients:
{
// other fields on filter object...
"pow": <integer between 0 and 255>
}
pow
field is an integer (between 0 and 255) that represent the minimum difficulty
that an event ID must have.
Relays SHOULD discard from query result all events with a difficulty lower than pow
.
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.