test.c (69365B)
1 2 #include "nostrdb.h" 3 #include "hex.h" 4 #include "io.h" 5 #include "bolt11/bolt11.h" 6 #include "invoice.h" 7 #include "block.h" 8 #include "protected_queue.h" 9 #include "memchr.h" 10 #include "print_util.h" 11 #include "bindings/c/profile_reader.h" 12 #include "bindings/c/profile_verifier.h" 13 #include "bindings/c/meta_reader.h" 14 #include "bindings/c/meta_verifier.h" 15 16 #include <stdio.h> 17 #include <assert.h> 18 #include <unistd.h> 19 20 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) 21 22 int ndb_print_kind_keys(struct ndb_txn *txn); 23 #define TEST_DIR "./testdata/db" 24 static const char *test_dir = TEST_DIR; 25 26 static void delete_test_db() { 27 // Delete ./testdata/db/data.mdb 28 unlink(TEST_DIR "/data.mdb"); 29 unlink(TEST_DIR "/data.lock"); 30 } 31 32 static NdbProfile_table_t lookup_profile(struct ndb_txn *txn, uint64_t pk) 33 { 34 void *root; 35 size_t len; 36 assert((root = ndb_get_profile_by_key(txn, pk, &len))); 37 assert(root); 38 39 NdbProfileRecord_table_t profile_record = NdbProfileRecord_as_root(root); 40 NdbProfile_table_t profile = NdbProfileRecord_profile_get(profile_record); 41 return profile; 42 } 43 44 static void print_search(struct ndb_txn *txn, struct ndb_search *search) 45 { 46 NdbProfile_table_t profile = lookup_profile(txn, search->profile_key); 47 const char *name = NdbProfile_name_get(profile); 48 const char *display_name = NdbProfile_display_name_get(profile); 49 printf("searched_name name:'%s' display_name:'%s' pk:%" PRIu64 " ts:%" PRIu64 " id:", name, display_name, search->profile_key, search->key->timestamp); 50 print_hex(search->key->id, 32); 51 printf("\n"); 52 } 53 54 static void test_filters() 55 { 56 struct ndb_filter filter, *f; 57 struct ndb_filter_elements *current; 58 struct ndb_note *note; 59 unsigned char buffer[4096]; 60 61 const char *test_note = "{\"id\": \"160e76ca67405d7ce9ef7d2dd72f3f36401c8661a73d45498af842d40b01b736\",\"pubkey\": \"67c67870aebc327eb2a2e765e6dbb42f0f120d2c4e4e28dc16b824cf72a5acc1\",\"created_at\": 1700688516,\"kind\": 1337,\"tags\": [[\"t\",\"hashtag\"],[\"t\",\"grownostr\"],[\"p\",\"4d2e7a6a8e08007ace5a03391d21735f45caf1bf3d67b492adc28967ab46525e\"]],\"content\": \"\",\"sig\": \"20c2d070261ed269559ada40ca5ac395c389681ee3b5f7d50de19dd9b328dd70cf27d9d13875e87c968d9b49fa05f66e90f18037be4529b9e582c7e2afac3f06\"}"; 62 63 f = &filter; 64 assert(ndb_note_from_json(test_note, strlen(test_note), ¬e, buffer, sizeof(buffer))); 65 66 assert(ndb_filter_init(f)); 67 assert(ndb_filter_start_field(f, NDB_FILTER_KINDS)); 68 assert(ndb_filter_add_int_element(f, 1337)); 69 assert(ndb_filter_add_int_element(f, 2)); 70 71 current = ndb_filter_current_element(f); 72 assert(current->count == 2); 73 assert(current->field.type == NDB_FILTER_KINDS); 74 75 // can't start if we've already started 76 assert(ndb_filter_start_field(f, NDB_FILTER_KINDS) == 0); 77 assert(ndb_filter_start_field(f, NDB_FILTER_TAGS) == 0); 78 ndb_filter_end_field(f); 79 80 // should be sorted after end 81 assert(current->elements[0] == 2); 82 assert(current->elements[1] == 1337); 83 84 ndb_filter_end(f); 85 86 // try matching the filter 87 assert(ndb_filter_matches(f, note)); 88 89 _ndb_note_set_kind(note, 1); 90 91 // inverse match 92 assert(!ndb_filter_matches(f, note)); 93 94 // should also match 2 95 _ndb_note_set_kind(note, 2); 96 assert(ndb_filter_matches(f, note)); 97 98 // don't free, just reset data pointers 99 ndb_filter_destroy(f); 100 ndb_filter_init(f); 101 102 // now try generic matches 103 assert(ndb_filter_start_tag_field(f, 't')); 104 assert(ndb_filter_add_str_element(f, "grownostr")); 105 ndb_filter_end_field(f); 106 assert(ndb_filter_start_field(f, NDB_FILTER_KINDS)); 107 assert(ndb_filter_add_int_element(f, 3)); 108 ndb_filter_end_field(f); 109 ndb_filter_end(f); 110 111 // shouldn't match the kind filter 112 assert(!ndb_filter_matches(f, note)); 113 114 _ndb_note_set_kind(note, 3); 115 116 // now it should 117 assert(ndb_filter_matches(f, note)); 118 119 ndb_filter_destroy(f); 120 ndb_filter_init(f); 121 assert(ndb_filter_start_field(f, NDB_FILTER_AUTHORS)); 122 assert(ndb_filter_add_id_element(f, ndb_note_pubkey(note))); 123 ndb_filter_end_field(f); 124 ndb_filter_end(f); 125 assert(f->current == -1); 126 assert(ndb_filter_matches(f, note)); 127 128 ndb_filter_destroy(f); 129 } 130 131 static void test_filter_json() 132 { 133 struct ndb_filter filter, *f = &filter; 134 char buf[1024]; 135 136 unsigned char pk[32] = { 0x32, 0xe1, 0x82, 0x76, 0x35, 0x45, 0x0e, 0xbb, 0x3c, 0x5a, 0x7d, 0x12, 0xc1, 0xf8, 0xe7, 0xb2, 0xb5, 0x14, 0x43, 0x9a, 0xc1, 0x0a, 0x67, 0xee, 0xf3, 0xd9, 0xfd, 0x9c, 0x5c, 0x68, 0xe2, 0x45 }; 137 138 unsigned char pk2[32] = { 139 0xd1, 0x2c, 0x17, 0xbd, 0xe3, 0x09, 0x4a, 0xd3, 0x2f, 0x4a, 0xb8, 0x62, 0xa6, 0xcc, 0x6f, 0x5c, 0x28, 0x9c, 0xfe, 0x7d, 0x58, 0x02, 0x27, 0x0b, 0xdf, 0x34, 0x90, 0x4d, 0xf5, 0x85, 0xf3, 0x49 140 }; 141 142 ndb_filter_init(f); 143 assert(ndb_filter_start_field(f, NDB_FILTER_UNTIL)); 144 assert(ndb_filter_add_int_element(f, 42)); 145 ndb_filter_end_field(f); 146 ndb_filter_end(f); 147 assert(ndb_filter_json(f, buf, sizeof(buf))); 148 assert(!strcmp("{\"until\":42}", buf)); 149 ndb_filter_destroy(f); 150 151 ndb_filter_init(f); 152 assert(ndb_filter_start_field(f, NDB_FILTER_UNTIL)); 153 assert(ndb_filter_add_int_element(f, 42)); 154 ndb_filter_end_field(f); 155 assert(ndb_filter_start_field(f, NDB_FILTER_KINDS)); 156 assert(ndb_filter_add_int_element(f, 1)); 157 assert(ndb_filter_add_int_element(f, 2)); 158 ndb_filter_end_field(f); 159 ndb_filter_end(f); 160 assert(ndb_filter_json(f, buf, sizeof(buf))); 161 assert(!strcmp("{\"until\":42,\"kinds\":[1,2]}", buf)); 162 ndb_filter_destroy(f); 163 164 ndb_filter_init(f); 165 assert(ndb_filter_start_field(f, NDB_FILTER_IDS)); 166 assert(ndb_filter_add_id_element(f, pk)); 167 assert(ndb_filter_add_id_element(f, pk2)); 168 ndb_filter_end_field(f); 169 ndb_filter_end(f); 170 assert(ndb_filter_json(f, buf, sizeof(buf))); 171 assert(!strcmp("{\"ids\":[\"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245\",\"d12c17bde3094ad32f4ab862a6cc6f5c289cfe7d5802270bdf34904df585f349\"]}", buf)); 172 ndb_filter_destroy(f); 173 } 174 175 static void test_invoice_encoding(const char *bolt11_str) 176 { 177 unsigned char buf[4096]; 178 char *fail = NULL; 179 struct cursor cur; 180 struct ndb_invoice invoice; 181 struct bolt11 *bolt11; 182 183 bolt11 = bolt11_decode_minimal(NULL, bolt11_str, &fail); 184 make_cursor(buf, buf + sizeof(buf), &cur); 185 186 assert(fail == NULL); 187 assert(ndb_encode_invoice(&cur, bolt11)); 188 cur.p = cur.start; 189 assert(ndb_decode_invoice(&cur, &invoice)); 190 191 assert(bolt11->msat->millisatoshis == invoice.amount); 192 assert(bolt11->timestamp == invoice.timestamp); 193 assert(bolt11->expiry == invoice.expiry); 194 195 if (bolt11->description != NULL && invoice.description != NULL) 196 assert(!strcmp(bolt11->description, invoice.description)); 197 else if (bolt11->description_hash != NULL && invoice.description_hash != NULL) 198 assert(!memcmp(bolt11->description_hash->u.u8, invoice.description_hash, 32)); 199 else 200 assert(0); 201 202 tal_free(bolt11); 203 } 204 205 static void test_encode_decode_invoice() 206 { 207 const char *deschash = "lnbc12n1pjctuljsp57l6za0xry37prkrz7vuv4324ljnssm8ukr2vrf6qvvrgclsmpyhspp5xqfuk89duzjlt2yg56ym7p3enrfxxltyfpc364qc8nsu3kznkl8shp5eugmd894yph7wq68u09gke5x2hmn7mg3zrwd06fs57gmcrjm0uxsxqyjw5qcqpjrzjqd7yw3w4kvhx8uvcj7qusfw4uqre3j56zjz9t07nd2u55yuya3awsrqdlcqqdzcqqqqqqqqqqqqqqzqqyg9qxpqysgqwm2tsc448ellvf5xem2c95hfvc07lakph9r8hffh704uxqhs22r9s4ly0jel48zv6f7fy8zjkgmjt5h2l4jc9gyj4av42s40qvve2ysqwuega8"; 208 const char *desc = "lnbc12u1pjctuklsp5lg8wdhq2g5xfphkqd5k6gf0femt06wfevu94uuqfprc4ggyqma7spp54lmpmz0mhv3lczepdckr0acf3gdany2654u4k2s8fp5xh0yanjhsdq5w3jhxapdd9h8vmmfvdjsxqyjw5qcqpjrzjqgtsq68q0s9wdadpg32gcfu7hslgkhdpaysj2ha3dtnm8882wa6jyzahpqqqpsgqqyqqqqlgqqqqqpsq9q9qxpqysgqdqzhl8gz46nmalhg27stl25z2u7mqtclv3zz223mjwut90m24fa46xqprjewsqys78j2uljfznz5vtefctu6fw7375ee66e62tj965gpcs85tc"; 209 210 test_invoice_encoding(deschash); 211 test_invoice_encoding(desc); 212 } 213 214 // Test the created_at query plan via a contact-list query 215 static void test_timeline_query() 216 { 217 struct ndb *ndb; 218 struct ndb_filter filter; 219 struct ndb_config config; 220 struct ndb_txn txn; 221 struct ndb_query_result results[10]; 222 int count; 223 ndb_default_config(&config); 224 225 assert(ndb_init(&ndb, test_dir, &config)); 226 227 ndb_filter_init(&filter); 228 ndb_filter_start_field(&filter, NDB_FILTER_AUTHORS); 229 #include "testdata/author-filter.c" 230 ndb_filter_end_field(&filter); 231 ndb_filter_end(&filter); 232 233 ndb_begin_query(ndb, &txn); 234 assert(ndb_query(&txn, &filter, 1, results, 235 sizeof(results)/sizeof(results[0]), &count)); 236 ndb_end_query(&txn); 237 ndb_filter_destroy(&filter); 238 239 assert(count == 10); 240 } 241 242 // Test fetched_at profile records. These are saved when new profiles are 243 // processed, or the last time we've fetched the profile. 244 static void test_fetched_at() 245 { 246 struct ndb *ndb; 247 struct ndb_txn txn; 248 uint64_t fetched_at, t1, t2; 249 struct ndb_config config; 250 ndb_default_config(&config); 251 252 assert(ndb_init(&ndb, test_dir, &config)); 253 254 const unsigned char pubkey[] = { 0x87, 0xfb, 0xc6, 0xd5, 0x98, 0x31, 0xa8, 0x23, 0xa4, 0x5d, 0x10, 0x1f, 255 0x86, 0x94, 0x2c, 0x41, 0xcd, 0xe2, 0x90, 0x23, 0xf4, 0x09, 0x20, 0x24, 256 0xa2, 0x7c, 0x50, 0x10, 0x3c, 0x15, 0x40, 0x01 }; 257 258 const char profile_1[] = "[\"EVENT\",{\"id\": \"a44eb8fb6931d6155b04038bef0624407e46c85c61e5758392cbb615f00184ca\",\"pubkey\": \"87fbc6d59831a823a45d101f86942c41cde29023f4092024a27c50103c154001\",\"created_at\": 1695593354,\"kind\": 0,\"tags\": [],\"content\": \"{\\\"name\\\":\\\"b\\\"}\",\"sig\": \"7540bbde4b4479275e20d95acaa64027359a73989927f878825093cba2f468bd8e195919a77b4c230acecddf92e6b4bee26918b0c0842f84ec7c1fae82453906\"}]"; 259 260 t1 = time(NULL); 261 262 // process the first event, this should set the fetched_at 263 assert(ndb_process_client_event(ndb, profile_1, sizeof(profile_1))); 264 265 // we sleep for a second because we want to make sure the fetched_at is not 266 // updated for the next record, which is an older profile. 267 sleep(1); 268 269 assert(ndb_begin_query(ndb, &txn)); 270 271 // this should be set to t1 272 fetched_at = ndb_read_last_profile_fetch(&txn, pubkey); 273 274 if (fetched_at != t1) { 275 printf("fetched_at != t1? %" PRIu64 " != %" PRIu64 "\n", fetched_at, t1); 276 } 277 assert(fetched_at == t1); 278 279 t2 = time(NULL); 280 assert(t1 != t2); // sanity 281 282 const char profile_2[] = "[\"EVENT\",{\"id\": \"9b2861dda8fc602ec2753f92f1a443c9565de606e0c8f4fd2db4f2506a3b13ca\",\"pubkey\": \"87fbc6d59831a823a45d101f86942c41cde29023f4092024a27c50103c154001\",\"created_at\": 1695593347,\"kind\": 0,\"tags\": [],\"content\": \"{\\\"name\\\":\\\"a\\\"}\",\"sig\": \"f48da228f8967d33c3caf0a78f853b5144631eb86c7777fd25949123a5272a92765a0963d4686dd0efe05b7a9b986bfac8d43070b234153acbae5006d5a90f31\"}]"; 283 284 t2 = time(NULL); 285 286 // process the second event, since this is older it should not change 287 // fetched_at 288 assert(ndb_process_client_event(ndb, profile_2, sizeof(profile_2))); 289 290 // we sleep for a second because we want to make sure the fetched_at is not 291 // updated for the next record, which is an older profile. 292 sleep(1); 293 294 fetched_at = ndb_read_last_profile_fetch(&txn, pubkey); 295 assert(fetched_at == t1); 296 297 ndb_end_query(&txn); 298 ndb_destroy(ndb); 299 } 300 301 static void test_reaction_counter() 302 { 303 static const int alloc_size = 1024 * 1024; 304 char *json = malloc(alloc_size); 305 struct ndb *ndb; 306 size_t len; 307 void *root; 308 int written, reactions, results; 309 NdbEventMeta_table_t meta; 310 struct ndb_txn txn; 311 struct ndb_config config; 312 ndb_default_config(&config); 313 static const int num_reactions = 3; 314 uint64_t note_ids[num_reactions], subid; 315 316 assert(ndb_init(&ndb, test_dir, &config)); 317 318 read_file("testdata/reactions.json", (unsigned char*)json, alloc_size, &written); 319 320 assert((subid = ndb_subscribe(ndb, NULL, 0))); 321 322 assert(ndb_process_client_events(ndb, json, written)); 323 324 for (reactions = 0; reactions < num_reactions;) { 325 results = ndb_wait_for_notes(ndb, subid, note_ids, num_reactions); 326 reactions += results; 327 fprintf(stderr, "got %d notes, total %d\n", results, reactions); 328 assert(reactions > 0); 329 } 330 331 assert(ndb_begin_query(ndb, &txn)); 332 333 const unsigned char id[32] = { 334 0x1a, 0x41, 0x56, 0x30, 0x31, 0x09, 0xbb, 0x4a, 0x66, 0x0a, 0x6a, 0x90, 335 0x04, 0xb0, 0xcd, 0xce, 0x8d, 0x83, 0xc3, 0x99, 0x1d, 0xe7, 0x86, 0x4f, 336 0x18, 0x76, 0xeb, 0x0f, 0x62, 0x2c, 0x68, 0xe8 337 }; 338 339 assert((root = ndb_get_note_meta(&txn, id, &len))); 340 assert(0 == NdbEventMeta_verify_as_root(root, len)); 341 assert((meta = NdbEventMeta_as_root(root))); 342 343 reactions = NdbEventMeta_reactions_get(meta); 344 //printf("counted reactions: %d\n", reactions); 345 assert(reactions == 2); 346 ndb_end_query(&txn); 347 ndb_destroy(ndb); 348 free(json); 349 } 350 351 static void test_profile_search(struct ndb *ndb) 352 { 353 struct ndb_txn txn; 354 struct ndb_search search; 355 int i; 356 const char *name; 357 NdbProfile_table_t profile; 358 359 assert(ndb_begin_query(ndb, &txn)); 360 assert(ndb_search_profile(&txn, &search, "jean")); 361 //print_search(&txn, &search); 362 profile = lookup_profile(&txn, search.profile_key); 363 name = NdbProfile_name_get(profile); 364 assert(!strncmp(name, "jean", 4)); 365 366 assert(ndb_search_profile_next(&search)); 367 //print_search(&txn, &search); 368 profile = lookup_profile(&txn, search.profile_key); 369 name = NdbProfile_name_get(profile); 370 //assert(strncmp(name, "jean", 4)); 371 372 for (i = 0; i < 3; i++) { 373 ndb_search_profile_next(&search); 374 //print_search(&txn, &search); 375 } 376 377 //assert(!strcmp(name, "jb55")); 378 379 ndb_search_profile_end(&search); 380 ndb_end_query(&txn); 381 } 382 383 static void test_profile_updates() 384 { 385 static const int alloc_size = 1024 * 1024; 386 static const int num_notes = 3; 387 char *json; 388 int written, i; 389 size_t len; 390 struct ndb *ndb; 391 struct ndb_config config; 392 struct ndb_txn txn; 393 uint64_t key, subid; 394 uint64_t note_ids[num_notes]; 395 void *record; 396 397 json = malloc(alloc_size); 398 399 ndb_default_config(&config); 400 assert(ndb_init(&ndb, test_dir, &config)); 401 402 subid = ndb_subscribe(ndb, NULL, 0); 403 404 ndb_debug("testing profile updates\n"); 405 read_file("testdata/profile-updates.json", (unsigned char*)json, alloc_size, &written); 406 assert(ndb_process_client_events(ndb, json, written)); 407 408 for (i = 0; i < num_notes;) 409 i += ndb_wait_for_notes(ndb, subid, note_ids, num_notes); 410 411 assert(ndb_begin_query(ndb, &txn)); 412 const unsigned char pk[32] = { 413 0x1c, 0x55, 0x46, 0xe4, 0xf5, 0x93, 0x3b, 0xbe, 0x86, 0x66, 414 0x2a, 0x8e, 0xc3, 0x28, 0x9a, 0x29, 0x87, 0xc0, 0x5d, 0xab, 415 0x25, 0x6c, 0x06, 0x8b, 0x77, 0x42, 0x9f, 0x0f, 0x08, 0xa7, 416 0xa0, 0x90 417 }; 418 record = ndb_get_profile_by_pubkey(&txn, pk, &len, &key); 419 420 assert(record); 421 int res = NdbProfileRecord_verify_as_root(record, len); 422 assert(res == 0); 423 424 NdbProfileRecord_table_t profile_record = NdbProfileRecord_as_root(record); 425 NdbProfile_table_t profile = NdbProfileRecord_profile_get(profile_record); 426 const char *name = NdbProfile_name_get(profile); 427 428 assert(!strcmp(name, "c")); 429 430 ndb_end_query(&txn); 431 ndb_destroy(ndb); 432 free(json); 433 } 434 435 static void test_load_profiles() 436 { 437 static const int alloc_size = 1024 * 1024; 438 char *json = malloc(alloc_size); 439 struct ndb *ndb; 440 int written; 441 struct ndb_config config; 442 ndb_default_config(&config); 443 444 assert(ndb_init(&ndb, test_dir, &config)); 445 446 read_file("testdata/profiles.json", (unsigned char*)json, alloc_size, &written); 447 448 assert(ndb_process_events(ndb, json, written)); 449 450 ndb_destroy(ndb); 451 452 assert(ndb_init(&ndb, test_dir, &config)); 453 unsigned char id[32] = { 454 0x22, 0x05, 0x0b, 0x6d, 0x97, 0xbb, 0x9d, 0xa0, 0x9e, 0x90, 0xed, 0x0c, 455 0x6d, 0xd9, 0x5e, 0xed, 0x1d, 0x42, 0x3e, 0x27, 0xd5, 0xcb, 0xa5, 0x94, 456 0xd2, 0xb4, 0xd1, 0x3a, 0x55, 0x43, 0x09, 0x07 }; 457 const char *expected_content = "{\"website\":\"selenejin.com\",\"lud06\":\"\",\"nip05\":\"selenejin@BitcoinNostr.com\",\"picture\":\"https://nostr.build/i/3549697beda0fe1f4ae621f359c639373d92b7c8d5c62582b656c5843138c9ed.jpg\",\"display_name\":\"Selene Jin\",\"about\":\"INTJ | Founding Designer @Blockstream\",\"name\":\"SeleneJin\"}"; 458 459 struct ndb_txn txn; 460 assert(ndb_begin_query(ndb, &txn)); 461 struct ndb_note *note = ndb_get_note_by_id(&txn, id, NULL, NULL); 462 assert(note != NULL); 463 assert(!strcmp(ndb_note_content(note), expected_content)); 464 ndb_end_query(&txn); 465 466 test_profile_search(ndb); 467 468 ndb_destroy(ndb); 469 470 free(json); 471 } 472 473 static void test_fuzz_events() { 474 struct ndb *ndb; 475 const char *str = "[\"EVENT\"\"\"{\"content\"\"created_at\":0 \"id\"\"5086a8f76fe1da7fb56a25d1bebbafd70fca62e36a72c6263f900ff49b8f8604\"\"kind\":0 \"pubkey\":9c87f94bcbe2a837adc28d46c34eeaab8fc2e1cdf94fe19d4b99ae6a5e6acedc \"sig\"\"27374975879c94658412469cee6db73d538971d21a7b580726a407329a4cafc677fb56b946994cea59c3d9e118fef27e4e61de9d2c46ac0a65df14153 ea93cf5\"\"tags\"[[][\"\"]]}]"; 476 struct ndb_config config; 477 ndb_default_config(&config); 478 479 ndb_init(&ndb, test_dir, &config); 480 ndb_process_event(ndb, str, strlen(str)); 481 ndb_destroy(ndb); 482 } 483 484 static void test_migrate() { 485 static const char *v0_dir = "testdata/db/v0"; 486 struct ndb *ndb; 487 struct ndb_config config; 488 struct ndb_txn txn; 489 490 ndb_default_config(&config); 491 ndb_config_set_flags(&config, NDB_FLAG_NOMIGRATE); 492 493 fprintf(stderr, "testing migrate on v0\n"); 494 assert(ndb_init(&ndb, v0_dir, &config)); 495 assert(ndb_begin_query(ndb, &txn)); 496 assert(ndb_db_version(&txn) == 0); 497 assert(ndb_end_query(&txn)); 498 ndb_destroy(ndb); 499 500 ndb_config_set_flags(&config, 0); 501 502 assert(ndb_init(&ndb, v0_dir, &config)); 503 ndb_destroy(ndb); 504 assert(ndb_init(&ndb, v0_dir, &config)); 505 506 assert(ndb_begin_query(ndb, &txn)); 507 assert(ndb_db_version(&txn) == 3); 508 assert(ndb_end_query(&txn)); 509 510 test_profile_search(ndb); 511 ndb_destroy(ndb); 512 } 513 514 static void test_basic_event() { 515 unsigned char buf[512]; 516 struct ndb_builder builder, *b = &builder; 517 struct ndb_note *note; 518 int ok; 519 520 unsigned char id[32]; 521 memset(id, 1, 32); 522 523 unsigned char pubkey[32]; 524 memset(pubkey, 2, 32); 525 526 unsigned char sig[64]; 527 memset(sig, 3, 64); 528 529 const char *hex_pk = "5d9b81b2d4d5609c5565286fc3b511dc6b9a1b3d7d1174310c624d61d1f82bb9"; 530 531 ok = ndb_builder_init(b, buf, sizeof(buf)); 532 assert(ok); 533 note = builder.note; 534 535 //memset(note->padding, 3, sizeof(note->padding)); 536 537 ok = ndb_builder_set_content(b, hex_pk, strlen(hex_pk)); assert(ok); 538 ndb_builder_set_id(b, id); assert(ok); 539 ndb_builder_set_pubkey(b, pubkey); assert(ok); 540 ndb_builder_set_sig(b, sig); assert(ok); 541 542 ok = ndb_builder_new_tag(b); assert(ok); 543 ok = ndb_builder_push_tag_str(b, "p", 1); assert(ok); 544 ok = ndb_builder_push_tag_str(b, hex_pk, 64); assert(ok); 545 546 ok = ndb_builder_new_tag(b); assert(ok); 547 ok = ndb_builder_push_tag_str(b, "word", 4); assert(ok); 548 ok = ndb_builder_push_tag_str(b, "words", 5); assert(ok); 549 ok = ndb_builder_push_tag_str(b, "w", 1); assert(ok); 550 551 ok = ndb_builder_finalize(b, ¬e, NULL); 552 assert(ok); 553 554 // content should never be packed id 555 // TODO: figure out how to test this now that we don't expose it 556 // assert(note->content.packed.flag != NDB_PACKED_ID); 557 assert(ndb_tags_count(ndb_note_tags(note)) == 2); 558 559 // test iterator 560 struct ndb_iterator iter, *it = &iter; 561 562 ndb_tags_iterate_start(note, it); 563 ok = ndb_tags_iterate_next(it); 564 assert(ok); 565 566 assert(ndb_tag_count(it->tag) == 2); 567 const char *p = ndb_iter_tag_str(it, 0).str; 568 struct ndb_str hpk = ndb_iter_tag_str(it, 1); 569 570 hex_decode(hex_pk, 64, id, 32); 571 572 assert(hpk.flag == NDB_PACKED_ID); 573 assert(memcmp(hpk.id, id, 32) == 0); 574 assert(!strcmp(p, "p")); 575 576 ok = ndb_tags_iterate_next(it); 577 assert(ok); 578 assert(ndb_tag_count(it->tag) == 3); 579 assert(!strcmp(ndb_iter_tag_str(it, 0).str, "word")); 580 assert(!strcmp(ndb_iter_tag_str(it, 1).str, "words")); 581 assert(!strcmp(ndb_iter_tag_str(it, 2).str, "w")); 582 583 ok = ndb_tags_iterate_next(it); 584 assert(!ok); 585 } 586 587 static void test_empty_tags() { 588 struct ndb_builder builder, *b = &builder; 589 struct ndb_iterator iter, *it = &iter; 590 struct ndb_note *note; 591 int ok; 592 unsigned char buf[1024]; 593 594 ok = ndb_builder_init(b, buf, sizeof(buf)); 595 assert(ok); 596 597 ok = ndb_builder_finalize(b, ¬e, NULL); 598 assert(ok); 599 600 assert(ndb_tags_count(ndb_note_tags(note)) == 0); 601 602 ndb_tags_iterate_start(note, it); 603 ok = ndb_tags_iterate_next(it); 604 assert(!ok); 605 } 606 607 static void print_tag(struct ndb_note *note, struct ndb_tag *tag) { 608 struct ndb_str str; 609 int tag_count = ndb_tag_count(tag); 610 for (int i = 0; i < tag_count; i++) { 611 str = ndb_tag_str(note, tag, i); 612 if (str.flag == NDB_PACKED_ID) { 613 printf("<id> "); 614 } else { 615 printf("%s ", str.str); 616 } 617 } 618 printf("\n"); 619 } 620 621 static void test_parse_contact_list() 622 { 623 int size, written = 0; 624 unsigned char id[32]; 625 static const int alloc_size = 2 << 18; 626 unsigned char *json = malloc(alloc_size); 627 unsigned char *buf = malloc(alloc_size); 628 struct ndb_note *note; 629 630 read_file("testdata/contacts.json", json, alloc_size, &written); 631 632 size = ndb_note_from_json((const char*)json, written, ¬e, buf, alloc_size); 633 printf("ndb_note_from_json size %d\n", size); 634 assert(size > 0); 635 assert(size == 34328); 636 637 memcpy(id, ndb_note_id(note), 32); 638 memset(ndb_note_id(note), 0, 32); 639 assert(ndb_calculate_id(note, json, alloc_size, ndb_note_id(note))); 640 assert(!memcmp(ndb_note_id(note), id, 32)); 641 642 const char* expected_content = 643 "{\"wss://nos.lol\":{\"write\":true,\"read\":true}," 644 "\"wss://relay.damus.io\":{\"write\":true,\"read\":true}," 645 "\"ws://monad.jb55.com:8080\":{\"write\":true,\"read\":true}," 646 "\"wss://nostr.wine\":{\"write\":true,\"read\":true}," 647 "\"wss://welcome.nostr.wine\":{\"write\":true,\"read\":true}," 648 "\"wss://eden.nostr.land\":{\"write\":true,\"read\":true}," 649 "\"wss://relay.mostr.pub\":{\"write\":true,\"read\":true}," 650 "\"wss://nostr-pub.wellorder.net\":{\"write\":true,\"read\":true}}"; 651 652 assert(!strcmp(expected_content, ndb_note_content(note))); 653 assert(ndb_note_created_at(note) == 1689904312); 654 assert(ndb_note_kind(note) == 3); 655 assert(ndb_tags_count(ndb_note_tags(note)) == 786); 656 //printf("note content length %d\n", ndb_note_content_length(note)); 657 printf("ndb_content_len %d, expected_len %ld\n", 658 ndb_note_content_length(note), 659 strlen(expected_content)); 660 assert(ndb_note_content_length(note) == strlen(expected_content)); 661 662 struct ndb_iterator iter, *it = &iter; 663 ndb_tags_iterate_start(note, it); 664 665 int tags = 0; 666 int total_elems = 0; 667 668 while (ndb_tags_iterate_next(it)) { 669 total_elems += ndb_tag_count(it->tag); 670 //printf("tag %d: ", tags); 671 if (tags == 0 || tags == 1 || tags == 2) 672 assert(ndb_tag_count(it->tag) == 3); 673 674 if (tags == 6) 675 assert(ndb_tag_count(it->tag) == 2); 676 677 if (tags == 7) 678 assert(!strcmp(ndb_tag_str(note, it->tag, 2).str, "wss://nostr-pub.wellorder.net")); 679 680 if (tags == 786) { 681 static unsigned char h[] = { 0x74, 0xfa, 0xe6, 0x66, 0x4c, 0x9e, 0x79, 0x98, 0x0c, 0x6a, 0xc1, 0x1c, 0x57, 0x75, 0xed, 0x30, 0x93, 0x2b, 0xe9, 0x26, 0xf5, 0xc4, 0x5b, 0xe8, 0xd6, 0x55, 0xe0, 0x0e, 0x35, 0xec, 0xa2, 0x88 }; 682 assert(!memcmp(ndb_tag_str(note, it->tag, 1).id, h, 32)); 683 } 684 685 //print_tag(it->note, it->tag); 686 687 tags += 1; 688 } 689 690 assert(tags == 786); 691 //printf("total_elems %d\n", total_elems); 692 assert(total_elems == 1580); 693 694 write_file("test_contacts_ndb_note", (unsigned char *)note, size); 695 printf("wrote test_contacts_ndb_note (raw ndb_note)\n"); 696 697 free(json); 698 free(buf); 699 } 700 701 static void test_replacement() 702 { 703 static const int alloc_size = 1024 * 1024; 704 char *json = malloc(alloc_size); 705 unsigned char *buf = malloc(alloc_size); 706 struct ndb *ndb; 707 size_t len; 708 int written; 709 struct ndb_config config; 710 ndb_default_config(&config); 711 712 assert(ndb_init(&ndb, test_dir, &config)); 713 714 read_file("testdata/old-new.json", (unsigned char*)json, alloc_size, &written); 715 assert(ndb_process_events(ndb, json, written)); 716 717 ndb_destroy(ndb); 718 assert(ndb_init(&ndb, test_dir, &config)); 719 720 struct ndb_txn txn; 721 assert(ndb_begin_query(ndb, &txn)); 722 723 unsigned char pubkey[32] = { 0x1e, 0x48, 0x9f, 0x6a, 0x4f, 0xc5, 0xc7, 0xac, 0x47, 0x5e, 0xa9, 0x04, 0x17, 0x43, 0xb8, 0x53, 0x11, 0x73, 0x25, 0x92, 0x61, 0xec, 0x71, 0x54, 0x26, 0x41, 0x05, 0x1e, 0x22, 0xa3, 0x82, 0xac }; 724 725 void *root = ndb_get_profile_by_pubkey(&txn, pubkey, &len, NULL); 726 727 assert(root); 728 int res = NdbProfileRecord_verify_as_root(root, len); 729 assert(res == 0); 730 731 NdbProfileRecord_table_t profile_record = NdbProfileRecord_as_root(root); 732 NdbProfile_table_t profile = NdbProfileRecord_profile_get(profile_record); 733 const char *name = NdbProfile_name_get(profile); 734 735 assert(!strcmp(name, "jb55")); 736 737 ndb_end_query(&txn); 738 739 free(json); 740 free(buf); 741 } 742 743 static void test_fetch_last_noteid() 744 { 745 static const int alloc_size = 1024 * 1024; 746 char *json = malloc(alloc_size); 747 unsigned char *buf = malloc(alloc_size); 748 struct ndb *ndb; 749 size_t len; 750 int written; 751 struct ndb_config config; 752 ndb_default_config(&config); 753 754 assert(ndb_init(&ndb, test_dir, &config)); 755 756 read_file("testdata/random.json", (unsigned char*)json, alloc_size, &written); 757 assert(ndb_process_events(ndb, json, written)); 758 759 ndb_destroy(ndb); 760 761 assert(ndb_init(&ndb, test_dir, &config)); 762 763 unsigned char id[32] = { 0xdc, 0x96, 0x4f, 0x4c, 0x89, 0x83, 0x64, 0x13, 0x8e, 0x81, 0x96, 0xf0, 0xc7, 0x33, 0x38, 0xc8, 0xcc, 0x3e, 0xbf, 0xa3, 0xaf, 0xdd, 0xbc, 0x7d, 0xd1, 0x58, 0xb4, 0x84, 0x7c, 0x1e, 0xbf, 0xa0 }; 764 765 struct ndb_txn txn; 766 assert(ndb_begin_query(ndb, &txn)); 767 struct ndb_note *note = ndb_get_note_by_id(&txn, id, &len, NULL); 768 assert(note != NULL); 769 assert(ndb_note_created_at(note) == 1650054135); 770 771 unsigned char pk[32] = { 0x32, 0xe1, 0x82, 0x76, 0x35, 0x45, 0x0e, 0xbb, 0x3c, 0x5a, 0x7d, 0x12, 0xc1, 0xf8, 0xe7, 0xb2, 0xb5, 0x14, 0x43, 0x9a, 0xc1, 0x0a, 0x67, 0xee, 0xf3, 0xd9, 0xfd, 0x9c, 0x5c, 0x68, 0xe2, 0x45 }; 772 773 unsigned char profile_note_id[32] = { 774 0xd1, 0x2c, 0x17, 0xbd, 0xe3, 0x09, 0x4a, 0xd3, 0x2f, 0x4a, 0xb8, 0x62, 0xa6, 0xcc, 0x6f, 0x5c, 0x28, 0x9c, 0xfe, 0x7d, 0x58, 0x02, 0x27, 0x0b, 0xdf, 0x34, 0x90, 0x4d, 0xf5, 0x85, 0xf3, 0x49 775 }; 776 777 void *root = ndb_get_profile_by_pubkey(&txn, pk, &len, NULL); 778 779 assert(root); 780 int res = NdbProfileRecord_verify_as_root(root, len); 781 printf("NdbProfileRecord verify result %d\n", res); 782 assert(res == 0); 783 784 NdbProfileRecord_table_t profile_record = NdbProfileRecord_as_root(root); 785 NdbProfile_table_t profile = NdbProfileRecord_profile_get(profile_record); 786 const char *lnurl = NdbProfileRecord_lnurl_get(profile_record); 787 const char *name = NdbProfile_name_get(profile); 788 uint64_t key = NdbProfileRecord_note_key_get(profile_record); 789 assert(name); 790 assert(lnurl); 791 assert(!strcmp(name, "jb55")); 792 assert(!strcmp(lnurl, "fixme")); 793 794 printf("note_key %" PRIu64 "\n", key); 795 796 struct ndb_note *n = ndb_get_note_by_key(&txn, key, NULL); 797 ndb_end_query(&txn); 798 assert(memcmp(profile_note_id, ndb_note_id(n), 32) == 0); 799 800 //fwrite(profile, len, 1, stdout); 801 802 ndb_destroy(ndb); 803 804 free(json); 805 free(buf); 806 } 807 808 static void test_parse_contact_event() 809 { 810 int written; 811 static const int alloc_size = 2 << 18; 812 char *json = malloc(alloc_size); 813 unsigned char *buf = malloc(alloc_size); 814 struct ndb_tce tce; 815 816 assert(read_file("testdata/contacts-event.json", (unsigned char*)json, 817 alloc_size, &written)); 818 assert(ndb_ws_event_from_json(json, written, &tce, buf, alloc_size, NULL)); 819 820 assert(tce.evtype == NDB_TCE_EVENT); 821 822 free(json); 823 free(buf); 824 } 825 826 static void test_content_len() 827 { 828 int written; 829 static const int alloc_size = 2 << 18; 830 char *json = malloc(alloc_size); 831 unsigned char *buf = malloc(alloc_size); 832 struct ndb_tce tce; 833 834 assert(read_file("testdata/failed_size.json", (unsigned char*)json, 835 alloc_size, &written)); 836 assert(ndb_ws_event_from_json(json, written, &tce, buf, alloc_size, NULL)); 837 838 assert(tce.evtype == NDB_TCE_EVENT); 839 assert(ndb_note_content_length(tce.event.note) == 0); 840 841 free(json); 842 free(buf); 843 } 844 845 static void test_parse_filter_json() 846 { 847 int i; 848 unsigned char buffer[1024]; 849 unsigned char *pid; 850 const char *str; 851 uint64_t val; 852 struct ndb_filter_elements *elems; 853 854 const unsigned char id_bytes[] = { 0x50, 0x04, 0xa0, 0x81, 0xe3, 0x97, 855 0xc6, 0xda, 0x9d, 0xc2, 0xf2, 0xd6, 0xb3, 0x13, 0x40, 0x06, 856 0xa9, 0xd0, 0xe8, 0xc1, 0xb4, 0x66, 0x89, 0xd9, 0xfe, 0x15, 857 0x0b, 0xb2, 0xf2, 0x1a, 0x20, 0x4d }; 858 859 const unsigned char id2_bytes[] = { 0xb1, 0x69, 0xf5, 0x96, 0x96, 0x89, 860 0x17, 0xa1, 0xab, 0xeb, 0x42, 0x34, 0xd3, 0xcf, 0x3a, 0xa9, 861 0xba, 0xee, 0x21, 0x12, 0xe5, 0x89, 0x98, 0xd1, 0x7c, 0x6d, 862 0xb4, 0x16, 0xad, 0x33, 0xfe, 0x40 }; 863 864 #define HEX_ID "5004a081e397c6da9dc2f2d6b3134006a9d0e8c1b46689d9fe150bb2f21a204d" 865 #define HEX_PK "b169f596968917a1abeb4234d3cf3aa9baee2112e58998d17c6db416ad33fe40" 866 867 static const char *json = "{\"ids\": [\"" HEX_ID "\", \"" HEX_PK "\"], \"kinds\": [1,2,3], \"limit\": 10, \"#e\":[\"" HEX_PK "\"], \"#t\": [\"hashtag\"]}"; 868 struct ndb_filter filter, *f = &filter; 869 ndb_filter_init(f); 870 871 assert(ndb_filter_from_json(json, strlen(json), f, buffer, sizeof(buffer))); 872 assert(filter.finalized); 873 874 for (i = 0; i < filter.num_elements; i++) { 875 elems = ndb_filter_get_elements(f, i); 876 877 switch (i) { 878 case 0: 879 assert(elems->field.type == NDB_FILTER_IDS); 880 assert(elems->count == 2); 881 882 pid = ndb_filter_get_id_element(f, elems, 0); 883 assert(!memcmp(pid, id_bytes, 32)); 884 pid = ndb_filter_get_id_element(f, elems, 1); 885 assert(!memcmp(pid, id2_bytes, 32)); 886 break; 887 case 1: 888 assert(elems->field.type == NDB_FILTER_KINDS); 889 assert(elems->count == 3); 890 val = ndb_filter_get_int_element(elems, 0); 891 assert(val == 1); 892 val = ndb_filter_get_int_element(elems, 1); 893 assert(val == 2); 894 val = ndb_filter_get_int_element(elems, 2); 895 assert(val == 3); 896 break; 897 898 case 2: 899 assert(elems->field.type == NDB_FILTER_LIMIT); 900 val = ndb_filter_get_int_element(elems, 0); 901 assert(val == 10); 902 break; 903 904 case 3: 905 assert(elems->field.type == NDB_FILTER_TAGS); 906 assert(elems->field.tag == 'e'); 907 pid = ndb_filter_get_id_element(f, elems, 0); 908 assert(pid != NULL); 909 assert(!memcmp(pid, id2_bytes, 32)); 910 break; 911 912 case 4: 913 assert(elems->field.type == NDB_FILTER_TAGS); 914 assert(elems->field.tag == 't'); 915 str = ndb_filter_get_string_element(f, elems, 0); 916 assert(!strcmp(str, "hashtag")); 917 break; 918 } 919 } 920 921 ndb_filter_destroy(f); 922 } 923 924 static void test_parse_json() { 925 char hex_id[32] = {0}; 926 unsigned char buffer[1024]; 927 struct ndb_note *note; 928 #define HEX_ID "5004a081e397c6da9dc2f2d6b3134006a9d0e8c1b46689d9fe150bb2f21a204d" 929 #define HEX_PK "b169f596968917a1abeb4234d3cf3aa9baee2112e58998d17c6db416ad33fe40" 930 static const char *json = 931 "{\"id\": \"" HEX_ID "\",\"pubkey\": \"" HEX_PK "\",\"created_at\": 1689836342,\"kind\": 1,\"tags\": [[\"p\",\"" HEX_ID "\"], [\"word\", \"words\", \"w\"]],\"content\": \"共通語\",\"sig\": \"e4d528651311d567f461d7be916c37cbf2b4d530e672f29f15f353291ed6df60c665928e67d2f18861c5ca88\"}"; 932 int ok; 933 934 ok = ndb_note_from_json(json, strlen(json), ¬e, buffer, sizeof(buffer)); 935 assert(ok); 936 937 const char *content = ndb_note_content(note); 938 unsigned char *id = ndb_note_id(note); 939 940 hex_decode(HEX_ID, 64, hex_id, sizeof(hex_id)); 941 942 assert(!strcmp(content, "共通語")); 943 assert(!memcmp(id, hex_id, 32)); 944 945 assert(ndb_tags_count(ndb_note_tags(note)) == 2); 946 947 struct ndb_iterator iter, *it = &iter; 948 ndb_tags_iterate_start(note, it); assert(ok); 949 ok = ndb_tags_iterate_next(it); assert(ok); 950 assert(ndb_tag_count(it->tag) == 2); 951 assert(!strcmp(ndb_iter_tag_str(it, 0).str, "p")); 952 assert(!memcmp(ndb_iter_tag_str(it, 1).id, hex_id, 32)); 953 954 ok = ndb_tags_iterate_next(it); assert(ok); 955 assert(ndb_tag_count(it->tag) == 3); 956 assert(!strcmp(ndb_iter_tag_str(it, 0).str, "word")); 957 assert(!strcmp(ndb_iter_tag_str(it, 1).str, "words")); 958 assert(!strcmp(ndb_iter_tag_str(it, 2).str, "w")); 959 } 960 961 static void test_strings_work_before_finalization() { 962 struct ndb_builder builder, *b = &builder; 963 struct ndb_note *note; 964 int ok; 965 unsigned char buf[1024]; 966 967 ok = ndb_builder_init(b, buf, sizeof(buf)); assert(ok); 968 ndb_builder_set_content(b, "hello", 5); 969 970 assert(!strcmp(ndb_note_content(b->note), "hello")); 971 assert(ndb_builder_finalize(b, ¬e, NULL)); 972 973 assert(!strcmp(ndb_note_content(note), "hello")); 974 } 975 976 static void test_parse_content() { 977 } 978 979 static void test_parse_nevent() { 980 unsigned char buf[4096]; 981 const char *content = "nostr:nevent1qqs9qhc0pjvp6jl2w6ppk5cft8ets8fhxy7fcqcjnp7g38whjy0x5aqpzpmhxue69uhkummnw3ezuamfdejsyg86np9a0kajstc8u9h846rmy6320wdepdeydfz8w8cv7kh9sqv02g947d58,#hashtag"; 982 struct ndb_blocks *blocks; 983 struct ndb_block *block = NULL; 984 struct nostr_bech32 *bech32; 985 struct ndb_block_iterator iterator, *iter; 986 iter = &iterator; 987 int ok = 0; 988 989 static unsigned char event_id[] = { 0x50, 0x5f, 0x0f, 0x0c, 0x98, 0x1d, 0x4b, 0xea, 0x76, 0x82, 0x1b, 0x53, 990 0x09, 0x59, 0xf2, 0xb8, 0x1d, 0x37, 0x31, 0x3c, 0x9c, 0x03, 0x12, 0x98, 991 0x7c, 0x88, 0x9d, 0xd7, 0x91, 0x1e, 0x6a, 0x74 }; 992 993 assert(ndb_parse_content(buf, sizeof(buf), content, strlen(content), &blocks)); 994 ndb_blocks_iterate_start(content, blocks, iter); 995 assert(blocks->num_blocks == 3); 996 while ((block = ndb_blocks_iterate_next(iter))) { 997 switch (++ok) { 998 case 1: 999 assert(ndb_get_block_type(block) == BLOCK_MENTION_BECH32); 1000 bech32 = ndb_bech32_block(block); 1001 assert(bech32->type == NOSTR_BECH32_NEVENT); 1002 assert(!memcmp(bech32->nevent.event_id, event_id, 32)); 1003 break; 1004 case 2: 1005 assert(ndb_get_block_type(block) == BLOCK_TEXT); 1006 assert(ndb_str_block_ptr(ndb_block_str(block))[0] == ','); 1007 break; 1008 case 3: 1009 assert(ndb_get_block_type(block) == BLOCK_HASHTAG); 1010 assert(!strncmp("hashtag", ndb_str_block_ptr(ndb_block_str(block)), 7)); 1011 break; 1012 } 1013 } 1014 assert(ok == 3); 1015 } 1016 1017 static void test_bech32_parsing() { 1018 unsigned char buf[4096]; 1019 const char *content = "https://damus.io/notedeck nostr:note1thp5828zk5xujrcuwdppcjnwlz43altca6269demenja3vqm5m2qclq35h"; 1020 1021 struct ndb_blocks *blocks; 1022 struct ndb_block *block; 1023 struct ndb_str_block *str; 1024 struct ndb_block_iterator iterator, *iter; 1025 1026 iter = &iterator; 1027 assert(ndb_parse_content(buf, sizeof(buf), content, strlen(content), &blocks)); 1028 assert(blocks->num_blocks == 3); 1029 1030 ndb_blocks_iterate_start(content, blocks, iter); 1031 int i = 0; 1032 while ((block = ndb_blocks_iterate_next(iter))) { 1033 str = ndb_block_str(block); 1034 switch (++i) { 1035 case 1: 1036 assert(ndb_get_block_type(block) == BLOCK_URL); 1037 assert(!strncmp(str->str, "https://damus.io/notedeck", str->len)); 1038 break; 1039 case 2: 1040 assert(ndb_get_block_type(block) == BLOCK_TEXT); 1041 assert(!strncmp(str->str, " ", str->len)); 1042 break; 1043 case 3: 1044 assert(ndb_get_block_type(block) == BLOCK_MENTION_BECH32); 1045 assert(!strncmp(str->str, "note1thp5828zk5xujrcuwdppcjnwlz43altca6269demenja3vqm5m2qclq35h", str->len)); 1046 break; 1047 } 1048 } 1049 1050 assert(i == 3); 1051 } 1052 1053 static void test_single_url_parsing() { 1054 unsigned char buf[4096]; 1055 const char *content = "https://damus.io/notedeck"; 1056 1057 struct ndb_blocks *blocks; 1058 struct ndb_block *block; 1059 struct ndb_str_block *str; 1060 struct ndb_block_iterator iterator, *iter; 1061 1062 iter = &iterator; 1063 assert(ndb_parse_content(buf, sizeof(buf), content, strlen(content), &blocks)); 1064 assert(blocks->num_blocks == 1); 1065 1066 ndb_blocks_iterate_start(content, blocks, iter); 1067 int i = 0; 1068 while ((block = ndb_blocks_iterate_next(iter))) { 1069 str = ndb_block_str(block); 1070 switch (++i) { 1071 case 1: 1072 assert(ndb_get_block_type(block) == BLOCK_URL); 1073 assert(!strncmp(str->str, "https://damus.io/notedeck", str->len)); 1074 break; 1075 } 1076 } 1077 1078 assert(i == 1); 1079 } 1080 1081 static void test_comma_url_parsing() { 1082 unsigned char buf[4096]; 1083 const char *content = "http://example.com,http://example.com"; 1084 1085 struct ndb_blocks *blocks; 1086 struct ndb_block *block; 1087 struct ndb_str_block *str; 1088 struct ndb_block_iterator iterator, *iter; 1089 1090 iter = &iterator; 1091 assert(ndb_parse_content(buf, sizeof(buf), content, strlen(content), &blocks)); 1092 assert(blocks->num_blocks == 3); 1093 1094 ndb_blocks_iterate_start(content, blocks, iter); 1095 int i = 0; 1096 while ((block = ndb_blocks_iterate_next(iter))) { 1097 str = ndb_block_str(block); 1098 switch (++i) { 1099 case 1: 1100 assert(ndb_get_block_type(block) == BLOCK_URL); 1101 assert(!strncmp(str->str, "http://example.com", str->len)); 1102 break; 1103 case 2: 1104 assert(ndb_get_block_type(block) == BLOCK_TEXT); 1105 assert(!strncmp(str->str, ",", str->len)); 1106 break; 1107 case 3: 1108 assert(ndb_get_block_type(block) == BLOCK_URL); 1109 assert(!strncmp(str->str, "http://example.com", str->len)); 1110 break; 1111 } 1112 } 1113 1114 assert(i == 3); 1115 } 1116 1117 static void test_url_parsing() { 1118 unsigned char buf[4096]; 1119 #define DAMUSIO "https://github.com/damus-io" 1120 #define JB55COM "https://jb55.com/" 1121 #define WIKIORG "http://wikipedia.org" 1122 const char *content = DAMUSIO ", " JB55COM ", " WIKIORG; 1123 struct ndb_blocks *blocks; 1124 struct ndb_block *block; 1125 struct ndb_str_block *str; 1126 struct ndb_block_iterator iterator, *iter; 1127 iter = &iterator; 1128 1129 assert(ndb_parse_content(buf, sizeof(buf), content, strlen(content), &blocks)); 1130 assert(blocks->num_blocks == 5); 1131 1132 ndb_blocks_iterate_start(content, blocks, iter); 1133 int i = 0; 1134 while ((block = ndb_blocks_iterate_next(iter))) { 1135 str = ndb_block_str(block); 1136 switch (++i) { 1137 case 1: 1138 assert(ndb_get_block_type(block) == BLOCK_URL); 1139 assert(!strncmp(str->str, DAMUSIO, str->len)); 1140 break; 1141 case 2: 1142 assert(ndb_get_block_type(block) == BLOCK_TEXT); 1143 assert(!strncmp(str->str, ", ", str->len)); 1144 break; 1145 case 3: 1146 assert(ndb_get_block_type(block) == BLOCK_URL); 1147 assert(!strncmp(str->str, JB55COM, str->len)); 1148 break; 1149 case 4: 1150 assert(ndb_get_block_type(block) == BLOCK_TEXT); 1151 assert(!strncmp(str->str, ", ", str->len)); 1152 break; 1153 case 5: 1154 assert(ndb_get_block_type(block) == BLOCK_URL); 1155 assert(!strncmp(str->str, WIKIORG, str->len)); break; 1156 } 1157 } 1158 1159 assert(i == 5); 1160 } 1161 1162 1163 static void test_bech32_objects() { 1164 struct nostr_bech32 obj; 1165 unsigned char buf[4096]; 1166 const char *nevent = "nevent1qqstjtqmd3lke9m3ftv49pagzxth4q2va4hy2m6kprl0p4y6es4vvnspz3mhxue69uhhyetvv9ujuerpd46hxtnfduqsuamn8ghj7mr0vdskc6r0wd6qegay04"; 1167 1168 unsigned char id[32] = { 1169 0xb9, 0x2c, 0x1b, 0x6c, 0x7f, 0x6c, 0x97, 0x71, 0x4a, 0xd9, 0x52, 0x87, 1170 0xa8, 0x11, 0x97, 0x7a, 0x81, 0x4c, 0xed, 0x6e, 0x45, 0x6f, 0x56, 0x08, 1171 0xfe, 0xf0, 0xd4, 0x9a, 0xcc, 0x2a, 0xc6, 0x4e }; 1172 1173 assert(parse_nostr_bech32(buf, sizeof(buf), nevent, strlen(nevent), &obj)); 1174 assert(obj.type == NOSTR_BECH32_NEVENT); 1175 assert(!memcmp(obj.nevent.event_id, id, 32)); 1176 assert(obj.nevent.relays.num_relays == 2); 1177 const char damus_relay[] = "wss://relay.damus.io"; 1178 const char local_relay[] = "ws://localhost"; 1179 assert(sizeof(damus_relay)-1 == obj.nevent.relays.relays[0].len); 1180 assert(!memcmp(obj.nevent.relays.relays[0].str, damus_relay, sizeof(damus_relay)-1)); 1181 assert(!memcmp(obj.nevent.relays.relays[1].str, local_relay, sizeof(local_relay)-1)); 1182 } 1183 1184 static void test_tce_eose() { 1185 unsigned char buf[1024]; 1186 const char json[] = "[\"EOSE\",\"s\"]"; 1187 struct ndb_tce tce; 1188 int ok; 1189 1190 ok = ndb_ws_event_from_json(json, sizeof(json), &tce, buf, sizeof(buf), NULL); 1191 assert(ok); 1192 1193 assert(tce.evtype == NDB_TCE_EOSE); 1194 assert(tce.subid_len == 1); 1195 assert(!memcmp(tce.subid, "s", 1)); 1196 } 1197 1198 static void test_tce_command_result() { 1199 unsigned char buf[1024]; 1200 const char json[] = "[\"OK\",\"\",true,\"blocked: ok\"]"; 1201 struct ndb_tce tce; 1202 int ok; 1203 1204 ok = ndb_ws_event_from_json(json, sizeof(json), &tce, buf, sizeof(buf), NULL); 1205 assert(ok); 1206 1207 assert(tce.evtype == NDB_TCE_OK); 1208 assert(tce.subid_len == 0); 1209 assert(tce.command_result.ok == 1); 1210 assert(!memcmp(tce.subid, "", 0)); 1211 } 1212 1213 static void test_tce_command_result_empty_msg() { 1214 unsigned char buf[1024]; 1215 const char json[] = "[\"OK\",\"b1d8f68d39c07ce5c5ea10c235100d529b2ed2250140b36a35d940b712dc6eff\",true,\"\"]"; 1216 struct ndb_tce tce; 1217 int ok; 1218 1219 ok = ndb_ws_event_from_json(json, sizeof(json), &tce, buf, sizeof(buf), NULL); 1220 assert(ok); 1221 1222 assert(tce.evtype == NDB_TCE_OK); 1223 assert(tce.subid_len == 64); 1224 assert(tce.command_result.ok == 1); 1225 assert(tce.command_result.msglen == 0); 1226 assert(!memcmp(tce.subid, "b1d8f68d39c07ce5c5ea10c235100d529b2ed2250140b36a35d940b712dc6eff", 0)); 1227 } 1228 1229 // test to-client event 1230 static void test_tce() { 1231 1232 #define HEX_ID "5004a081e397c6da9dc2f2d6b3134006a9d0e8c1b46689d9fe150bb2f21a204d" 1233 #define HEX_PK "b169f596968917a1abeb4234d3cf3aa9baee2112e58998d17c6db416ad33fe40" 1234 #define JSON "{\"id\": \"" HEX_ID "\",\"pubkey\": \"" HEX_PK "\",\"created_at\": 1689836342,\"kind\": 1,\"tags\": [[\"p\",\"" HEX_ID "\"], [\"word\", \"words\", \"w\"]],\"content\": \"共通語\",\"sig\": \"e4d528651311d567f461d7be916c37cbf2b4d530e672f29f15f353291ed6df60c665928e67d2f18861c5ca88\"}" 1235 unsigned char buf[1024]; 1236 const char json[] = "[\"EVENT\",\"subid123\"," JSON "]"; 1237 struct ndb_tce tce; 1238 int ok; 1239 1240 ok = ndb_ws_event_from_json(json, sizeof(json), &tce, buf, sizeof(buf), NULL); 1241 assert(ok); 1242 1243 assert(tce.evtype == NDB_TCE_EVENT); 1244 assert(tce.subid_len == 8); 1245 assert(!memcmp(tce.subid, "subid123", 8)); 1246 1247 #undef HEX_ID 1248 #undef HEX_PK 1249 #undef JSON 1250 } 1251 1252 #define TEST_BUF_SIZE 10 // For simplicity 1253 1254 static void test_queue_init_pop_push() { 1255 struct prot_queue q; 1256 int buffer[TEST_BUF_SIZE]; 1257 int data; 1258 1259 // Initialize 1260 assert(prot_queue_init(&q, buffer, sizeof(buffer), sizeof(int)) == 1); 1261 1262 // Push and Pop 1263 data = 5; 1264 assert(prot_queue_push(&q, &data) == 1); 1265 prot_queue_pop(&q, &data); 1266 assert(data == 5); 1267 1268 // Push to full, and then fail to push 1269 for (int i = 0; i < TEST_BUF_SIZE; i++) { 1270 assert(prot_queue_push(&q, &i) == 1); 1271 } 1272 assert(prot_queue_push(&q, &data) == 0); // Should fail as queue is full 1273 1274 // Pop to empty, and then fail to pop 1275 for (int i = 0; i < TEST_BUF_SIZE; i++) { 1276 assert(prot_queue_try_pop_all(&q, &data, 1) == 1); 1277 assert(data == i); 1278 } 1279 assert(prot_queue_try_pop_all(&q, &data, 1) == 0); // Should fail as queue is empty 1280 } 1281 1282 // This function will be used by threads to test thread safety. 1283 void* thread_func(void* arg) { 1284 struct prot_queue* q = (struct prot_queue*) arg; 1285 int data; 1286 1287 for (int i = 0; i < 100; i++) { 1288 data = i; 1289 prot_queue_push(q, &data); 1290 prot_queue_pop(q, &data); 1291 } 1292 return NULL; 1293 } 1294 1295 static void test_queue_thread_safety() { 1296 struct prot_queue q; 1297 int buffer[TEST_BUF_SIZE]; 1298 pthread_t threads[2]; 1299 1300 assert(prot_queue_init(&q, buffer, sizeof(buffer), sizeof(int)) == 1); 1301 1302 // Create threads 1303 for (int i = 0; i < 2; i++) { 1304 pthread_create(&threads[i], NULL, thread_func, &q); 1305 } 1306 1307 // Join threads 1308 for (int i = 0; i < 2; i++) { 1309 pthread_join(threads[i], NULL); 1310 } 1311 1312 // After all operations, the queue should be empty 1313 int data; 1314 assert(prot_queue_try_pop_all(&q, &data, 1) == 0); 1315 } 1316 1317 static void test_queue_boundary_conditions() { 1318 struct prot_queue q; 1319 int buffer[TEST_BUF_SIZE]; 1320 int data; 1321 1322 // Initialize 1323 assert(prot_queue_init(&q, buffer, sizeof(buffer), sizeof(int)) == 1); 1324 1325 // Push to full 1326 for (int i = 0; i < TEST_BUF_SIZE; i++) { 1327 assert(prot_queue_push(&q, &i) == 1); 1328 } 1329 1330 // Try to push to a full queue 1331 int old_head = q.head; 1332 int old_tail = q.tail; 1333 int old_count = q.count; 1334 assert(prot_queue_push(&q, &data) == 0); 1335 1336 // Assert the queue's state has not changed 1337 assert(old_head == q.head); 1338 assert(old_tail == q.tail); 1339 assert(old_count == q.count); 1340 1341 // Pop to empty 1342 for (int i = 0; i < TEST_BUF_SIZE; i++) { 1343 assert(prot_queue_try_pop_all(&q, &data, 1) == 1); 1344 } 1345 1346 // Try to pop from an empty queue 1347 old_head = q.head; 1348 old_tail = q.tail; 1349 old_count = q.count; 1350 assert(prot_queue_try_pop_all(&q, &data, 1) == 0); 1351 1352 // Assert the queue's state has not changed 1353 assert(old_head == q.head); 1354 assert(old_tail == q.tail); 1355 assert(old_count == q.count); 1356 } 1357 1358 static void test_fast_strchr() 1359 { 1360 // Test 1: Basic test 1361 const char *testStr1 = "Hello, World!"; 1362 assert(fast_strchr(testStr1, 'W', strlen(testStr1)) == testStr1 + 7); 1363 1364 // Test 2: Character not present in the string 1365 assert(fast_strchr(testStr1, 'X', strlen(testStr1)) == NULL); 1366 1367 // Test 3: Multiple occurrences of the character 1368 const char *testStr2 = "Multiple occurrences."; 1369 assert(fast_strchr(testStr2, 'u', strlen(testStr2)) == testStr2 + 1); 1370 1371 // Test 4: Check with an empty string 1372 const char *testStr3 = ""; 1373 assert(fast_strchr(testStr3, 'a', strlen(testStr3)) == NULL); 1374 1375 // Test 5: Check with a one-character string 1376 const char *testStr4 = "a"; 1377 assert(fast_strchr(testStr4, 'a', strlen(testStr4)) == testStr4); 1378 1379 // Test 6: Check the last character in the string 1380 const char *testStr5 = "Last character check"; 1381 assert(fast_strchr(testStr5, 'k', strlen(testStr5)) == testStr5 + 19); 1382 1383 // Test 7: Large string test (>16 bytes) 1384 char *testStr6 = "This is a test for large strings with more than 16 bytes."; 1385 assert(fast_strchr(testStr6, 'm', strlen(testStr6)) == testStr6 + 38); 1386 } 1387 1388 static void test_tag_query() 1389 { 1390 struct ndb *ndb; 1391 struct ndb_txn txn; 1392 struct ndb_filter filters[1], *f = &filters[0]; 1393 struct ndb_config config; 1394 struct ndb_query_result results[4]; 1395 int count, cap; 1396 uint64_t subid, note_ids[1]; 1397 ndb_default_config(&config); 1398 1399 cap = sizeof(results) / sizeof(results[0]); 1400 1401 assert(ndb_init(&ndb, test_dir, &config)); 1402 1403 const char *ev = "[\"EVENT\",\"s\",{\"id\": \"7fd6e4286e595b60448bf69d8ec4a472c5ad14521555813cdfce1740f012aefd\",\"pubkey\": \"b85beab689aed6a10110cc3cdd6e00ac37a2f747c4e60b18a31f4352a5bfb6ed\",\"created_at\": 1704762185,\"kind\": 1,\"tags\": [[\"t\",\"hashtag\"]],\"content\": \"hi\",\"sig\": \"5b05669af5a322730731b13d38667464ea3b45bef1861e26c99ef1815d7e8d557a76e06afa5fffa1dcd207402b92ae7dda6ef411ea515df2bca58d74e6f2772e\"}]"; 1404 1405 f = &filters[0]; 1406 ndb_filter_init(f); 1407 ndb_filter_start_tag_field(f, 't'); 1408 ndb_filter_add_str_element(f, "hashtag"); 1409 ndb_filter_end_field(f); 1410 ndb_filter_end(f); 1411 1412 assert((subid = ndb_subscribe(ndb, f, 1))); 1413 assert(ndb_process_event(ndb, ev, strlen(ev))); 1414 ndb_wait_for_notes(ndb, subid, note_ids, 1); 1415 1416 ndb_begin_query(ndb, &txn); 1417 1418 assert(ndb_query(&txn, f, 1, results, cap, &count)); 1419 assert(count == 1); 1420 assert(!strcmp(ndb_note_content(results[0].note), "hi")); 1421 1422 ndb_end_query(&txn); 1423 ndb_filter_destroy(f); 1424 ndb_destroy(ndb); 1425 } 1426 1427 static void test_query() 1428 { 1429 struct ndb *ndb; 1430 struct ndb_txn txn; 1431 struct ndb_filter filters[2], *f; 1432 struct ndb_config config; 1433 struct ndb_query_result results[4]; 1434 int count, cap; 1435 uint64_t subid, note_ids[4]; 1436 ndb_default_config(&config); 1437 1438 cap = sizeof(results) / sizeof(results[0]); 1439 1440 const unsigned char id[] = { 1441 0x03, 0x36, 0x94, 0x8b, 0xdf, 0xbf, 0x5f, 0x93, 0x98, 0x02, 0xeb, 0xa0, 1442 0x3a, 0xa7, 0x87, 0x35, 0xc8, 0x28, 0x25, 0x21, 0x1e, 0xec, 0xe9, 0x87, 1443 0xa6, 0xd2, 0xe2, 0x0e, 0x3c, 0xff, 0xf9, 0x30 1444 }; 1445 1446 const unsigned char id2[] = { 1447 0x0a, 0x35, 0x0c, 0x58, 0x51, 0xaf, 0x6f, 0x6c, 0xe3, 0x68, 0xba, 0xb4, 1448 0xe2, 0xd4, 0xfe, 0x44, 0x2a, 0x13, 0x18, 0x64, 0x2c, 0x7f, 0xe5, 0x8d, 1449 0xe5, 0x39, 0x21, 0x03, 0x70, 0x0c, 0x10, 0xfc 1450 }; 1451 1452 const char *ev = "[\"EVENT\",\"s\",{\"id\": \"0336948bdfbf5f939802eba03aa78735c82825211eece987a6d2e20e3cfff930\",\"pubkey\": \"aeadd3bf2fd92e509e137c9e8bdf20e99f286b90be7692434e03c015e1d3bbfe\",\"created_at\": 1704401597,\"kind\": 1,\"tags\": [],\"content\": \"hello\",\"sig\": \"232395427153b693e0426b93d89a8319324d8657e67d23953f014a22159d2127b4da20b95644b3e34debd5e20be0401c283e7308ccb63c1c1e0f81cac7502f09\"}]"; 1453 const char *ev2 = "[\"EVENT\",\"s\",{\"id\": \"0a350c5851af6f6ce368bab4e2d4fe442a1318642c7fe58de5392103700c10fc\",\"pubkey\": \"dfa3fc062f7430dab3d947417fd3c6fb38a7e60f82ffe3387e2679d4c6919b1d\",\"created_at\": 1704404822,\"kind\": 1,\"tags\": [],\"content\": \"hello2\",\"sig\": \"48a0bb9560b89ee2c6b88edcf1cbeeff04f5e1b10d26da8564cac851065f30fa6961ee51f450cefe5e8f4895e301e8ffb2be06a2ff44259684fbd4ea1c885696\"}]"; 1454 const char *ev3 = "[\"EVENT\",\"s\",{\"id\": \"20d2b66e1a3ac4a2afe22866ad742091b6267e6e614303de062adb33e12c9931\",\"pubkey\": \"7987bfb2632d561088fc8e3c30a95836f822e4f53633228ec92ae2f5cd6690aa\",\"created_at\": 1704408561,\"kind\": 2,\"tags\": [],\"content\": \"what\",\"sig\": \"cc8533bf177ac87771a5218a04bed24f7a1706f0b2d92700045cdeb38accc5507c6c8de09525e43190df3652012b554d4efe7b82ab268a87ff6f23da44e16a8f\"}]"; 1455 const char *ev4 = "[\"EVENT\",\"s\",{\"id\": \"8a2057c13c1c57b536eab78e6c55428732d33b6b5b234c1f5eab2b5918c37fa1\",\"pubkey\": \"303b5851504da5caa14142e9e2e1b1b60783c48d6f137c205019d46d09244c26\",\"created_at\": 1704408730,\"kind\": 2,\"tags\": [],\"content\": \"hmm\",\"sig\": \"e7cd3029042d41964192411929cade59592840af766da6420077ccc57a61405312db6ca879150db01f53c3b81c477cec5d6bd49f9dc10937267cacf7e5c784b3\"}]"; 1456 1457 assert(ndb_init(&ndb, test_dir, &config)); 1458 1459 f = &filters[0]; 1460 ndb_filter_init(f); 1461 ndb_filter_start_field(f, NDB_FILTER_IDS); 1462 ndb_filter_add_id_element(f, id2); 1463 ndb_filter_add_id_element(f, id); 1464 assert(ndb_filter_current_element(f)->count == 2); 1465 ndb_filter_end_field(f); 1466 ndb_filter_end(f); 1467 1468 assert((subid = ndb_subscribe(ndb, f, 1))); 1469 1470 assert(ndb_process_event(ndb, ev, strlen(ev))); 1471 assert(ndb_process_event(ndb, ev2, strlen(ev2))); 1472 assert(ndb_process_event(ndb, ev3, strlen(ev3))); 1473 assert(ndb_process_event(ndb, ev4, strlen(ev4))); 1474 1475 for (count = 0; count < 2;) 1476 count += ndb_wait_for_notes(ndb, subid, note_ids+count, 4-count); 1477 1478 ndb_begin_query(ndb, &txn); 1479 assert(ndb_query(&txn, f, 1, results, cap, &count)); 1480 assert(count == 2); 1481 assert(0 == memcmp(ndb_note_id(results[0].note), id2, 32)); 1482 1483 ndb_filter_destroy(f); 1484 ndb_filter_init(f); 1485 ndb_filter_start_field(f, NDB_FILTER_KINDS); 1486 ndb_filter_add_int_element(f, 2); 1487 ndb_filter_end_field(f); 1488 ndb_filter_start_field(f, NDB_FILTER_LIMIT); 1489 ndb_filter_add_int_element(f, 2); 1490 ndb_filter_end_field(f); 1491 ndb_filter_end(f); 1492 1493 count = 0; 1494 assert(ndb_query(&txn, f, 1, results, cap, &count)); 1495 ndb_print_kind_keys(&txn); 1496 printf("count %d\n", count); 1497 assert(count == 2); 1498 assert(!strcmp(ndb_note_content(results[0].note), "hmm")); 1499 assert(!strcmp(ndb_note_content(results[1].note), "what")); 1500 1501 ndb_end_query(&txn); 1502 ndb_filter_destroy(f); 1503 ndb_destroy(ndb); 1504 } 1505 1506 static void test_fulltext() 1507 { 1508 struct ndb *ndb; 1509 struct ndb_txn txn; 1510 int written; 1511 static const int alloc_size = 2 << 18; 1512 char *json = malloc(alloc_size); 1513 struct ndb_text_search_results results; 1514 struct ndb_config config; 1515 struct ndb_text_search_config search_config; 1516 ndb_default_config(&config); 1517 ndb_default_text_search_config(&search_config); 1518 1519 assert(ndb_init(&ndb, test_dir, &config)); 1520 1521 read_file("testdata/search.json", (unsigned char*)json, alloc_size, &written); 1522 assert(ndb_process_client_events(ndb, json, written)); 1523 ndb_destroy(ndb); 1524 assert(ndb_init(&ndb, test_dir, &config)); 1525 1526 ndb_begin_query(ndb, &txn); 1527 ndb_text_search(&txn, "Jump Over", &results, &search_config); 1528 ndb_end_query(&txn); 1529 1530 ndb_destroy(ndb); 1531 1532 free(json); 1533 1534 } 1535 1536 static void test_varint(uint64_t value) { 1537 unsigned char buffer[10]; 1538 struct cursor cursor; 1539 uint64_t result; 1540 1541 // Initialize cursor 1542 cursor.start = buffer; 1543 cursor.p = buffer; 1544 cursor.end = buffer + sizeof(buffer); 1545 1546 // Push the value 1547 assert(cursor_push_varint(&cursor, value)); 1548 1549 // Reset cursor for reading 1550 cursor.p = buffer; 1551 1552 // Pull the value 1553 if (!cursor_pull_varint(&cursor, &result)) { 1554 printf("Test failed for value %" PRIu64 " \n", value); 1555 assert(!"Failed to pull value"); 1556 } 1557 1558 // Check if the pushed and pulled values are the same 1559 if (value != result) { 1560 printf("Test failed for value %" PRIu64 "\n", value); 1561 assert(!"test failed"); 1562 } 1563 } 1564 1565 static int test_varints() { 1566 test_varint(0); 1567 test_varint(127); // Edge case for 1-byte varint 1568 test_varint(128); // Edge case for 2-byte varint 1569 test_varint(16383); // Edge case for 2-byte varint 1570 test_varint(16384); // Edge case for 3-byte varint 1571 test_varint(2097151); // Edge case for 3-byte varint 1572 test_varint(2097152); // Edge case for 4-byte varint 1573 test_varint(268435455); // Edge case for 4-byte varint 1574 test_varint(268435456); // Edge case for 5-byte varint 1575 test_varint(34359738367ULL); // Edge case for 5-byte varint 1576 test_varint(34359738368ULL); // Edge case for 6-byte varint 1577 test_varint(4398046511103ULL); // Edge case for 6-byte varint 1578 test_varint(4398046511104ULL); // Edge case for 7-byte varint 1579 test_varint(562949953421311ULL); // Edge case for 7-byte varint 1580 test_varint(562949953421312ULL); // Edge case for 8-byte varint 1581 test_varint(72057594037927935ULL); // Edge case for 8-byte varint 1582 test_varint(72057594037927936ULL); // Edge case for 9-byte varint 1583 test_varint(9223372036854775807ULL); // Maximum 64-bit integer 1584 1585 return 0; 1586 } 1587 1588 static void test_subscriptions() 1589 { 1590 struct ndb *ndb; 1591 struct ndb_config config; 1592 struct ndb_filter filter, *f = &filter; 1593 uint64_t subid; 1594 uint64_t note_id = 0; 1595 struct ndb_txn txn; 1596 struct ndb_note *note; 1597 ndb_default_config(&config); 1598 1599 const char *ev = "[\"EVENT\",\"s\",{\"id\": \"3718b368de4d01a021990e6e00dce4bdf860caed21baffd11b214ac498e7562e\",\"pubkey\": \"57c811c86a871081f52ca80e657004fe0376624a978f150073881b6daf0cbf1d\",\"created_at\": 1704300579,\"kind\": 1337,\"tags\": [],\"content\": \"test\",\"sig\": \"061c36d4004d8342495eb22e8e7c2e2b6e1a1c7b4ae6077fef09f9a5322c561b88bada4f63ff05c9508cb29d03f50f71ef3c93c0201dbec440fc32eda87f273b\"}]"; 1600 1601 assert(ndb_init(&ndb, test_dir, &config)); 1602 1603 assert(ndb_filter_init(f)); 1604 assert(ndb_filter_start_field(f, NDB_FILTER_KINDS)); 1605 assert(ndb_filter_add_int_element(f, 1337)); 1606 ndb_filter_end_field(f); 1607 ndb_filter_end(f); 1608 1609 assert((subid = ndb_subscribe(ndb, f, 1))); 1610 1611 assert(ndb_process_event(ndb, ev, strlen(ev))); 1612 1613 assert(ndb_wait_for_notes(ndb, subid, ¬e_id, 1) == 1); 1614 assert(note_id > 0); 1615 assert(ndb_begin_query(ndb, &txn)); 1616 1617 assert((note = ndb_get_note_by_key(&txn, note_id, NULL))); 1618 assert(!strcmp(ndb_note_content(note), "test")); 1619 1620 // unsubscribe 1621 assert(ndb_num_subscriptions(ndb) == 1); 1622 assert(ndb_unsubscribe(ndb, subid)); 1623 assert(ndb_num_subscriptions(ndb) == 0); 1624 1625 ndb_end_query(&txn); 1626 ndb_filter_destroy(f); 1627 ndb_destroy(ndb); 1628 } 1629 1630 static void test_weird_note_corruption() { 1631 struct ndb *ndb; 1632 struct ndb_config config; 1633 struct ndb_blocks *blocks; 1634 struct ndb_block *block; 1635 struct ndb_str_block *str; 1636 struct ndb_block_iterator iterator, *iter = &iterator; 1637 struct ndb_filter filter, *f = &filter; 1638 uint64_t subid; 1639 uint64_t note_id = 0; 1640 struct ndb_txn txn; 1641 struct ndb_note *note; 1642 ndb_default_config(&config); 1643 1644 const char *ev = "[\"EVENT\",\"a\",{\"content\":\"https://damus.io/notedeck\",\"created_at\":1722537589,\"id\":\"1876ca8cd29afba5805e698cf04ac6611d50e5e5a22e1efb895816a4c5790a1b\",\"kind\":1,\"pubkey\":\"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245\",\"sig\":\"2478aac6e9e66e5a04938d54544928589b55d2a324a7229ef7e709903b5dd12dc9a279abf81ed5753692cd30f62213fd3e0adf8b835543616a60e2d7010f0627\",\"tags\":[[\"e\",\"423fdf3f6e438fded84fe496643008eada5c1db7ba80428521c2c098f1173b83\",\"\",\"root\"],[\"p\",\"406a61077fe67f0eda4be931572c522f937952ddb024c87673e3de6b37e9a98f\"]]}]"; 1645 assert(ndb_init(&ndb, test_dir, &config)); 1646 1647 assert(ndb_filter_init(f)); 1648 assert(ndb_filter_start_field(f, NDB_FILTER_KINDS)); 1649 assert(ndb_filter_add_int_element(f, 1)); 1650 ndb_filter_end_field(f); 1651 ndb_filter_end(f); 1652 1653 assert((subid = ndb_subscribe(ndb, f, 1))); 1654 assert(ndb_process_event(ndb, ev, strlen(ev))); 1655 1656 assert(ndb_wait_for_notes(ndb, subid, ¬e_id, 1) == 1); 1657 assert(note_id > 0); 1658 assert(ndb_begin_query(ndb, &txn)); 1659 1660 assert((note = ndb_get_note_by_key(&txn, note_id, NULL))); 1661 assert(!strcmp(ndb_note_content(note), "https://damus.io/notedeck")); 1662 assert(ndb_note_content_length(note) == 25); 1663 1664 assert(ndb_num_subscriptions(ndb) == 1); 1665 assert(ndb_unsubscribe(ndb, subid)); 1666 assert(ndb_num_subscriptions(ndb) == 0); 1667 1668 blocks = ndb_get_blocks_by_key(ndb, &txn, note_id); 1669 1670 ndb_blocks_iterate_start(ndb_note_content(note), blocks, iter); 1671 //printf("url num blocks: %d\n", blocks->num_blocks); 1672 assert(blocks->num_blocks == 1); 1673 int i = 0; 1674 while ((block = ndb_blocks_iterate_next(iter))) { 1675 str = ndb_block_str(block); 1676 //printf("block (%d): %d:'%.*s'\n", ndb_get_block_type(block), str->len, str->len, str->str); 1677 switch (++i) { 1678 case 1: 1679 assert(ndb_get_block_type(block) == BLOCK_URL); 1680 assert(str->len == 25); 1681 assert(!strncmp(str->str, "https://damus.io/notedeck", str->len)); 1682 break; 1683 } 1684 } 1685 assert(i == 1); 1686 1687 ndb_end_query(&txn); 1688 ndb_filter_destroy(f); 1689 ndb_destroy(ndb); 1690 } 1691 1692 static void test_filter_eq() { 1693 struct ndb_filter filter, *f = &filter; 1694 struct ndb_filter filter2, *f2 = &filter2; 1695 1696 ndb_filter_init(f); 1697 assert(ndb_filter_start_field(f, NDB_FILTER_UNTIL)); 1698 assert(ndb_filter_add_int_element(f, 42)); 1699 ndb_filter_end_field(f); 1700 assert(ndb_filter_start_field(f, NDB_FILTER_KINDS)); 1701 assert(ndb_filter_add_int_element(f, 1)); 1702 assert(ndb_filter_add_int_element(f, 2)); 1703 ndb_filter_end_field(f); 1704 ndb_filter_end(f); 1705 1706 ndb_filter_init(f2); 1707 assert(ndb_filter_start_field(f2, NDB_FILTER_KINDS)); 1708 assert(ndb_filter_add_int_element(f2, 2)); 1709 assert(ndb_filter_add_int_element(f2, 1)); 1710 ndb_filter_end_field(f2); 1711 assert(ndb_filter_start_field(f2, NDB_FILTER_UNTIL)); 1712 assert(ndb_filter_add_int_element(f2, 42)); 1713 ndb_filter_end_field(f2); 1714 ndb_filter_end(f2); 1715 1716 assert(ndb_filter_eq(f, f2)); 1717 1718 ndb_filter_destroy(f); 1719 ndb_filter_destroy(f2); 1720 } 1721 1722 static void test_filter_is_subset() { 1723 struct ndb_filter global, *g = &global; 1724 struct ndb_filter kind, *k = &kind; 1725 struct ndb_filter kind_and_id, *ki = &kind_and_id; 1726 1727 const unsigned char id[] = { 1728 0x03, 0x36, 0x94, 0x8b, 0xdf, 0xbf, 0x5f, 0x93, 0x98, 0x02, 0xeb, 0xa0, 1729 0x3a, 0xa7, 0x87, 0x35, 0xc8, 0x28, 0x25, 0x21, 0x1e, 0xec, 0xe9, 0x87, 1730 0xa6, 0xd2, 0xe2, 0x0e, 0x3c, 0xff, 0xf9, 0x30 1731 }; 1732 1733 ndb_filter_init(g); 1734 ndb_filter_end(g); 1735 1736 ndb_filter_init(k); 1737 assert(ndb_filter_start_field(k, NDB_FILTER_KINDS)); 1738 assert(ndb_filter_add_int_element(k, 1)); 1739 ndb_filter_end_field(k); 1740 ndb_filter_end(k); 1741 1742 ndb_filter_init(ki); 1743 assert(ndb_filter_start_field(ki, NDB_FILTER_KINDS)); 1744 assert(ndb_filter_add_int_element(ki, 1)); 1745 ndb_filter_end_field(ki); 1746 assert(ndb_filter_start_field(ki, NDB_FILTER_IDS)); 1747 assert(ndb_filter_add_id_element(ki, id)); 1748 ndb_filter_end_field(ki); 1749 ndb_filter_end(ki); 1750 1751 assert(ndb_filter_is_subset_of(g, k) == 0); 1752 assert(ndb_filter_is_subset_of(k, g) == 1); 1753 assert(ndb_filter_is_subset_of(ki, k) == 1); 1754 assert(ndb_filter_is_subset_of(k, ki) == 0); 1755 1756 ndb_filter_destroy(k); 1757 ndb_filter_destroy(ki); 1758 } 1759 1760 static void test_filter_search() 1761 { 1762 struct ndb_filter filter, *f = &filter; 1763 1764 assert(ndb_filter_init_with(f, 1)); 1765 1766 assert(ndb_filter_start_field(f, NDB_FILTER_SEARCH)); 1767 assert(ndb_filter_add_str_element(f, "searchterm")); 1768 assert(!ndb_filter_add_str_element(f, "searchterm 2")); 1769 ndb_filter_end_field(f); 1770 1771 assert(ndb_filter_end(f)); 1772 ndb_filter_destroy(f); 1773 } 1774 1775 static void test_filter_parse_search_json() { 1776 const char *json = "{\"search\":\"abc\",\"limit\":1}"; 1777 unsigned char buf[1024]; 1778 int i; 1779 1780 struct ndb_filter filter, *f = &filter; 1781 struct ndb_filter_elements *es; 1782 1783 ndb_filter_init_with(f, 1); 1784 assert(ndb_filter_from_json(json, strlen(json), f, buf, sizeof(buf))); 1785 assert(filter.finalized); 1786 1787 assert(f->num_elements == 2); 1788 for (i = 0; i < f->num_elements; i++) { 1789 es = ndb_filter_get_elements(f, i); 1790 if (i == 0) { 1791 assert(es->field.type == NDB_FILTER_SEARCH); 1792 assert(es->count == 1); 1793 assert(!strcmp(ndb_filter_get_string_element(f, es, 0), "abc")); 1794 } else if (i == 1) { 1795 assert(es->field.type == NDB_FILTER_LIMIT); 1796 assert(es->count == 1); 1797 assert(ndb_filter_get_int_element(es, 0) == 1); 1798 } 1799 } 1800 1801 // test back to json 1802 assert(ndb_filter_json(f, (char *)buf, sizeof(buf))); 1803 printf("search json: '%s'\n", (const char *)buf); 1804 assert(!strcmp((const char*)buf, json)); 1805 1806 ndb_filter_destroy(f); 1807 } 1808 1809 static void test_note_relay_index() 1810 { 1811 const char *relay; 1812 struct ndb *ndb; 1813 struct ndb_txn txn; 1814 struct ndb_config config; 1815 struct ndb_filter filter, *f = &filter; 1816 uint64_t note_key, subid; 1817 struct ndb_ingest_meta meta; 1818 1819 const char *json = "[\"EVENT\",{\"id\": \"0f20295584a62d983a4fa85f7e50b460cd0049f94d8cd250b864bb822a747114\",\"pubkey\": \"55c882cf4a255ac66fc8507e718a1d1283ba46eb7d678d0573184dada1a4f376\",\"created_at\": 1742498339,\"kind\": 1,\"tags\": [],\"content\": \"hi\",\"sig\": \"ae1218280f554ea0b04ae09921031493d60fb7831dfd2dbd7086efeace2719a46842ce80342ebc002da8943df02e98b8b4abb4629c7103ca2114e6c4425f97fe\"}]"; 1820 1821 // Initialize NDB 1822 ndb_default_config(&config); 1823 assert(ndb_init(&ndb, test_dir, &config)); 1824 1825 // 1) Ingest the note from “relay1”. 1826 // Use ndb_ingest_meta_init to record the relay. 1827 1828 assert(ndb_filter_init(f)); 1829 assert(ndb_filter_start_field(f, NDB_FILTER_KINDS)); 1830 assert(ndb_filter_add_int_element(f, 1)); 1831 ndb_filter_end_field(f); 1832 ndb_filter_end(f); 1833 1834 assert((subid = ndb_subscribe(ndb, f, 1))); 1835 1836 ndb_ingest_meta_init(&meta, 1, "wss://relay.damus.io"); 1837 assert(ndb_process_event_with(ndb, json, strlen(json), &meta)); 1838 meta.relay = "wss://relay.mit.edu"; 1839 assert(ndb_process_event_with(ndb, json, strlen(json), &meta)); 1840 meta.relay = "wss://nostr.mom"; 1841 assert(ndb_process_event_with(ndb, json, strlen(json), &meta)); 1842 meta.relay = "ws://monad.jb55.com:8080"; 1843 assert(ndb_process_event_with(ndb, json, strlen(json), &meta)); 1844 1845 assert(ndb_wait_for_notes(ndb, subid, ¬e_key, 1) == 1); 1846 assert(note_key > 0); 1847 1848 sleep(1); 1849 1850 // 4) Check that we have both relays 1851 assert(ndb_begin_query(ndb, &txn)); 1852 assert(ndb_note_seen_on_relay(&txn, note_key, "wss://relay.damus.io")); 1853 assert(ndb_note_seen_on_relay(&txn, note_key, "wss://relay.mit.edu")); 1854 assert(ndb_note_seen_on_relay(&txn, note_key, "wss://nostr.mom")); 1855 assert(ndb_note_seen_on_relay(&txn, note_key, "ws://monad.jb55.com:8080")); 1856 1857 // walk the relays 1858 struct ndb_note_relay_iterator iter; 1859 1860 assert(ndb_note_relay_iterate_start(&txn, &iter, note_key)); 1861 1862 relay = ndb_note_relay_iterate_next(&iter); 1863 assert(relay); 1864 assert(!strcmp(relay, "ws://monad.jb55.com:8080")); 1865 1866 relay = ndb_note_relay_iterate_next(&iter); 1867 assert(relay); 1868 assert(!strcmp(relay, "wss://nostr.mom")); 1869 1870 relay = ndb_note_relay_iterate_next(&iter); 1871 assert(relay); 1872 assert(!strcmp(relay, "wss://relay.damus.io")); 1873 1874 relay = ndb_note_relay_iterate_next(&iter); 1875 assert(relay); 1876 assert(!strcmp(relay, "wss://relay.mit.edu")); 1877 1878 assert(ndb_note_relay_iterate_next(&iter) == NULL); 1879 ndb_note_relay_iterate_close(&iter); 1880 assert(iter.mdb_cur == NULL); 1881 1882 assert(ndb_end_query(&txn)); 1883 1884 // Cleanup 1885 ndb_filter_destroy(f); 1886 ndb_destroy(ndb); 1887 1888 printf("ok test_note_relay_index\n"); 1889 } 1890 1891 static void test_nip50_profile_search() { 1892 struct ndb *ndb; 1893 struct ndb_txn txn; 1894 struct ndb_config config; 1895 struct ndb_filter filter, *f = &filter; 1896 int count; 1897 struct ndb_query_result result; 1898 1899 // Initialize NDB 1900 ndb_default_config(&config); 1901 assert(ndb_init(&ndb, test_dir, &config)); 1902 1903 // 1) Ingest the note from “relay1”. 1904 // Use ndb_ingest_meta_init to record the relay. 1905 1906 unsigned char expected_id[32] = { 1907 0x22, 0x05, 0x0b, 0x6d, 0x97, 0xbb, 0x9d, 0xa0, 0x9e, 0x90, 0xed, 0x0c, 1908 0x6d, 0xd9, 0x5e, 0xed, 0x1d, 0x42, 0x3e, 0x27, 0xd5, 0xcb, 0xa5, 0x94, 1909 0xd2, 0xb4, 0xd1, 0x3a, 0x55, 0x43, 0x09, 0x07 }; 1910 assert(ndb_filter_init(f)); 1911 assert(ndb_filter_start_field(f, NDB_FILTER_SEARCH)); 1912 assert(ndb_filter_add_str_element(f, "Selene")); 1913 ndb_filter_end_field(f); 1914 assert(ndb_filter_start_field(f, NDB_FILTER_KINDS)); 1915 assert(ndb_filter_add_int_element(f, 0)); 1916 ndb_filter_end_field(f); 1917 ndb_filter_end(f); 1918 1919 ndb_begin_query(ndb, &txn); 1920 ndb_query(&txn, f, 1, &result, 1, &count); 1921 ndb_end_query(&txn); 1922 1923 assert(count == 1); 1924 assert(!memcmp(ndb_note_id(result.note), expected_id, 32)); 1925 1926 // Cleanup 1927 ndb_filter_destroy(f); 1928 ndb_destroy(ndb); 1929 1930 printf("ok test_nip50_profile_search\n"); 1931 } 1932 1933 static bool only_threads_filter(void *ctx, struct ndb_note *note) 1934 { 1935 struct ndb_iterator it; 1936 struct ndb_str str; 1937 const char *c; 1938 1939 ndb_tags_iterate_start(note, &it); 1940 1941 while (ndb_tags_iterate_next(&it)) { 1942 if (ndb_tag_count(it.tag) < 4) 1943 continue; 1944 1945 str = ndb_iter_tag_str(&it, 0); 1946 if (str.str[0] != 'e') 1947 continue; 1948 1949 str = ndb_iter_tag_str(&it, 3); 1950 if (str.flag == NDB_PACKED_ID) 1951 continue; 1952 1953 if (str.str[0] != 'r') 1954 continue; 1955 1956 // if it has a reply or root marker, then this is a reply 1957 c = &str.str[1]; 1958 if (*c++ == 'e' && *c++ == 'p' && *c++ == 'l' && *c++ == 'y' && *c++ == '\0') { 1959 return false; 1960 } 1961 1962 c = &str.str[1]; 1963 if (*c++ == 'o' && *c++ == 'o' && *c++ == 't' && *c++ == '\0') { 1964 return false; 1965 } 1966 } 1967 1968 return true; 1969 } 1970 1971 static void test_custom_filter() 1972 { 1973 struct ndb *ndb; 1974 struct ndb_txn txn; 1975 struct ndb_config config; 1976 struct ndb_filter filter, *f = &filter; 1977 struct ndb_filter filter2, *f2 = &filter2; 1978 int count, nres = 2; 1979 uint64_t sub_id, note_keys[2]; 1980 struct ndb_query_result results[2]; 1981 struct ndb_ingest_meta meta; 1982 1983 const char *root = "[\"EVENT\",{\"id\":\"3d3fba391ce6f83cf336b161f3de90bb2610c20dfb9f4de3a6dacb6b11362971\",\"pubkey\":\"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245\",\"created_at\":1744084064,\"kind\":1,\"tags\":[],\"content\":\"dire wolves are back LFG\",\"sig\":\"340a6ee8a859a1d78e50551dae7b24aaba7137647a6ac295acc97faa06ba33310593f5b4081dad188aee81266144f2312afb249939a2e07c14ca167af08e998f\"}]"; 1984 1985 const char *reply_json = "[\"EVENT\",{\"id\":\"3a338522ee1e27056acccee65849de8deba426db1c71cbd61d105280bbb67ed2\",\"pubkey\":\"7cc328a08ddb2afdf9f9be77beff4c83489ff979721827d628a542f32a247c0e\",\"created_at\":1744151551,\"kind\":1,\"tags\":[[\"alt\",\"A short note: pokerdeck ♠️🦍🐧\"],[\"e\",\"086ccb1873fa10d4338713f24b034e17e543d8ad79c15ff39cf59f4d0cb7a2d6\",\"wss://nostr.wine/\",\"root\",\"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245\"],[\"p\",\"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245\",\"wss://nostr.wine\"]],\"content\":\"pokerdeck ♠️🦍🐧\",\"sig\":\"1099e47ba1ba962a8f2617c139e240d0369d81e70241dce7e73340e3230d8a3039c07114ed21f0e765e40f1b71fc2770fa5585994d27d2ece3b14e7b98f988d3\"}]"; 1986 1987 ndb_default_config(&config); 1988 assert(ndb_init(&ndb, test_dir, &config)); 1989 1990 ndb_filter_init(f); 1991 ndb_filter_start_field(f, NDB_FILTER_KINDS); 1992 ndb_filter_add_int_element(f, 1); 1993 ndb_filter_end_field(f); 1994 1995 ndb_filter_start_field(f, NDB_FILTER_CUSTOM); 1996 ndb_filter_add_custom_filter_element(f, only_threads_filter, NULL); 1997 ndb_filter_end_field(f); 1998 ndb_filter_end(f); 1999 2000 assert(ndb_filter_init(f2)); 2001 assert(ndb_filter_start_field(f2, NDB_FILTER_KINDS)); 2002 assert(ndb_filter_add_int_element(f2, 1)); 2003 ndb_filter_end_field(f2); 2004 ndb_filter_end(f2); 2005 2006 sub_id = ndb_subscribe(ndb, f2, 1); 2007 2008 ndb_ingest_meta_init(&meta, 1, "test"); 2009 2010 assert(ndb_process_event_with(ndb, root, strlen(root), &meta)); 2011 assert(ndb_process_event_with(ndb, reply_json, strlen(reply_json), &meta)); 2012 2013 for (nres = 2; nres > 0;) 2014 nres -= ndb_wait_for_notes(ndb, sub_id, note_keys, 2); 2015 2016 ndb_begin_query(ndb, &txn); 2017 ndb_query(&txn, f, 1, results, 2, &count); 2018 ndb_end_query(&txn); 2019 2020 assert(count == 1); 2021 assert(ndb_note_id(results[0].note)[0] == 0x3d); 2022 2023 // Cleanup 2024 ndb_filter_destroy(f); 2025 ndb_filter_destroy(f2); 2026 ndb_destroy(ndb); 2027 2028 printf("ok test_custom_filter\n"); 2029 } 2030 2031 void test_replay_attack() { 2032 struct ndb_filter filter, *f = &filter; 2033 struct ndb *ndb; 2034 struct ndb_txn txn; 2035 struct ndb_config config; 2036 uint64_t note_key, sub_id; 2037 int count; 2038 struct ndb_query_result results[2]; 2039 unsigned char expected[] = { 0x1f, 0x5f, 0x21, 0xb2, 0x2e, 0x4c, 0x87, 0xb1, 0xd7, 0xcf, 0x92, 0x71, 0xa1, 0xc8, 0xae, 0xaf, 0x5b, 0x49, 0x06, 0x1a, 0xf2, 0xc0, 0x3c, 0xab, 0x70, 0x6b, 0xbe, 0x2e, 0xbb, 0x9f, 0xef, 0xd9 }; 2040 2041 // maleated note 2042 const char *bad_note = "[\"EVENT\",\"blah\",{\"id\":\"3d3fba391ce6f83cf336b161f3de90bb2610c20dfb9f4de3a6dacb6b11362971\",\"pubkey\":\"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245\",\"created_at\":1744084064,\"kind\":1,\"tags\":[],\"content\":\"dire wolves are cool\",\"sig\":\"340a6ee8a859a1d78e50551dae7b24aaba7137647a6ac295acc97faa06ba33310593f5b4081dad188aee81266144f2312afb249939a2e07c14ca167af08e998f\"}]"; 2043 2044 const char *ok_note = "[\"EVENT\",\"blah\",{\"id\": \"1f5f21b22e4c87b1d7cf9271a1c8aeaf5b49061af2c03cab706bbe2ebb9fefd9\",\"pubkey\": \"73764df506728297a9c1f359024d2f9c895001f4afda2d0afa844ce7a94778ca\",\"created_at\": 1750785986,\"kind\": 1,\"tags\": [],\"content\": \"ok\",\"sig\": \"bde9ee6933b01c11a9881a3ea4730c6e7f7d952c165fb7ab3f77488c5f73e6d60ce25a32386f2e1d0244c24fd840f25af5d2d04dc6d229ec0f67c7782e8879d9\"}]"; 2045 2046 ndb_default_config(&config); 2047 assert(ndb_init(&ndb, test_dir, &config)); 2048 2049 ndb_filter_init(f); 2050 assert(ndb_filter_start_field(f, NDB_FILTER_KINDS)); 2051 assert(ndb_filter_add_int_element(f, 1)); 2052 ndb_filter_end_field(f); 2053 ndb_filter_end(f); 2054 2055 sub_id = ndb_subscribe(ndb, f, 1); 2056 2057 ndb_process_event(ndb, bad_note, strlen(bad_note)); 2058 ndb_process_event(ndb, ok_note, strlen(bad_note)); 2059 2060 assert(ndb_wait_for_notes(ndb, sub_id, ¬e_key, 1) == 1); 2061 2062 ndb_begin_query(ndb, &txn); 2063 ndb_query(&txn, f, 1, results, 2, &count); 2064 ndb_end_query(&txn); 2065 2066 assert(count == 1); 2067 assert(memcmp(expected, ndb_note_id(results[0].note), 32) == 0); 2068 2069 ndb_filter_destroy(f); 2070 ndb_destroy(ndb); 2071 } 2072 2073 int main(int argc, const char *argv[]) { 2074 delete_test_db(); 2075 2076 test_replay_attack(); 2077 delete_test_db(); 2078 2079 test_custom_filter(); 2080 delete_test_db(); 2081 2082 test_note_relay_index(); 2083 test_filter_search(); 2084 test_filter_parse_search_json(); 2085 test_parse_filter_json(); 2086 test_filter_eq(); 2087 test_filter_is_subset(); 2088 test_filter_json(); 2089 test_bech32_parsing(); 2090 test_single_url_parsing(); 2091 test_url_parsing(); 2092 test_query(); 2093 test_tag_query(); 2094 test_weird_note_corruption(); 2095 test_parse_content(); 2096 test_subscriptions(); 2097 test_comma_url_parsing(); 2098 test_varints(); 2099 test_bech32_objects(); 2100 //test_block_coding(); 2101 test_encode_decode_invoice(); 2102 test_filters(); 2103 //test_migrate(); 2104 test_fetched_at(); 2105 test_profile_updates(); 2106 test_reaction_counter(); 2107 test_load_profiles(); 2108 test_nip50_profile_search(); 2109 test_basic_event(); 2110 test_empty_tags(); 2111 test_parse_json(); 2112 test_parse_contact_list(); 2113 test_strings_work_before_finalization(); 2114 test_tce(); 2115 test_tce_command_result(); 2116 test_tce_eose(); 2117 test_tce_command_result_empty_msg(); 2118 test_content_len(); 2119 test_fuzz_events(); 2120 2121 // note fetching 2122 test_fetch_last_noteid(); 2123 2124 test_timeline_query(); 2125 2126 // fulltext 2127 test_fulltext(); 2128 2129 // protected queue tests 2130 test_queue_init_pop_push(); 2131 test_queue_thread_safety(); 2132 test_queue_boundary_conditions(); 2133 2134 // memchr stuff 2135 test_fast_strchr(); 2136 2137 // profiles 2138 test_replacement(); 2139 2140 printf("All tests passed!\n"); // Print this if all tests pass. 2141 } 2142 2143 2144