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, ¬e->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