damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

nostrdb.h (16038B)


      1 #ifndef NOSTRDB_H
      2 #define NOSTRDB_H
      3 
      4 #include <inttypes.h>
      5 #include "cursor.h"
      6 
      7 #define NDB_PACKED_STR     0x1
      8 #define NDB_PACKED_ID      0x2
      9 
     10 #define NDB_FLAG_NOMIGRATE (1 << 0)
     11 #define NDB_FLAG_SKIP_NOTE_VERIFY (1 << 1)
     12 
     13 //#define DEBUG 1
     14 
     15 #ifdef DEBUG
     16 #define ndb_debug(...) printf(__VA_ARGS__)
     17 #else
     18 #define ndb_debug(...) (void)0
     19 #endif
     20 
     21 struct ndb_json_parser;
     22 struct ndb;
     23 
     24 // sorry, swift needs help with forward declared pointers like this
     25 struct ndb_t {
     26 	struct ndb *ndb;
     27 };
     28 
     29 struct ndb_search_key
     30 {
     31 	char search[24];
     32 	unsigned char id[32];
     33 	uint64_t timestamp;
     34 };
     35 
     36 enum ndb_dbs {
     37 	NDB_DB_NOTE,
     38 	NDB_DB_META,
     39 	NDB_DB_PROFILE,
     40 	NDB_DB_NOTE_ID,
     41 	NDB_DB_PROFILE_PK, // profile pk index
     42 	NDB_DB_NDB_META,
     43 	NDB_DB_PROFILE_SEARCH,
     44 	NDB_DB_PROFILE_LAST_FETCH,
     45 	NDB_DB_NOTE_KIND, // note kind index
     46 	NDB_DB_NOTE_TEXT, // note fulltext index
     47 	NDB_DBS,
     48 };
     49 
     50 // common kinds. we collect stats on these in ndb_stat. mainly because I don't
     51 // want to deal with including a hashtable to the project.
     52 enum ndb_common_kind {
     53 	NDB_CKIND_PROFILE,
     54 	NDB_CKIND_TEXT,
     55 	NDB_CKIND_CONTACTS,
     56 	NDB_CKIND_DM,
     57 	NDB_CKIND_DELETE,
     58 	NDB_CKIND_REPOST,
     59 	NDB_CKIND_REACTION,
     60 	NDB_CKIND_ZAP,
     61 	NDB_CKIND_ZAP_REQUEST,
     62 	NDB_CKIND_NWC_REQUEST,
     63 	NDB_CKIND_NWC_RESPONSE,
     64 	NDB_CKIND_HTTP_AUTH,
     65 	NDB_CKIND_LIST,
     66 	NDB_CKIND_LONGFORM,
     67 	NDB_CKIND_STATUS,
     68 	NDB_CKIND_COUNT, // should always be last
     69 };
     70 
     71 struct ndb_stat_counts {
     72 	size_t key_size;
     73 	size_t value_size;
     74 	size_t count;
     75 };
     76 
     77 struct ndb_stat {
     78 	struct ndb_stat_counts dbs[NDB_DBS];
     79 	struct ndb_stat_counts common_kinds[NDB_CKIND_COUNT];
     80 	struct ndb_stat_counts other_kinds;
     81 };
     82 
     83 struct ndb_search {
     84 	struct ndb_search_key *key;
     85 	uint64_t profile_key;
     86 	void *cursor; // MDB_cursor *
     87 };
     88 
     89 // required to keep a read 
     90 struct ndb_txn {
     91 	struct ndb_lmdb *lmdb;
     92 	void *mdb_txn;
     93 };
     94 
     95 // From-client event types
     96 enum fce_type {
     97 	NDB_FCE_EVENT = 0x1
     98 };
     99 
    100 // To-client event types
    101 enum tce_type {
    102 	NDB_TCE_EVENT  = 0x1,
    103 	NDB_TCE_OK     = 0x2,
    104 	NDB_TCE_NOTICE = 0x3,
    105 	NDB_TCE_EOSE   = 0x4,
    106 	NDB_TCE_AUTH   = 0x5,
    107 };
    108 
    109 enum ndb_ingest_filter_action {
    110 	NDB_INGEST_REJECT,
    111 	NDB_INGEST_ACCEPT,
    112 	NDB_INGEST_SKIP_VALIDATION
    113 };
    114 
    115 // function pointer for controlling what to do after we parse an id
    116 typedef enum ndb_idres (*ndb_id_fn)(void *, const char *);
    117 
    118 // id callback + closure data
    119 struct ndb_id_cb {
    120 	ndb_id_fn fn;
    121 	void *data;
    122 };
    123 
    124 struct ndb_str {
    125 	unsigned char flag;
    126 	union {
    127 		const char *str;
    128 		unsigned char *id;
    129 	};
    130 };
    131 
    132 struct ndb_event {
    133 	struct ndb_note *note;
    134 };
    135 
    136 struct ndb_command_result {
    137 	int ok;
    138 	const char *msg;
    139 	int msglen;
    140 };
    141 
    142 
    143 // From-client event
    144 struct ndb_fce {
    145 	enum fce_type evtype;
    146 	union {
    147 		struct ndb_event event;
    148 	};
    149 };
    150 
    151 // To-client event
    152 struct ndb_tce {
    153 	enum tce_type evtype;
    154 	const char *subid;
    155 	int subid_len;
    156 
    157 	union {
    158 		struct ndb_event event;
    159 		struct ndb_command_result command_result;
    160 	};
    161 };
    162 
    163 struct ndb_keypair {
    164 	unsigned char pubkey[32];
    165 	unsigned char secret[32];
    166 	
    167 	// this corresponds to secp256k1's keypair type. it's guaranteed to
    168 	// be 96 bytes according to their docs. I don't want to depend on
    169 	// the secp256k1 header here so we just use raw bytes.
    170 	unsigned char pair[96];
    171 };
    172 
    173 #define MAX_TEXT_SEARCH_RESULTS 128
    174 #define MAX_TEXT_SEARCH_WORDS 8
    175 
    176 // unpacked form of the actual lmdb fulltext search key
    177 // see `ndb_make_text_search_key` for how the packed version is constructed
    178 struct ndb_text_search_key
    179 {
    180 	int str_len;
    181 	const char *str;
    182 	uint64_t timestamp;
    183 	uint64_t note_id;
    184 	int word_index;
    185 };
    186 
    187 struct ndb_text_search_result {
    188 	struct ndb_text_search_key key;
    189 	int prefix_chars;
    190 };
    191 
    192 struct ndb_text_search_results {
    193 	struct ndb_text_search_result results[MAX_TEXT_SEARCH_RESULTS];
    194 	int num_results;
    195 };
    196 
    197 // these must be byte-aligned, they are directly accessing the serialized data
    198 // representation
    199 #pragma pack(push, 1)
    200 
    201 
    202 union ndb_packed_str {
    203 	struct {
    204 		char str[3];
    205 		// we assume little endian everywhere. sorry not sorry.
    206 		unsigned char flag; // NDB_PACKED_STR, etc
    207 	} packed;
    208 
    209 	uint32_t offset;
    210 	unsigned char bytes[4];
    211 };
    212 
    213 struct ndb_tag {
    214 	uint16_t count;
    215 	union ndb_packed_str strs[0];
    216 };
    217 
    218 struct ndb_tags {
    219 	uint16_t padding;
    220 	uint16_t count;
    221 	struct ndb_tag tag[0];
    222 };
    223 
    224 // v1
    225 struct ndb_note {
    226 	unsigned char version;    // v=1
    227 	unsigned char padding[3]; // keep things aligned
    228 	unsigned char id[32];
    229 	unsigned char pubkey[32];
    230 	unsigned char sig[64];
    231 
    232 	uint64_t created_at;
    233 	uint32_t kind;
    234 	uint32_t content_length;
    235 	union ndb_packed_str content;
    236 	uint32_t strings;
    237 	// nothing can come after tags since it contains variadic data
    238 	struct ndb_tags tags;
    239 };
    240 
    241 #pragma pack(pop)
    242 
    243 typedef enum ndb_ingest_filter_action (*ndb_ingest_filter_fn)(void *, struct ndb_note *);
    244 
    245 struct ndb_builder {
    246 	struct cursor mem;
    247 	struct cursor note_cur;
    248 	struct cursor strings;
    249 	struct cursor str_indices;
    250 	struct ndb_note *note;
    251 	struct ndb_tag *current_tag;
    252 };
    253 
    254 struct ndb_iterator {
    255 	struct ndb_note *note;
    256 	struct ndb_tag *tag;
    257 
    258 	// current outer index
    259 	int index;
    260 };
    261 
    262 enum ndb_filter_fieldtype {
    263 	NDB_FILTER_IDS     = 1,
    264 	NDB_FILTER_AUTHORS = 2,
    265 	NDB_FILTER_KINDS   = 3,
    266 	NDB_FILTER_GENERIC = 4,
    267 	NDB_FILTER_SINCE   = 5,
    268 	NDB_FILTER_UNTIL   = 6,
    269 	NDB_FILTER_LIMIT   = 7,
    270 };
    271 #define NDB_NUM_FILTERS 7
    272 
    273 // when matching generic tags, we need to know if we're dealing with
    274 // a pointer to a 32-byte ID or a null terminated string
    275 enum ndb_generic_element_type {
    276 	NDB_ELEMENT_UNKNOWN = 0,
    277 	NDB_ELEMENT_STRING  = 1,
    278 	NDB_ELEMENT_ID      = 2,
    279 };
    280 
    281 enum ndb_search_order {
    282 	NDB_ORDER_DESCENDING,
    283 	NDB_ORDER_ASCENDING,
    284 };
    285 
    286 union ndb_filter_element {
    287 	const char *string;
    288 	const unsigned char *id;
    289 	uint64_t integer;
    290 };
    291 
    292 struct ndb_filter_field {
    293 	enum ndb_filter_fieldtype type;
    294 	enum ndb_generic_element_type elem_type;
    295 	char generic; // for generic queries like #t
    296 };
    297 
    298 struct ndb_filter_elements {
    299 	struct ndb_filter_field field;
    300 	int count;
    301 	union ndb_filter_element elements[0];
    302 };
    303 
    304 struct ndb_filter {
    305 	struct cursor elem_buf;
    306 	struct cursor data_buf;
    307 	int num_elements;
    308 	struct ndb_filter_elements *current;
    309 	struct ndb_filter_elements *elements[NDB_NUM_FILTERS];
    310 };
    311 
    312 struct ndb_config {
    313 	int flags;
    314 	int ingester_threads;
    315 	size_t mapsize;
    316 	void *filter_context;
    317 	ndb_ingest_filter_fn ingest_filter;
    318 };
    319 
    320 struct ndb_text_search_config {
    321 	enum ndb_search_order order;
    322 	int limit;
    323 };
    324 
    325 // CONFIG
    326 void ndb_default_config(struct ndb_config *);
    327 void ndb_config_set_ingest_threads(struct ndb_config *config, int threads);
    328 void ndb_config_set_flags(struct ndb_config *config, int flags);
    329 void ndb_config_set_mapsize(struct ndb_config *config, size_t mapsize);
    330 void ndb_config_set_ingest_filter(struct ndb_config *config, ndb_ingest_filter_fn fn, void *);
    331 
    332 // HELPERS
    333 int ndb_calculate_id(struct ndb_note *note, unsigned char *buf, int buflen);
    334 int ndb_sign_id(struct ndb_keypair *keypair, unsigned char id[32], unsigned char sig[64]);
    335 int ndb_create_keypair(struct ndb_keypair *key);
    336 int ndb_decode_key(const char *secstr, struct ndb_keypair *keypair);
    337 int ndb_note_verify(void *secp_ctx, unsigned char pubkey[32], unsigned char id[32], unsigned char signature[64]);
    338 
    339 // NDB
    340 int ndb_init(struct ndb **ndb, const char *dbdir, struct ndb_config *);
    341 int ndb_db_version(struct ndb *ndb);
    342 int ndb_process_event(struct ndb *, const char *json, int len);
    343 int ndb_process_events(struct ndb *, const char *ldjson, size_t len);
    344 int ndb_process_client_event(struct ndb *, const char *json, int len);
    345 int ndb_process_client_events(struct ndb *, const char *json, size_t len);
    346 int ndb_begin_query(struct ndb *, struct ndb_txn *);
    347 int ndb_search_profile(struct ndb_txn *txn, struct ndb_search *search, const char *query);
    348 int ndb_search_profile_next(struct ndb_search *search);
    349 void ndb_search_profile_end(struct ndb_search *search);
    350 int ndb_end_query(struct ndb_txn *);
    351 int ndb_write_last_profile_fetch(struct ndb *ndb, const unsigned char *pubkey, uint64_t fetched_at);
    352 uint64_t ndb_read_last_profile_fetch(struct ndb_txn *txn, const unsigned char *pubkey);
    353 void *ndb_get_profile_by_pubkey(struct ndb_txn *txn, const unsigned char *pubkey, size_t *len, uint64_t *primkey);
    354 void *ndb_get_profile_by_key(struct ndb_txn *txn, uint64_t key, size_t *len);
    355 uint64_t ndb_get_notekey_by_id(struct ndb_txn *txn, const unsigned char *id);
    356 uint64_t ndb_get_profilekey_by_pubkey(struct ndb_txn *txn, const unsigned char *id);
    357 struct ndb_note *ndb_get_note_by_id(struct ndb_txn *txn, const unsigned char *id, size_t *len, uint64_t *primkey);
    358 struct ndb_note *ndb_get_note_by_key(struct ndb_txn *txn, uint64_t key, size_t *len);
    359 void *ndb_get_note_meta(struct ndb_txn *txn, const unsigned char *id, size_t *len);
    360 void ndb_destroy(struct ndb *);
    361 
    362 // BUILDER
    363 int ndb_parse_json_note(struct ndb_json_parser *, struct ndb_note **);
    364 int ndb_client_event_from_json(const char *json, int len, struct ndb_fce *fce, unsigned char *buf, int bufsize, struct ndb_id_cb *cb);
    365 int ndb_ws_event_from_json(const char *json, int len, struct ndb_tce *tce, unsigned char *buf, int bufsize, struct ndb_id_cb *);
    366 int ndb_note_from_json(const char *json, int len, struct ndb_note **, unsigned char *buf, int buflen);
    367 int ndb_builder_init(struct ndb_builder *builder, unsigned char *buf, int bufsize);
    368 int ndb_builder_finalize(struct ndb_builder *builder, struct ndb_note **note, struct ndb_keypair *privkey);
    369 int ndb_builder_set_content(struct ndb_builder *builder, const char *content, int len);
    370 void ndb_builder_set_created_at(struct ndb_builder *builder, uint64_t created_at);
    371 void ndb_builder_set_sig(struct ndb_builder *builder, unsigned char *sig);
    372 void ndb_builder_set_pubkey(struct ndb_builder *builder, unsigned char *pubkey);
    373 void ndb_builder_set_id(struct ndb_builder *builder, unsigned char *id);
    374 void ndb_builder_set_kind(struct ndb_builder *builder, uint32_t kind);
    375 int ndb_builder_new_tag(struct ndb_builder *builder);
    376 int ndb_builder_push_tag_str(struct ndb_builder *builder, const char *str, int len);
    377 
    378 // FILTERS
    379 int ndb_filter_init(struct ndb_filter *);
    380 int ndb_filter_add_id_element(struct ndb_filter *, const unsigned char *id);
    381 int ndb_filter_add_int_element(struct ndb_filter *, uint64_t integer);
    382 int ndb_filter_add_str_element(struct ndb_filter *, const char *str);
    383 int ndb_filter_start_field(struct ndb_filter *, enum ndb_filter_fieldtype);
    384 int ndb_filter_start_generic_field(struct ndb_filter *, char tag);
    385 int ndb_filter_matches(struct ndb_filter *, struct ndb_note *);
    386 void ndb_filter_reset(struct ndb_filter *);
    387 void ndb_filter_end_field(struct ndb_filter *);
    388 void ndb_filter_free(struct ndb_filter *filter);
    389 
    390 // FULLTEXT SEARCH
    391 int ndb_text_search(struct ndb_txn *txn, const char *query, struct ndb_text_search_results *, struct ndb_text_search_config *);
    392 void ndb_default_text_search_config(struct ndb_text_search_config *);
    393 void ndb_text_search_config_set_order(struct ndb_text_search_config *, enum ndb_search_order);
    394 void ndb_text_search_config_set_limit(struct ndb_text_search_config *, int limit);
    395 
    396 // stats
    397 int ndb_stat(struct ndb *ndb, struct ndb_stat *stat);
    398 void ndb_stat_counts_init(struct ndb_stat_counts *counts);
    399 
    400 static inline struct ndb_str ndb_note_str(struct ndb_note *note,
    401 					  union ndb_packed_str *pstr)
    402 {
    403 	struct ndb_str str;
    404 	str.flag = pstr->packed.flag;
    405 
    406 	if (str.flag == NDB_PACKED_STR) {
    407 		str.str = pstr->packed.str;
    408 		return str;
    409 	}
    410 
    411 	str.str = ((const char *)note) + note->strings + (pstr->offset & 0xFFFFFF);
    412 	return str;
    413 }
    414 
    415 static inline struct ndb_str ndb_tag_str(struct ndb_note *note,
    416 					 struct ndb_tag *tag, int ind)
    417 {
    418 	return ndb_note_str(note, &tag->strs[ind]);
    419 }
    420 
    421 static inline struct ndb_str ndb_iter_tag_str(struct ndb_iterator *iter,
    422 					      int ind)
    423 {
    424 	return ndb_tag_str(iter->note, iter->tag, ind);
    425 }
    426 
    427 static inline unsigned char * ndb_note_id(struct ndb_note *note)
    428 {
    429 	return note->id;
    430 }
    431 
    432 static inline unsigned char * ndb_note_pubkey(struct ndb_note *note)
    433 {
    434 	return note->pubkey;
    435 }
    436 
    437 static inline unsigned char * ndb_note_sig(struct ndb_note *note)
    438 {
    439 	return note->sig;
    440 }
    441 
    442 static inline uint32_t ndb_note_created_at(struct ndb_note *note)
    443 {
    444 	return note->created_at;
    445 }
    446 
    447 static inline uint32_t ndb_note_kind(struct ndb_note *note)
    448 {
    449 	return note->kind;
    450 }
    451 
    452 static inline const char *ndb_note_content(struct ndb_note *note)
    453 {
    454 	return ndb_note_str(note, &note->content).str;
    455 }
    456 
    457 static inline uint32_t ndb_note_content_length(struct ndb_note *note)
    458 {
    459 	return note->content_length;
    460 }
    461 
    462 static inline struct ndb_note * ndb_note_from_bytes(unsigned char *bytes)
    463 {
    464 	struct ndb_note *note = (struct ndb_note *)bytes;
    465 	if (note->version != 1)
    466 		return 0;
    467 	return note;
    468 }
    469 
    470 static inline union ndb_packed_str ndb_offset_str(uint32_t offset)
    471 {
    472 	// ensure accidents like -1 don't corrupt our packed_str
    473 	union ndb_packed_str str;
    474 	// most significant byte is reserved for ndb_packtype
    475 	str.offset = offset & 0xFFFFFF;
    476 	return str;
    477 }
    478 
    479 static inline union ndb_packed_str ndb_char_to_packed_str(char c)
    480 {
    481 	union ndb_packed_str str;
    482 	str.packed.flag = NDB_PACKED_STR;
    483 	str.packed.str[0] = c;
    484 	str.packed.str[1] = '\0';
    485 	return str;
    486 }
    487 
    488 static inline union ndb_packed_str ndb_chars_to_packed_str(char c1, char c2)
    489 {
    490 	union ndb_packed_str str;
    491 	str.packed.flag = NDB_PACKED_STR;
    492 	str.packed.str[0] = c1;
    493 	str.packed.str[1] = c2;
    494 	str.packed.str[2] = '\0';
    495 	return str;
    496 }
    497 
    498 static inline void ndb_tags_iterate_start(struct ndb_note *note,
    499 					  struct ndb_iterator *iter)
    500 {
    501 	iter->note = note;
    502 	iter->tag = NULL;
    503 	iter->index = -1;
    504 }
    505 
    506 static inline int ndb_tags_iterate_next(struct ndb_iterator *iter)
    507 {
    508 	if (iter->tag == NULL || iter->index == -1) {
    509 		iter->tag = iter->note->tags.tag;
    510 		iter->index = 0;
    511 		return iter->note->tags.count != 0;
    512 	}
    513 
    514 	struct ndb_tags *tags = &iter->note->tags;
    515 
    516 	if (++iter->index < tags->count) {
    517 		uint32_t tag_data_size = iter->tag->count * sizeof(iter->tag->strs[0]);
    518 		iter->tag = (struct ndb_tag *)(iter->tag->strs[0].bytes + tag_data_size);
    519 		return 1;
    520 	}
    521 
    522 	return 0;
    523 }
    524 
    525 static inline enum ndb_common_kind
    526 ndb_kind_to_common_kind(int kind)
    527 {
    528 	switch (kind)
    529 	{
    530 		case 0:     return NDB_CKIND_PROFILE;
    531 		case 1:     return NDB_CKIND_TEXT;
    532 		case 3:     return NDB_CKIND_CONTACTS;
    533 		case 4:     return NDB_CKIND_DM;
    534 		case 5:     return NDB_CKIND_DELETE;
    535 		case 6:     return NDB_CKIND_REPOST;
    536 		case 7:     return NDB_CKIND_REACTION;
    537 		case 9735:  return NDB_CKIND_ZAP;
    538 		case 9734:  return NDB_CKIND_ZAP_REQUEST;
    539 		case 23194: return NDB_CKIND_NWC_REQUEST;
    540 		case 23195: return NDB_CKIND_NWC_RESPONSE;
    541 		case 27235: return NDB_CKIND_HTTP_AUTH;
    542 		case 30000: return NDB_CKIND_LIST;
    543 		case 30023: return NDB_CKIND_LONGFORM;
    544 		case 30315: return NDB_CKIND_STATUS;
    545 	}
    546 
    547 	return -1;
    548 }
    549 
    550 static inline const char *
    551 ndb_kind_name(enum ndb_common_kind ck)
    552 {
    553 	switch (ck) {
    554 		case NDB_CKIND_PROFILE:      return "profile";
    555 		case NDB_CKIND_TEXT:         return "text";
    556 		case NDB_CKIND_CONTACTS:     return "contacts";
    557 		case NDB_CKIND_DM:           return "dm";
    558 		case NDB_CKIND_DELETE:       return "delete";
    559 		case NDB_CKIND_REPOST:       return "repost";
    560 		case NDB_CKIND_REACTION:     return "reaction";
    561 		case NDB_CKIND_ZAP:          return "zap";
    562 		case NDB_CKIND_ZAP_REQUEST:  return "zap_request";
    563 		case NDB_CKIND_NWC_REQUEST:  return "nwc_request";
    564 		case NDB_CKIND_NWC_RESPONSE: return "nwc_response";
    565 		case NDB_CKIND_HTTP_AUTH:    return "http_auth";
    566 		case NDB_CKIND_LIST:         return "list";
    567 		case NDB_CKIND_LONGFORM:     return "longform";
    568 		case NDB_CKIND_STATUS:       return "status";
    569 		case NDB_CKIND_COUNT:        return "unknown";
    570 	}
    571 
    572 	return "unknown";
    573 }
    574 
    575 static inline const char *
    576 ndb_db_name(enum ndb_dbs db)
    577 {
    578 	switch (db) {
    579 		case NDB_DB_NOTE:
    580 			return "note";
    581 		case NDB_DB_META:
    582 			return "note_metadata";
    583 		case NDB_DB_PROFILE:
    584 			return "profile";
    585 		case NDB_DB_NOTE_ID:
    586 			return "note_index";
    587 		case NDB_DB_PROFILE_PK:
    588 			return "profile_pubkey_index";
    589 		case NDB_DB_NDB_META:
    590 			return "nostrdb_metadata";
    591 		case NDB_DB_PROFILE_SEARCH:
    592 			return "profile_search";
    593 		case NDB_DB_PROFILE_LAST_FETCH:
    594 			return "profile_last_fetch";
    595 		case NDB_DB_NOTE_KIND:
    596 			return "note_kind_index";
    597 		case NDB_DB_NOTE_TEXT:
    598 			return "note_fulltext";
    599 		case NDB_DBS:
    600 			return "count";
    601 	}
    602 
    603 	return "unknown";
    604 }
    605 
    606 #endif