nostrdb

an unfairly fast embedded nostr database backed by lmdb
git clone git://jb55.com/nostrdb
Log | Files | Refs | README | LICENSE

commit 4e75ac71c34124e04b9e726e78418129cb159f4b
parent 576617ececc98cdad6fa0e621942befead49c218
Author: William Casarin <jb55@jb55.com>
Date:   Tue, 25 Jul 2023 09:15:24 -0700

support note id calculation

Diffstat:
Mnostrdb.c | 161+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mnostrdb.h | 4++++
Mtest.c | 6++++++
3 files changed, 171 insertions(+), 0 deletions(-)

diff --git a/nostrdb.c b/nostrdb.c @@ -3,6 +3,7 @@ #include "jsmn.h" #include "hex.h" #include "cursor.h" +#include "sha256.h" #include <stdlib.h> #include <limits.h> @@ -118,6 +119,166 @@ static int cursor_push_unescaped_char(struct cursor *cur, char c1, char c2) } } +static int cursor_push_escaped_char(struct cursor *cur, char c) +{ + switch (c) { + case '"': return cursor_push_str(cur, "\\\""); + case '\\': return cursor_push_str(cur, "\\\\"); + case '\b': return cursor_push_str(cur, "\\b"); + case '\f': return cursor_push_str(cur, "\\f"); + case '\n': return cursor_push_str(cur, "\\n"); + case '\r': return cursor_push_str(cur, "\\r"); + case '\t': return cursor_push_str(cur, "\\t"); + // TODO: \u hex hex hex hex + } + return cursor_push_byte(cur, c); +} + +static int cursor_push_hex_str(struct cursor *cur, unsigned char *buf, int len) +{ + int i; + + if (len % 2 != 0) + return 0; + + if (!cursor_push_byte(cur, '"')) + return 0; + + for (i = 0; i < len; i++) { + unsigned int c = ((const unsigned char *)buf)[i]; + if (!cursor_push_byte(cur, hexchar(c >> 4))) + return 0; + if (!cursor_push_byte(cur, hexchar(c & 0xF))) + return 0; + } + + if (!cursor_push_byte(cur, '"')) + return 0; + + return 1; +} + +static int cursor_push_jsonstr(struct cursor *cur, const char *str) +{ + int i; + int len; + + len = strlen(str); + + if (!cursor_push_byte(cur, '"')) + return 0; + + for (i = 0; i < len; i++) { + if (!cursor_push_escaped_char(cur, str[i])) + return 0; + } + + if (!cursor_push_byte(cur, '"')) + return 0; + + return 1; +} + + +static inline int cursor_push_json_tag_str(struct cursor *cur, struct ndb_str str) +{ + if (str.flag == NDB_PACKED_ID) + return cursor_push_hex_str(cur, str.id, 32); + + return cursor_push_jsonstr(cur, str.str); +} + +static int cursor_push_json_tag(struct cursor *cur, struct ndb_note *note, + struct ndb_tag *tag) +{ + int i; + + if (!cursor_push_byte(cur, '[')) + return 0; + + for (i = 0; i < tag->count; i++) { + if (!cursor_push_json_tag_str(cur, ndb_note_str(note, &tag->strs[i]))) + return 0; + if (i != tag->count-1 && !cursor_push_byte(cur, ',')) + return 0; + } + + return cursor_push_byte(cur, ']'); +} + +static int cursor_push_json_tags(struct cursor *cur, struct ndb_note *note) +{ + int i; + struct ndb_iterator iter, *it = &iter; + ndb_tags_iterate_start(note, it); + + if (!cursor_push_byte(cur, '[')) + return 0; + + i = 0; + while (ndb_tags_iterate_next(it)) { + if (!cursor_push_json_tag(cur, note, it->tag)) + return 0; + if (i != note->tags.count-1 && !cursor_push_str(cur, ",")) + return 0; + i++; + } + + if (!cursor_push_byte(cur, ']')) + return 0; + + return 1; +} + +static int ndb_event_commitment(struct ndb_note *ev, unsigned char *buf, int buflen) +{ + char timebuf[16] = {0}; + char kindbuf[16] = {0}; + char pubkey[65]; + struct cursor cur; + int ok; + + if (!hex_encode(ev->pubkey, sizeof(ev->pubkey), pubkey, 32)) + return 0; + + make_cursor(buf, buf + buflen, &cur); + + snprintf(timebuf, sizeof(timebuf), "%d", ev->created_at); + snprintf(kindbuf, sizeof(kindbuf), "%d", ev->kind); + + ok = + cursor_push_str(&cur, "[0,\"") && + cursor_push_str(&cur, pubkey) && + cursor_push_str(&cur, "\",") && + cursor_push_str(&cur, timebuf) && + cursor_push_str(&cur, ",") && + cursor_push_str(&cur, kindbuf) && + cursor_push_str(&cur, ",") && + cursor_push_json_tags(&cur, ev) && + cursor_push_str(&cur, ",") && + cursor_push_jsonstr(&cur, ndb_note_str(ev, &ev->content).str) && + cursor_push_str(&cur, "]"); + + if (!ok) + return 0; + + return cur.p - cur.start; +} + +int ndb_calculate_note_id(struct ndb_note *note, unsigned char *buf, int buflen) { + int len; + + if (!(len = ndb_event_commitment(note, buf, buflen))) + return 0; + + //fprintf(stderr, "%.*s\n", len, buf); + + sha256((struct sha256*)note->id, buf, len); + + return 1; +} + + int ndb_builder_finalize(struct ndb_builder *builder, struct ndb_note **note) { int strings_len = builder->strings.p - builder->strings.start; diff --git a/nostrdb.h b/nostrdb.h @@ -77,6 +77,10 @@ struct ndb_iterator { int index; }; +// HELPERS +int ndb_calculate_note_id(struct ndb_note *note, unsigned char *buf, int buflen); +// BYE HELPERS + // HI BUILDER int ndb_note_from_json(const char *json, int len, struct ndb_note **, unsigned char *buf, int buflen); int ndb_builder_init(struct ndb_builder *builder, unsigned char *buf, int bufsize); diff --git a/test.c b/test.c @@ -116,6 +116,7 @@ static void print_tag(struct ndb_note *note, struct ndb_tag *tag) { static void test_parse_contact_list() { int size, written = 0; + unsigned char id[32]; static const int alloc_size = 2 << 18; unsigned char *json = malloc(alloc_size); unsigned char *buf = malloc(alloc_size); @@ -128,6 +129,11 @@ static void test_parse_contact_list() assert(size > 0); assert(size == 34322); + memcpy(id, note->id, 32); + memset(note->id, 0, 32); + assert(ndb_calculate_note_id(note, json, alloc_size)); + assert(!memcmp(note->id, id, 32)); + const char* expected_content = "{\"wss://nos.lol\":{\"write\":true,\"read\":true}," "\"wss://relay.damus.io\":{\"write\":true,\"read\":true},"