nostril

A C cli tool for creating nostr events
git clone git://jb55.com/nostril
Log | Files | Refs | README

commit 29be8497fc7ce0db994a8a1e5bc7c2dd34b324cf
parent 75da4517bef7fb7fbe67151574b41e1160e45abb
Author: William Casarin <jb55@jb55.com>
Date:   Fri, 15 Apr 2022 10:30:52 -0700

add proof-of-work mining

Signed-off-by: William Casarin <jb55@jb55.com>

Diffstat:
Mnostril.c | 72+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Aproof.h | 26++++++++++++++++++++++++++
2 files changed, 95 insertions(+), 3 deletions(-)

diff --git a/nostril.c b/nostril.c @@ -16,6 +16,7 @@ #include "aes.h" #include "sha256.h" #include "random.h" +#include "proof.h" #define MAX_TAGS 32 #define MAX_TAG_ELEMS 16 @@ -24,6 +25,7 @@ #define HAS_KIND (1<<2) #define HAS_ENVELOPE (1<<3) #define HAS_ENCRYPT (1<<4) +#define HAS_DIFFICULTY (1<<5) struct key { secp256k1_keypair pair; @@ -34,6 +36,7 @@ struct key { struct args { unsigned int flags; int kind; + int difficulty; unsigned char encrypt_to[32]; const char *sec; @@ -72,6 +75,7 @@ void usage() printf(" --kind <number> set kind\n"); printf(" --created-at <unix timestamp> set a specific created-at time\n"); printf(" --sec <hex seckey> set the secret key for signing, otherwise one will be randomly generated\n"); + printf(" --pow <difficulty> number of leading 0 bits of the id to mine\n"); exit(1); } @@ -375,6 +379,14 @@ static int parse_args(int argc, const char *argv[], struct args *args) args->flags |= HAS_KIND; } else if (!strcmp(arg, "--envelope")) { args->flags |= HAS_ENVELOPE; + } else if (!strcmp(arg, "--pow")) { + arg = *argv++; argc--; + if (!parse_num(arg, &n)) { + fprintf(stderr, "could not parse difficulty as number: '%s'\n", arg); + return 0; + } + args->difficulty = n; + args->flags |= HAS_DIFFICULTY; } else if (!strcmp(arg, "--dm")) { arg = *argv++; argc--; if (!hex_decode(arg, strlen(arg), args->encrypt_to, 32)) { @@ -439,6 +451,53 @@ static int copyx(unsigned char *output, const unsigned char *x32, const unsigned return 1; } +static int ensure_nonce_tag(struct nostr_event *ev, int *index) +{ + struct nostr_tag *tag; + int i; + + for (i = 0; i < ev->num_tags; i++) { + tag = &ev->tags[i]; + if (tag->num_elems == 2 && !strcmp(tag->strs[0], "nonce")) { + *index = i; + return 1; + } + } + + *index = ev->num_tags; + return nostr_add_tag(ev, "nonce", "0"); +} + +static int mine_event(struct nostr_event *ev, int difficulty) +{ + char *strnonce = malloc(33); + struct nostr_tag *tag; + uint64_t nonce; + int index, res; + + if (!ensure_nonce_tag(ev, &index)) + return 0; + + tag = &ev->tags[index]; + assert(tag->num_elems == 2); + assert(!strcmp(tag->strs[0], "nonce")); + tag->strs[1] = strnonce; + + for (nonce = 0;; nonce++) { + snprintf(strnonce, 32, "%" PRIu64, nonce); + + if (!generate_event_id(ev)) + return 0; + + if ((res = count_leading_zero_bits(ev->id)) >= difficulty) { + fprintf(stderr, "mined %d bits\n", res); + return 1; + } + } + + return 0; +} + static int make_encrypted_dm(secp256k1_context *ctx, struct key *key, struct nostr_event *ev, unsigned char nostr_pubkey[32]) { @@ -572,9 +631,16 @@ int main(int argc, const char *argv[]) // set the event's pubkey memcpy(ev.pubkey, key.pubkey, 32); - if (!generate_event_id(&ev)) { - fprintf(stderr, "could not generate event id\n"); - return 5; + if (args.flags & HAS_DIFFICULTY) { + if (!mine_event(&ev, args.difficulty)) { + fprintf(stderr, "error when mining id\n"); + return 22; + } + } else { + if (!generate_event_id(&ev)) { + fprintf(stderr, "could not generate event id\n"); + return 5; + } } if (!sign_event(ctx, &key, &ev)) { diff --git a/proof.h b/proof.h @@ -0,0 +1,26 @@ +static inline 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 */ +static 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; +}