From f443d0fdb9b14d8356e0b42806607ea6e49b8c43 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Sun, 26 Oct 2025 10:02:50 -0700 Subject: [PATCH 01/12] util: remove extra semicolon Signed-off-by: William Casarin --- src/print_util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/print_util.h b/src/print_util.h index c567e991..3521bd10 100644 --- a/src/print_util.h +++ b/src/print_util.h @@ -21,7 +21,7 @@ static void ndb_print_text_search_key(int bytes_size, struct ndb_text_search_key static void print_tag_kv(struct ndb_txn *txn, MDB_val *k, MDB_val *v) { - char hex_id[65], c;; + char hex_id[65], c; struct ndb_note *note; uint64_t ts; From 99f5ff1d42fc08d62022fb8b1734681811257992 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Sun, 26 Oct 2025 09:54:27 -0700 Subject: [PATCH 02/12] migrate: fix some migration error messages these had wrong function names in them Signed-off-by: William Casarin --- src/nostrdb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nostrdb.c b/src/nostrdb.c index 10be7039..33d97f27 100644 --- a/src/nostrdb.c +++ b/src/nostrdb.c @@ -1914,7 +1914,7 @@ static int ndb_rebuild_note_indices(struct ndb_txn *txn, enum ndb_dbs *indices, // ensure they are all index dbs for (i = 0; i < num_indices; i++) { if (!ndb_db_is_index(indices[i])) { - fprintf(stderr, "ndb_rebuild_note_index: %s is not an index db\n", ndb_db_name(indices[i])); + fprintf(stderr, "ndb_rebuild_note_indices: %s is not an index db\n", ndb_db_name(indices[i])); return -1; } } @@ -1923,13 +1923,13 @@ static int ndb_rebuild_note_indices(struct ndb_txn *txn, enum ndb_dbs *indices, for (i = 0; i < num_indices; i++) { index = indices[i]; if (mdb_drop(txn->mdb_txn, index, drop_dbi)) { - fprintf(stderr, "ndb_rebuild_pubkey_index: mdb_drop failed for %s\n", ndb_db_name(index)); + fprintf(stderr, "ndb_rebuild_note_indices: mdb_drop failed for %s\n", ndb_db_name(index)); return -1; } } if ((rc = mdb_cursor_open(txn->mdb_txn, txn->lmdb->dbs[NDB_DB_NOTE], &cur))) { - fprintf(stderr, "ndb_migrate_user_search_indices: mdb_cursor_open failed, error %d\n", rc); + fprintf(stderr, "ndb_rebuild_note_indices: mdb_cursor_open failed, error %d\n", rc); return -1; } From 9c2c4b9cc90c0d417f026d72b5700e630445327f Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 24 Oct 2025 11:37:12 -0700 Subject: [PATCH 03/12] metadata: initial docs and remaining TODO Signed-off-by: William Casarin --- TODO | 2 + docs/metadata.md | 95 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 docs/metadata.md diff --git a/TODO b/TODO index e69de29b..c26cc436 100644 --- a/TODO +++ b/TODO @@ -0,0 +1,2 @@ +implement NDB_WRITER_NOTE_META +test write metadata diff --git a/docs/metadata.md b/docs/metadata.md new file mode 100644 index 00000000..9e2ab3da --- /dev/null +++ b/docs/metadata.md @@ -0,0 +1,95 @@ + +# Note Metadata + +nostrdb supports a flexible metadata system which allows you to store additional information about a nostr note. + +metadata is stored as a sorted list of TVs (tag, value). The lengths are a fixed size but can reference a data table. + +The type follows the "it's ok to be odd" rule. odd tags are opaque, user defined types that can store data of any kind. + +## Binary format + +The binary format starts with a header containing the number of metadata entries. The count is followed by a sorted, aligned list of these entries: + +```c + +// 16 bytes +struct ndb_note_meta { + // 4 bytes + uint8_t version; + uint8_t padding; + uint16_t count; + + // 4 bytes + uint32_t data_table_size; + + // 8 bytes + uint64_t flags; +}; + +// 16 bytes +// 16 bytes +struct ndb_note_meta_entry { + // 4 byte entry header + uint16_t type; + uint16_t flags; + + // additional 4 bytes of aux storage for payloads that are >8 bytes + // + // for reactions types, this is used for counts + // normally this would have been padding but we make use of it + // in our manually packed structure + union { + uint32_t value; + + /* if this is a thread root, this counts the total replies in the thread */ + uint32_t total_reactions; + } aux; + + // 8 byte metadata payload + union { + uint64_t value; + + struct { + uint32_t offset; + uint32_t padding; + } offset; + + struct { + /* number of direct replies */ + uint16_t direct_replies; + uint16_t quotes; + + /* number of replies in this thread */ + uint32_t thread_replies; + } counts; + + // the reaction binmoji[1] for reaction, count is stored in aux + union ndb_reaction_str reaction_str; + } payload; +}; + +``` + +The offset is to a chunk of potentially unaligned data: + +``` +size : varint +data : u8[size] +``` + +### Rationale + +We want the following properties: + +* The ability to quickly iterate/skip over different metadata fields. This is achieved by a fixed table format, without needing to encode/decode like blocks +* The ability to quickly update metadata fields. To update flags or counts can be done via race-safe mutation operations. The mutation can be done inplace (memcpy + poke memory address) +* The table entries can be inplace sorted for binary search lookups on large metadata tables + +## Write thread mutation operations + +We want to support many common operations: + +* Increase reaction count for a specific reaction type + +[binmoji]: https://github.com/jb55/binmoji From 86ae238edc913f825359bd9f9da0b0682c114337 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 24 Oct 2025 11:15:23 -0700 Subject: [PATCH 04/12] binmoji: add to project this is needed for the metadata table Signed-off-by: William Casarin --- Makefile | 2 +- src/binmoji.c | 278 ++++++++++++++++++++++++++++++++++++++++++++ src/binmoji.h | 34 ++++++ src/binmoji_table.h | 169 +++++++++++++++++++++++++++ 4 files changed, 482 insertions(+), 1 deletion(-) create mode 100644 src/binmoji.c create mode 100644 src/binmoji.h create mode 100644 src/binmoji_table.h diff --git a/Makefile b/Makefile index bbbe68ba..4f9b38e3 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ CCAN_HDRS := ccan/ccan/utf8/utf8.h ccan/ccan/container_of/container_of.h ccan/cc HEADERS = deps/lmdb/lmdb.h deps/secp256k1/include/secp256k1.h src/nostrdb.h src/cursor.h src/hex.h src/jsmn.h src/config.h src/random.h src/memchr.h src/cpu.h src/nostr_bech32.h src/block.h src/str_block.h src/print_util.h $(C_BINDINGS) $(CCAN_HDRS) $(BOLT11_HDRS) FLATCC_SRCS=deps/flatcc/src/runtime/json_parser.c deps/flatcc/src/runtime/verifier.c deps/flatcc/src/runtime/builder.c deps/flatcc/src/runtime/emitter.c deps/flatcc/src/runtime/refmap.c BOLT11_SRCS = src/bolt11/bolt11.c src/bolt11/bech32.c src/bolt11/amount.c src/bolt11/hash_u5.c -SRCS = src/nostrdb.c src/invoice.c src/nostr_bech32.c src/content_parser.c src/block.c $(BOLT11_SRCS) $(FLATCC_SRCS) $(CCAN_SRCS) +SRCS = src/nostrdb.c src/invoice.c src/nostr_bech32.c src/content_parser.c src/block.c src/binmoji.c $(BOLT11_SRCS) $(FLATCC_SRCS) $(CCAN_SRCS) LDS = $(OBJS) $(ARS) OBJS = $(SRCS:.c=.o) DEPS = $(OBJS) $(HEADERS) $(ARS) diff --git a/src/binmoji.c b/src/binmoji.c new file mode 100644 index 00000000..f0311450 --- /dev/null +++ b/src/binmoji.c @@ -0,0 +1,278 @@ +#include +#include +#include +#include +#include +#include + +#include "binmoji.h" + +#define PRIMARY_CP_SHIFT 42 +#define HASH_SHIFT 10 +#define TONE1_SHIFT 7 +#define TONE2_SHIFT 4 +#define FLAGS_SHIFT 0 + +#define PRIMARY_CP_MASK 0x3FFFFF +#define HASH_MASK 0xFFFFFFFF +#define TONE_MASK 0x7 +#define FLAGS_MASK 0xF + +typedef struct { + uint32_t hash; + size_t count; + uint32_t components[16]; +} EmojiHashEntry; + +#include "binmoji_table.h" + +const size_t num_hash_entries = + sizeof(binmoji_table) / sizeof(binmoji_table[0]); + +static uint32_t crc32(const uint32_t *data, size_t length) +{ + uint32_t item, bit, crc = 0xFFFFFFFF; + size_t i; + int j; + + if (data == NULL || length == 0) + return 0; + for (i = 0; i < length; ++i) { + item = data[i]; + for (j = 0; j < 32; ++j) { + bit = (item >> (31 - j)) & 1; + if ((crc >> 31) ^ bit) { + crc = (crc << 1) ^ 0x04C11DB7; + } else { + crc = (crc << 1); + } + } + } + return crc; +} + +static int is_base_emoji(uint32_t codepoint) +{ + if (codepoint >= 0x1F3FB && codepoint <= 0x1F3FF) /* Skin Tones */ + return 0; + if (codepoint == 0x200D) /* Zero Width Joiner */ + return 0; + return 1; +} + +void binmoji_parse(const char *emoji_str, struct binmoji *binmoji) +{ + const unsigned char *s; + memset(binmoji, 0, sizeof(struct binmoji)); + s = (const unsigned char *)emoji_str; + + while (*s) { + uint32_t codepoint = 0; + int len = 0; + if (*s < 0x80) { + len = 1; + codepoint = s[0]; + } else if ((*s & 0xE0) == 0xC0) { + len = 2; + codepoint = ((s[0] & 0x1F) << 6) | (s[1] & 0x3F); + } else if ((*s & 0xF0) == 0xE0) { + len = 3; + codepoint = ((s[0] & 0x0F) << 12) | + ((s[1] & 0x3F) << 6) | (s[2] & 0x3F); + } else if ((*s & 0xF8) == 0xF0) { + len = 4; + codepoint = ((s[0] & 0x07) << 18) | + ((s[1] & 0x3F) << 12) | + ((s[2] & 0x3F) << 6) | (s[3] & 0x3F); + } else { + s++; + continue; + } + s += len; + + if (codepoint >= 0x1F3FB && codepoint <= 0x1F3FF) { + uint8_t tone_val = (codepoint - 0x1F3FB) + 1; + if (binmoji->skin_tone1 == 0) + binmoji->skin_tone1 = tone_val; + else if (binmoji->skin_tone2 == 0) + binmoji->skin_tone2 = tone_val; + } else if (is_base_emoji(codepoint)) { + if (binmoji->primary_codepoint == 0) { + binmoji->primary_codepoint = codepoint; + } else if (binmoji->component_count < 16) { + binmoji->component_list + [binmoji->component_count++] = codepoint; + } + } + } + binmoji->component_hash = + crc32(binmoji->component_list, binmoji->component_count); +} + +uint64_t binmoji_encode(const struct binmoji *binmoji) +{ + uint64_t id = 0; + id |= ((uint64_t)(binmoji->primary_codepoint & PRIMARY_CP_MASK) + << PRIMARY_CP_SHIFT); + id |= ((uint64_t)(binmoji->component_hash & HASH_MASK) << HASH_SHIFT); + id |= ((uint64_t)(binmoji->skin_tone1 & TONE_MASK) << TONE1_SHIFT); + id |= ((uint64_t)(binmoji->skin_tone2 & TONE_MASK) << TONE2_SHIFT); + id |= ((uint64_t)(binmoji->flags & FLAGS_MASK) << FLAGS_SHIFT); + return id; +} + +/** + * @brief Comparison function for bsearch. + * + * Compares a target hash key against an EmojiHashEntry's hash. + * @param key Pointer to the target uint32_t hash. + * @param element Pointer to the EmojiHashEntry from the array. + * @return <0 if key is less than element's hash, 0 if equal, >0 if greater. + */ +static int compare_emoji_hash(const void *key, const void *element) +{ + const uint32_t hash_key = *(const uint32_t *)key; + const EmojiHashEntry *entry = (const EmojiHashEntry *)element; + + if (hash_key < entry->hash) { + return -1; + } else if (hash_key > entry->hash) { + return 1; + } else { + return 0; + } +} + +/** + * @brief Optimized lookup using binary search. + */ +static int lookup_binmoji_by_hash(uint32_t hash, uint32_t *out_binmoji, + size_t *out_count) +{ + const EmojiHashEntry *result = + bsearch(&hash, binmoji_table, num_hash_entries, + sizeof(EmojiHashEntry), compare_emoji_hash); + + if (result != NULL) { + *out_count = result->count; + memcpy(out_binmoji, result->components, + (*out_count) * sizeof(uint32_t)); + return 1; /* Found */ + } + + *out_count = 0; + return 0; /* Not found */ +} + +void binmoji_decode(uint64_t id, struct binmoji *binmoji) +{ + memset(binmoji, 0, sizeof(struct binmoji)); + binmoji->primary_codepoint = (id >> PRIMARY_CP_SHIFT) & PRIMARY_CP_MASK; + binmoji->component_hash = (id >> HASH_SHIFT) & HASH_MASK; + binmoji->skin_tone1 = (id >> TONE1_SHIFT) & TONE_MASK; + binmoji->skin_tone2 = (id >> TONE2_SHIFT) & TONE_MASK; + binmoji->flags = (id >> FLAGS_SHIFT) & FLAGS_MASK; + if (binmoji->component_hash != 0) { + lookup_binmoji_by_hash(binmoji->component_hash, + binmoji->component_list, + &binmoji->component_count); + } +} + +static int append_utf8(char *buf, size_t buf_size, size_t *offset, + uint32_t codepoint) +{ + char *p; + int bytes_to_write = 0; + + if (!buf) + return 0; + if (codepoint < 0x80) + bytes_to_write = 1; + else if (codepoint < 0x800) + bytes_to_write = 2; + else if (codepoint < 0x10000) + bytes_to_write = 3; + else if (codepoint < 0x110000) + bytes_to_write = 4; + else + return 0; + if (*offset + bytes_to_write >= buf_size) + return 0; + + p = buf + *offset; + if (bytes_to_write == 1) { + *p = (char)codepoint; + } else if (bytes_to_write == 2) { + p[0] = 0xC0 | (codepoint >> 6); + p[1] = 0x80 | (codepoint & 0x3F); + } else if (bytes_to_write == 3) { + p[0] = 0xE0 | (codepoint >> 12); + p[1] = 0x80 | ((codepoint >> 6) & 0x3F); + p[2] = 0x80 | (codepoint & 0x3F); + } else { + p[0] = 0xF0 | (codepoint >> 18); + p[1] = 0x80 | ((codepoint >> 12) & 0x3F); + p[2] = 0x80 | ((codepoint >> 6) & 0x3F); + p[3] = 0x80 | (codepoint & 0x3F); + } + *offset += bytes_to_write; + return bytes_to_write; +} + +void binmoji_to_string(const struct binmoji *binmoji, char *out_str, + size_t out_str_size) +{ + size_t i, offset; + uint32_t comp; + int needs_zwj, is_country_flag, is_subdivision_flag, no_zwj_sequence; + + if (!binmoji || !out_str || out_str_size == 0) + return; + + offset = 0; + out_str[0] = '\0'; + + is_country_flag = (binmoji->primary_codepoint >= 0x1F1E6 && + binmoji->primary_codepoint <= 0x1F1FF); + + is_subdivision_flag = (binmoji->primary_codepoint == 0x1F3F4 && + binmoji->component_count > 0 && + binmoji->component_list[0] >= 0xE0020 && + binmoji->component_list[0] <= 0xE007F); + + no_zwj_sequence = is_country_flag || is_subdivision_flag; + + if (binmoji->primary_codepoint > 0) { + append_utf8(out_str, out_str_size, &offset, + binmoji->primary_codepoint); + } + + if (binmoji->skin_tone1 > 0) { + append_utf8(out_str, out_str_size, &offset, + 0x1F3FB + binmoji->skin_tone1 - 1); + } + + for (i = 0; i < binmoji->component_count; i++) { + comp = binmoji->component_list[i]; + needs_zwj = + (comp != 0xFE0F && comp != 0x20E3 && !no_zwj_sequence); + + if (needs_zwj) { + append_utf8(out_str, out_str_size, &offset, + 0x200D); /* ZWJ */ + } + append_utf8(out_str, out_str_size, &offset, comp); + + if (i == binmoji->component_count - 1 && + binmoji->skin_tone2 > 0) { + append_utf8(out_str, out_str_size, &offset, + 0x1F3FB + binmoji->skin_tone2 - 1); + } + } + + if (offset < out_str_size) + out_str[offset] = '\0'; + else if (out_str_size > 0) + out_str[out_str_size - 1] = '\0'; +} diff --git a/src/binmoji.h b/src/binmoji.h new file mode 100644 index 00000000..068efbde --- /dev/null +++ b/src/binmoji.h @@ -0,0 +1,34 @@ + +#ifndef BINMOJI_H +#define BINMOJI_H + +#include +#include + +struct binmoji { + uint32_t primary_codepoint; + uint32_t component_list[16]; + size_t component_count; + uint32_t component_hash; + uint8_t skin_tone1; + uint8_t skin_tone2; + uint8_t flags; +}; + +static const uint64_t USER_FLAG_MASK = 1 << 3; + +void binmoji_to_string(const struct binmoji *binmoji, char *out_str, size_t out_str_size); +void binmoji_decode(uint64_t id, struct binmoji *binmoji); +void binmoji_parse(const char *emoji, struct binmoji *binmoji); +uint64_t binmoji_encode(const struct binmoji *binmoji); + +/* some user flag helpers */ +static __inline uint64_t binmoji_set_user_flag(uint64_t binmoji, uint8_t enable) { + return enable ? (binmoji | USER_FLAG_MASK) : (binmoji & ~USER_FLAG_MASK); +} + +static __inline uint8_t binmoji_get_user_flag(uint64_t binmoji) { + return (binmoji & USER_FLAG_MASK) == USER_FLAG_MASK; +} + +#endif /* BINMOJI_H */ diff --git a/src/binmoji_table.h b/src/binmoji_table.h new file mode 100644 index 00000000..2e9b9081 --- /dev/null +++ b/src/binmoji_table.h @@ -0,0 +1,169 @@ +#ifndef EMOJI_HASH_TABLE_H +#define EMOJI_HASH_TABLE_H + +#include + +/* This file is auto-generated by generate_hash_table.py */ + +static const EmojiHashEntry binmoji_table[] = { + {0x009D7FB7, 1, {0x1F1E9}}, + {0x03827B8D, 2, {0xFE0F, 0x2642}}, + {0x03A767C3, 1, {0x2194}}, + {0x045C6200, 1, {0x1F1E8}}, + {0x07667A74, 1, {0x2195}}, + {0x08C7368D, 2, {0x1F9BD, 0x27A1}}, + {0x091F44D9, 1, {0x1F1EB}}, + {0x0A0040E3, 2, {0xFE0F, 0x2640}}, + {0x0A4D693F, 2, {0x26A7, 0xFE0F}}, + {0x0DDE596E, 1, {0x1F1EA}}, + {0x0FF6A5EC, 1, {0x1F52C}}, + {0x1035EC63, 2, {0x2194, 0xFE0F}}, + {0x10E7495E, 3, {0x1F469, 0x1F467, 0x1F466}}, + {0x11C8318C, 2, {0xFE0F, 0x1F525}}, + {0x11CE3159, 3, {0x1F9D1, 0x1F9D2, 0x1F9D2}}, + {0x12E2D1B2, 3, {0x2642, 0x27A1, 0xFE0F}}, + {0x1399096B, 1, {0x1F1ED}}, + {0x13BA8F92, 2, {0xFE0F, 0x20E3}}, + {0x142654E9, 3, {0x1F469, 0x1F467, 0x1F467}}, + {0x175814DC, 1, {0x1F1EC}}, + {0x17ADDABC, 2, {0x1F5E8, 0xFE0F}}, + {0x18E0A986, 2, {0x2695, 0xFE0F}}, + {0x190FF79B, 2, {0x1F9AF, 0x27A1}}, + {0x1A1B3205, 1, {0x1F1EF}}, + {0x1AD4A79F, 2, {0x1F467, 0x1F467}}, + {0x1B706904, 4, {0x2764, 0xFE0F, 0x1F48B, 0x1F9D1}}, + {0x1E15BA28, 2, {0x1F467, 0x1F466}}, + {0x1EDA2FB2, 1, {0x1F1EE}}, + {0x24BD6E8D, 1, {0x1F527}}, + {0x253E99D4, 4, {0x2642, 0xFE0F, 0x27A1, 0xFE0F}}, + {0x2586DE9E, 1, {0x27A1}}, + {0x2D3F55E3, 1, {0x1F525}}, + {0x2DE4D231, 2, {0x2642, 0x27A1}}, + {0x2F4DEBFD, 1, {0x20E3}}, + {0x30A9040E, 1, {0x2695}}, + {0x36F7248C, 1, {0x2640}}, + {0x38D2C20A, 1, {0x1F1E6}}, + {0x3C13DFBD, 1, {0x1F1E7}}, + {0x3C5FF473, 2, {0x1F9D1, 0x1F9D2}}, + {0x3D07A1FC, 1, {0x1F384}}, + {0x3DEA22D7, 1, {0x2696}}, + {0x3F751FE2, 1, {0x2642}}, + {0x417843A9, 1, {0x1F5E8}}, + {0x41CA5100, 2, {0x1F9BC, 0x27A1}}, + {0x41CF821E, 1, {0x1F1FA}}, + {0x450E9FA9, 1, {0x1F1FB}}, + {0x484DB970, 1, {0x1F1F8}}, + {0x48D80AE6, 3, {0x2640, 0xFE0F, 0x27A1}}, + {0x4C8CA4C7, 1, {0x1F1F9}}, + {0x4F9091D9, 3, {0x1F9BD, 0x27A1, 0xFE0F}}, + {0x52CBF4C2, 1, {0x1F1FE}}, + {0x560AE975, 1, {0x1F1FF}}, + {0x5718DDA5, 2, {0x1F466, 0x1F466}}, + {0x58A121D3, 2, {0x1F430, 0x1F469}}, + {0x58AC2E7A, 2, {0x2764, 0x1F468}}, + {0x59388BEE, 2, {0x2195, 0xFE0F}}, + {0x59EA2ED3, 3, {0x1F469, 0x1F466, 0x1F466}}, + {0x5B49CFAC, 1, {0x1F1FC}}, + {0x5B5F53CD, 2, {0x1F91D, 0x1F9D1}}, + {0x5C603C64, 2, {0x1F430, 0x1F468}}, + {0x5C6D33CD, 2, {0x2764, 0x1F469}}, + {0x5DF0BE19, 2, {0xFE0F, 0x1F308}}, + {0x5F88D21B, 1, {0x1F1FD}}, + {0x60EA7029, 3, {0x2764, 0xFE0F, 0x1F9D1}}, + {0x6107DA76, 1, {0x1F308}}, + {0x63067211, 1, {0x1F1F3}}, + {0x63ED85D3, 1, {0xFE0F}}, + {0x67C76FA6, 1, {0x1F1F2}}, + {0x68B58497, 3, {0xFE0F, 0x2640, 0xFE0F}}, + {0x6A84497F, 1, {0x1F1F1}}, + {0x6B8E2FD6, 3, {0x2764, 0x1F48B, 0x1F469}}, + {0x6B8F897C, 2, {0x2642, 0xFE0F}}, + {0x6E4554C8, 1, {0x1F1F0}}, + {0x6F4F3261, 3, {0x2764, 0x1F48B, 0x1F468}}, + {0x6F512A89, 1, {0x1F393}}, + {0x6FD4811E, 2, {0x1FAEF, 0x1F9D1}}, + {0x700204CD, 1, {0x1F1F7}}, + {0x74C3197A, 1, {0x1F1F6}}, + {0x75AC9A14, 1, {0x1F9D2}}, + {0x785EE7CD, 1, {0x2B1B}}, + {0x79803FA3, 1, {0x1F1F5}}, + {0x7D412214, 1, {0x1F1F4}}, + {0x7D8F27C6, 2, {0xFE0F, 0x1F5E8}}, + {0x868D184D, 3, {0xFE0F, 0x1F5E8, 0xFE0F}}, + {0x90288C78, 1, {0x1F3A8}}, + {0x92B7B14D, 1, {0x1F37C}}, + {0x9499EDEC, 6, {0xE0067, 0xE0062, 0xE0073, 0xE0063, 0xE0074, 0xE007F}}, + {0x962A74D1, 1, {0x1F4A5}}, + {0x996BD75C, 6, {0xE0067, 0xE0062, 0xE0077, 0xE006C, 0xE0073, 0xE007F}}, + {0x99F2B752, 4, {0x2764, 0xFE0F, 0x1F48B, 0x1F468}}, + {0x9A52E21B, 1, {0x2620}}, + {0x9B6DABCE, 3, {0xFE0F, 0x26A7, 0xFE0F}}, + {0x9D1458D9, 2, {0x1F32B, 0xFE0F}}, + {0x9D33AAE5, 4, {0x2764, 0xFE0F, 0x1F48B, 0x1F469}}, + {0x9F647327, 1, {0x1FAF2}}, + {0xA0DE8B8D, 2, {0x1F468, 0x1F467}}, + {0xA38B28DE, 2, {0x27A1, 0xFE0F}}, + {0xA41F963A, 2, {0x1F468, 0x1F466}}, + {0xA4D50120, 1, {0x1F9AF}}, + {0xA524171C, 1, {0x1F3A4}}, + {0xA59E4F46, 2, {0x2708, 0xFE0F}}, + {0xA7E7F202, 1, {0x1F4A8}}, + {0xA818439D, 3, {0x2642, 0xFE0F, 0x27A1}}, + {0xA9EDDAD6, 1, {0x1F3EB}}, + {0xAAA4D4DB, 1, {0x1F4AB}}, + {0xAADD10BE, 2, {0xFE0F, 0x1F4A5}}, + {0xAAF80CF0, 1, {0x1F373}}, + {0xABEA84B5, 3, {0x1F468, 0x1F466, 0x1F466}}, + {0xAFB3FA54, 1, {0x1F33E}}, + {0xB36B9764, 1, {0x1F3ED}}, + {0xB715C37F, 3, {0x1F9AF, 0x27A1, 0xFE0F}}, + {0xB93E3755, 1, {0x1F680}}, + {0xB9CF624F, 6, {0xE0067, 0xE0062, 0xE0065, 0xE006E, 0xE0067, 0xE007F}}, + {0xBD903BBF, 3, {0x1F9BC, 0x27A1, 0xFE0F}}, + {0xBFFE1D2B, 2, {0x2640, 0x27A1}}, + {0xC222DF26, 1, {0x1F467}}, + {0xC249C89D, 2, {0x2744, 0xFE0F}}, + {0xC3F70111, 2, {0x2696, 0xFE0F}}, + {0xC6E3C291, 1, {0x1F466}}, + {0xD08B67ED, 1, {0x1F9B0}}, + {0xD1EE369F, 2, {0xFE0F, 0x26A7}}, + {0xD44A7A5A, 1, {0x1F9B1}}, + {0xD9095C83, 1, {0x1F9B2}}, + {0xD9DD8D9B, 2, {0x1F91D, 0x1F468}}, + {0xDA2EF02C, 2, {0x2764, 0x1F9D1}}, + {0xDD1C902C, 2, {0x1F91D, 0x1F469}}, + {0xDD2358A8, 1, {0x1FA79}}, + {0xDDC84134, 1, {0x1F9B3}}, + {0xDEE2E232, 2, {0x1F430, 0x1F9D1}}, + {0xE146E13E, 1, {0x1F9BD}}, + {0xE1D43CC7, 2, {0xFE0F, 0x1FA79}}, + {0xE268AE7F, 3, {0x2764, 0xFE0F, 0x1F468}}, + {0xE2E7E338, 3, {0x1F468, 0x1F467, 0x1F466}}, + {0xE54BC011, 1, {0x1F7E9}}, + {0xE587FC89, 1, {0x1F9BC}}, + {0xE626FE8F, 3, {0x1F468, 0x1F467, 0x1F467}}, + {0xE6A9B3C8, 3, {0x2764, 0xFE0F, 0x1F469}}, + {0xE6B50FAB, 1, {0x1F4BB}}, + {0xE7485CE4, 2, {0x1F9D2, 0x1F9D2}}, + {0xE99742FF, 2, {0x1FAEF, 0x1F469}}, + {0xE9D3EC00, 2, {0x1F469, 0x1F467}}, + {0xECC9FB7F, 1, {0x1F7EB}}, + {0xED12F1B7, 2, {0x1F469, 0x1F466}}, + {0xED1952F0, 1, {0x26A7}}, + {0xED565F48, 2, {0x1FAEF, 0x1F468}}, + {0xEDCDEC37, 3, {0x2764, 0x1F48B, 0x1F9D1}}, + {0xF0B72869, 4, {0x2640, 0xFE0F, 0x27A1, 0xFE0F}}, + {0xF22298C9, 3, {0x2640, 0x27A1, 0xFE0F}}, + {0xF4674A4F, 1, {0x1F32B}}, + {0xF660789F, 1, {0x2708}}, + {0xF7EA938C, 1, {0x2744}}, + {0xF8F25FAE, 1, {0x1F4BC}}, + {0xF9954666, 2, {0x2640, 0xFE0F}}, + {0xFAAF4B8D, 3, {0xFE0F, 0x2642, 0xFE0F}}, + {0xFC3A4497, 2, {0x2620, 0xFE0F}}, + {0xFCADD74B, 1, {0x1F692}}, + {0xFF01B13B, 1, {0x1F9BA}}, + {0xFFEAA8A7, 1, {0x1FA70}}, +}; + +#endif /* EMOJI_HASH_TABLE_H */ From 53313c747a9399bb9ef2aa03f1bbe13f28a4fa58 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 24 Oct 2025 11:16:04 -0700 Subject: [PATCH 05/12] metadata: add initial metadata table api Signed-off-by: William Casarin --- Makefile | 2 +- src/metadata.c | 296 +++++++++++++++++++++++++++++++++++++++++++++++++ src/nostrdb.c | 64 +++++++++-- src/nostrdb.h | 70 +++++++++++- 4 files changed, 423 insertions(+), 9 deletions(-) create mode 100644 src/metadata.c diff --git a/Makefile b/Makefile index 4f9b38e3..be477e6a 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ CCAN_HDRS := ccan/ccan/utf8/utf8.h ccan/ccan/container_of/container_of.h ccan/cc HEADERS = deps/lmdb/lmdb.h deps/secp256k1/include/secp256k1.h src/nostrdb.h src/cursor.h src/hex.h src/jsmn.h src/config.h src/random.h src/memchr.h src/cpu.h src/nostr_bech32.h src/block.h src/str_block.h src/print_util.h $(C_BINDINGS) $(CCAN_HDRS) $(BOLT11_HDRS) FLATCC_SRCS=deps/flatcc/src/runtime/json_parser.c deps/flatcc/src/runtime/verifier.c deps/flatcc/src/runtime/builder.c deps/flatcc/src/runtime/emitter.c deps/flatcc/src/runtime/refmap.c BOLT11_SRCS = src/bolt11/bolt11.c src/bolt11/bech32.c src/bolt11/amount.c src/bolt11/hash_u5.c -SRCS = src/nostrdb.c src/invoice.c src/nostr_bech32.c src/content_parser.c src/block.c src/binmoji.c $(BOLT11_SRCS) $(FLATCC_SRCS) $(CCAN_SRCS) +SRCS = src/nostrdb.c src/invoice.c src/nostr_bech32.c src/content_parser.c src/block.c src/binmoji.c src/metadata.c $(BOLT11_SRCS) $(FLATCC_SRCS) $(CCAN_SRCS) LDS = $(OBJS) $(ARS) OBJS = $(SRCS:.c=.o) DEPS = $(OBJS) $(HEADERS) $(ARS) diff --git a/src/metadata.c b/src/metadata.c new file mode 100644 index 00000000..93be0f4e --- /dev/null +++ b/src/metadata.c @@ -0,0 +1,296 @@ + +#include "nostrdb.h" +#include "binmoji.h" + +// these must be byte-aligned, they are directly accessing the serialized data +// representation +#pragma pack(push, 1) + +// 16 bytes +struct ndb_note_meta_entry { + // 4 byte entry header + uint16_t type; + uint16_t flags; + + // additional 4 bytes of aux storage for payloads that are >8 bytes + // + // for reactions types, this is used for counts + // normally this would have been padding but we make use of it + // in our manually packed structure + uint32_t aux; + + // 8 byte metadata payload + union { + uint64_t value; + + struct { + uint32_t offset; + uint32_t padding; + } offset; + + // the reaction binmoji[1] for reaction, count is stored in aux + union ndb_reaction_str reaction_str; + } payload; +}; +STATIC_ASSERT(sizeof(struct ndb_note_meta_entry) == 16, note_meta_entry_should_be_16_bytes); + +/* newtype wrapper around the header entry */ +struct ndb_note_meta { + // 4 bytes + uint8_t version; + uint8_t padding; + uint16_t count; + + // 4 bytes + uint32_t data_table_size; + + // 8 bytes + uint64_t flags; +}; +STATIC_ASSERT(sizeof(struct ndb_note_meta) == 16, note_meta_entry_should_be_16_bytes); + +#pragma pack(pop) + +int ndb_reaction_str_is_emoji(union ndb_reaction_str str) +{ + return binmoji_get_user_flag(str.binmoji) == 0; +} + +uint16_t ndb_note_meta_entries_count(struct ndb_note_meta *meta) +{ + return meta->count; +} + +static int ndb_reaction_set_emoji(union ndb_reaction_str *str, const char *emoji) +{ + struct binmoji binmoji; + /* TODO: parse failures? */ + binmoji_parse(emoji, &binmoji); + str->binmoji = binmoji_encode(&binmoji); + return 1; +} + +static int ndb_reaction_set_str(union ndb_reaction_str *reaction, const char *str) +{ + int i; + char c; + + /* this is like memset'ing the packed string to all 0s as well */ + reaction->binmoji = 0; + + /* set the binmoji user flag so we can catch corrupt binmojis */ + /* this is in the LSB so it will only touch reaction->packed.flag */ + reaction->binmoji = binmoji_set_user_flag(reaction->binmoji, 1); + assert(reaction->packed.flag != 0); + + for (i = 0; i < 7; i++) { + c = str[i]; + /* string is too big */ + if (i == 6 && c != '\0') + return 0; + reaction->packed.str[i] = c; + if (c == '\0') + return 1; + } + + return 0; +} + +/* set the value of an ndb_reaction_str to an emoji or small string */ +int ndb_reaction_set(union ndb_reaction_str *reaction, const char *str) +{ + struct binmoji binmoji; + char output_emoji[136]; + + /* our variant of emoji detection is to simply try to create + * a binmoji and parse it again. if we round-trip successfully + * then we know its an emoji, or at least a simple string + */ + binmoji_parse(str, &binmoji); + reaction->binmoji = binmoji_encode(&binmoji); + binmoji_to_string(&binmoji, output_emoji, sizeof(output_emoji)); + + /* round trip is successful, let's just use binmojis for this encoding */ + if (!strcmp(output_emoji, str)) + return 1; + + /* no round trip? let's just set a non-emoji string */ + return ndb_reaction_set_str(reaction, str); +} + +static void ndb_note_meta_header_init(struct ndb_note_meta *meta) +{ + meta->version = 1; + meta->flags = 0; + meta->count = 0; + meta->data_table_size = 0; +} + +static inline size_t ndb_note_meta_entries_size(struct ndb_note_meta *meta) +{ + return (sizeof(struct ndb_note_meta_entry) * meta->count); +} + +void *ndb_note_meta_data_table(struct ndb_note_meta *meta, size_t *size) +{ + return meta + ndb_note_meta_entries_size(meta); +} + +size_t ndb_note_meta_total_size(struct ndb_note_meta *header) +{ + size_t total_size = sizeof(*header) + header->data_table_size + ndb_note_meta_entries_size(header); + assert((total_size % 8) == 0); + return total_size; +} + +struct ndb_note_meta_entry *ndb_note_meta_add_entry(struct ndb_note_meta_builder *builder) +{ + struct ndb_note_meta *header = (struct ndb_note_meta *)builder->cursor.start; + struct ndb_note_meta_entry *entry = NULL; + + assert(builder->cursor.p != builder->cursor.start); + + if (!(entry = cursor_malloc(&builder->cursor, sizeof(*entry)))) + return NULL; + + /* increase count entry count */ + header->count++; + + return entry; +} + +int ndb_note_meta_builder_init(struct ndb_note_meta_builder *builder, unsigned char *buf, size_t bufsize) +{ + make_cursor(buf, buf + bufsize, &builder->cursor); + + /* allocate some space for the header */ + if (!cursor_malloc(&builder->cursor, sizeof(struct ndb_note_meta))) + return 0; + + ndb_note_meta_header_init((struct ndb_note_meta*)builder->cursor.start); + + return 1; +} + +/* note flags are stored in the header entry */ +uint32_t ndb_note_meta_flags(struct ndb_note_meta *meta) +{ + return meta->flags; +} + +/* note flags are stored in the header entry */ +void ndb_note_meta_set_flags(struct ndb_note_meta *meta, uint32_t flags) +{ + meta->flags = flags; +} + +static int compare_entries(const void *a, const void *b) +{ + struct ndb_note_meta_entry *entry_a, *entry_b; + uint64_t binmoji_a, binmoji_b; + int res; + + entry_a = (struct ndb_note_meta_entry *)a; + entry_b = (struct ndb_note_meta_entry *)b; + + res = entry_a->type - entry_b->type; + + if (res == 0 && entry_a->type == NDB_NOTE_META_REACTION) { + /* we sort by reaction string for stability */ + binmoji_a = entry_a->payload.reaction_str.binmoji; + binmoji_b = entry_b->payload.reaction_str.binmoji; + + if (binmoji_a < binmoji_b) { + return -1; + } else if (binmoji_a > binmoji_b) { + return 1; + } else { + return 0; + } + } else { + return res; + } +} + +struct ndb_note_meta_entry *ndb_note_meta_entries(struct ndb_note_meta *meta) +{ + /* entries start at the end of the header record */ + return (struct ndb_note_meta_entry *)((uint8_t*)meta + sizeof(*meta)); +} + +void ndb_note_meta_build(struct ndb_note_meta_builder *builder, struct ndb_note_meta **meta) +{ + /* sort entries */ + struct ndb_note_meta_entry *entries; + struct ndb_note_meta *header = (struct ndb_note_meta*)builder->cursor.start; + + /* not initialized */ + assert(builder->cursor.start != builder->cursor.p); + + if (header->count > 0) { + entries = ndb_note_meta_entries(header); + + /* ensure entries are always sorted so bsearch is possible for large metadata + * entries. probably won't need that for awhile though */ + qsort(entries, header->count, sizeof(struct ndb_note_meta_entry), compare_entries); + } + + *meta = header; + return; +} + +/* find a metadata entry, optionally matching a payload */ +struct ndb_note_meta_entry *ndb_note_meta_find_entry(struct ndb_note_meta *meta, uint16_t type, uint64_t *payload) +{ + struct ndb_note_meta_entry *entries, *entry; + int i; + + if (meta->count == 0) + return NULL; + + entries = ndb_note_meta_entries(meta); + + for (i = 0; i < meta->count; i++) { + entry = &entries[i]; + if (entry->type != type) + continue; + if (payload && *payload != entry->payload.value) + continue; + return entry; + } + + return NULL; +} + +void ndb_note_meta_reaction_set(struct ndb_note_meta_entry *entry, uint32_t count, union ndb_reaction_str str) +{ + entry->type = NDB_NOTE_META_REACTION; + entry->flags = 0; + entry->aux = count; + entry->payload.reaction_str = str; +} + +/* sets the quote repost count for this note */ +void ndb_note_meta_quotes_set(struct ndb_note_meta_entry *entry, uint32_t count) +{ + entry->type = NDB_NOTE_META_QUOTES; + entry->flags = 0; + entry->aux = count; + /* unused */ + entry->payload.value = 0; +} + +uint32_t ndb_note_meta_reaction_count(struct ndb_note_meta_entry *entry) +{ + return entry->aux; +} + +void ndb_note_meta_reaction_set_count(struct ndb_note_meta_entry *entry, uint32_t count) +{ + entry->aux = count; +} + +union ndb_reaction_str ndb_note_meta_reaction_str(struct ndb_note_meta_entry *entry) +{ + return entry->payload.reaction_str; +} diff --git a/src/nostrdb.c b/src/nostrdb.c index 33d97f27..27a1d038 100644 --- a/src/nostrdb.c +++ b/src/nostrdb.c @@ -142,6 +142,7 @@ enum ndb_writer_msgtype { NDB_WRITER_BLOCKS, // write parsed note blocks NDB_WRITER_MIGRATE, // migrate the database NDB_WRITER_NOTE_RELAY, // we already have the note, but we have more relays to write + NDB_WRITER_NOTE_META, // write note metadata to the db }; // keys used for storing data in the NDB metadata database (NDB_DB_NDB_META) @@ -2213,6 +2214,11 @@ struct ndb_writer_ndb_meta { uint64_t version; }; +struct ndb_writer_note_meta { + unsigned char note_id[32]; + struct ndb_note_meta *metadata; +}; + // Used in the writer thread when writing ndb_profile_fetch_record's // kv = pubkey: recor struct ndb_writer_last_fetch { @@ -2237,6 +2243,7 @@ struct ndb_writer_msg { struct ndb_writer_ndb_meta ndb_meta; struct ndb_writer_last_fetch last_fetch; struct ndb_writer_blocks blocks; + struct ndb_writer_note_meta note_meta; }; }; @@ -3334,6 +3341,42 @@ static unsigned char *ndb_note_last_id_tag(struct ndb_note *note, char type) return last; } +int ndb_set_note_meta(struct ndb *ndb, const unsigned char *id, struct ndb_note_meta *meta) +{ + struct ndb_writer_msg msg; + struct ndb_writer_note_meta *meta_msg = &msg.note_meta; + + msg.type = NDB_WRITER_NOTE_META; + + memcpy(meta_msg->note_id, id, 32); + meta_msg->metadata = meta; + + return ndb_writer_queue_msg(&ndb->writer, &msg); +} + +int ndb_writer_set_note_meta(struct ndb_txn *txn, const unsigned char *id, struct ndb_note_meta *meta) +{ + int rc; + MDB_val k, v; + MDB_dbi note_meta_db; + + // get dbs + note_meta_db = txn->lmdb->dbs[NDB_DB_META]; + + k.mv_data = (unsigned char *)id; + k.mv_size = 32; + + v.mv_data = (unsigned char *)meta; + v.mv_size = ndb_note_meta_total_size(meta); + + if ((rc = mdb_put(txn->mdb_txn, note_meta_db, &k, &v, 0))) { + ndb_debug("ndb_set_note_meta: write note metadata to db failed: %s\n", mdb_strerror(rc)); + return 0; + } + + return 1; +} + void *ndb_get_note_meta(struct ndb_txn *txn, const unsigned char *id, size_t *len) { MDB_val k, v; @@ -5514,13 +5557,16 @@ static void *ndb_writer_thread(void *data) for (i = 0 ; i < popped; i++) { msg = &msgs[i]; switch (msg->type) { - case NDB_WRITER_NOTE: needs_commit = 1; break; - case NDB_WRITER_PROFILE: needs_commit = 1; break; - case NDB_WRITER_DBMETA: needs_commit = 1; break; - case NDB_WRITER_PROFILE_LAST_FETCH: needs_commit = 1; break; - case NDB_WRITER_BLOCKS: needs_commit = 1; break; - case NDB_WRITER_MIGRATE: needs_commit = 1; break; - case NDB_WRITER_NOTE_RELAY: needs_commit = 1; break; + case NDB_WRITER_NOTE: + case NDB_WRITER_NOTE_META: + case NDB_WRITER_PROFILE: + case NDB_WRITER_DBMETA: + case NDB_WRITER_PROFILE_LAST_FETCH: + case NDB_WRITER_BLOCKS: + case NDB_WRITER_MIGRATE: + case NDB_WRITER_NOTE_RELAY: + needs_commit = 1; + break; case NDB_WRITER_QUIT: break; } } @@ -5561,6 +5607,10 @@ static void *ndb_writer_thread(void *data) ndb_debug("failed to write note\n"); } break; + case NDB_WRITER_NOTE_META: + ndb_writer_set_note_meta(&txn, msg->note_meta.note_id, msg->note_meta.metadata); + break; + case NDB_WRITER_NOTE: note_nkey = ndb_write_note(&txn, &msg->note, scratch, diff --git a/src/nostrdb.h b/src/nostrdb.h index 75b8db66..05691c2a 100644 --- a/src/nostrdb.h +++ b/src/nostrdb.h @@ -6,6 +6,10 @@ #include "win.h" #include "cursor.h" +/* static assert helper */ +#define STATIC_ASSERT(cond, msg) typedef char static_assert_##msg[(cond) ? 1 : -1] +/* #define STATIC_ASSERT(cond, msg) */ + // maximum number of filters allowed in a filter group #define NDB_PACKED_STR 0x1 #define NDB_PACKED_ID 0x2 @@ -34,9 +38,21 @@ struct ndb_note; struct ndb_tag; struct ndb_tags; struct ndb_lmdb; +struct ndb_note_meta; +struct ndb_note_meta_entry; +struct ndb_note_meta_builder; union ndb_packed_str; struct bolt11; +/* Types, standard types are multiplied by 2, since odd types are user defined. + * We explicitly multiply by two in the enum to be unambiguous + */ +enum ndb_metadata_type { + NDB_NOTE_META_RESERVED = 0, /* not used */ + NDB_NOTE_META_REACTION = 2, /* count of all the reactions on a post, grouped by different reaction strings */ + NDB_NOTE_META_QUOTES = 4, /* count of all the all the notes with q tag references to this note */ +}; + // some bindings like swift needs help with forward declared pointers struct ndb_tag_ptr { struct ndb_tag *ptr; }; struct ndb_tags_ptr { struct ndb_tags *ptr; }; @@ -48,6 +64,33 @@ struct ndb_t { struct ndb *ndb; }; +/* Compact reaction strings for the metadata table. + * + * We compact all emojis into 64bits using binmojis (github.com/jb55/binmoji) + * + * 6-byte non-emoji strings are also supported in the same memory layout. This + * is achieved by reserving the LSB byte of the compact string, which overlaps + * with the binmojis user flag bit. If this flag is set, then we know its not + * really a bitmoji, its a compact string. + */ +union ndb_reaction_str { + uint64_t binmoji; + struct { + /* flag is at LSB, which aligns with our binmoji user flag */ + uint8_t flag; + char str[7]; + } packed; +}; +STATIC_ASSERT(sizeof(union ndb_reaction_str) == 8, reaction_string_must_be_8_bytes); + +/* An ndb_note_meta builder. Maintains a cursor of a fixed sized buffer while adding + * metadata entries. + * + */ +struct ndb_note_meta_builder { + struct cursor cursor; +}; + struct ndb_str { // NDB_PACKED_STR, NDB_PACKED_ID unsigned char flag; @@ -241,6 +284,14 @@ struct ndb_note_relay_iterator { void *mdb_cur; }; +struct ndb_note_meta_iterator { + struct ndb_note_meta *header; + struct ndb_note_meta *cur; + + // current outer index + int index; +}; + struct ndb_iterator { struct ndb_note *note; struct ndb_tag *tag; @@ -527,7 +578,6 @@ uint64_t ndb_get_notekey_by_id(struct ndb_txn *txn, const unsigned char *id); uint64_t ndb_get_profilekey_by_pubkey(struct ndb_txn *txn, const unsigned char *id); struct ndb_note *ndb_get_note_by_id(struct ndb_txn *txn, const unsigned char *id, size_t *len, uint64_t *primkey); struct ndb_note *ndb_get_note_by_key(struct ndb_txn *txn, uint64_t key, size_t *len); -void *ndb_get_note_meta(struct ndb_txn *txn, const unsigned char *id, size_t *len); int ndb_note_seen_on_relay(struct ndb_txn *txn, uint64_t note_key, const char *relay); void ndb_destroy(struct ndb *); @@ -603,6 +653,24 @@ void ndb_text_search_config_set_limit(struct ndb_text_search_config *, int limit // QUERY int ndb_query(struct ndb_txn *txn, struct ndb_filter *filters, int num_filters, struct ndb_query_result *results, int result_capacity, int *count); +// NOTE METADATA +void *ndb_get_note_meta(struct ndb_txn *txn, const unsigned char *id, size_t *len); +int ndb_set_note_meta(struct ndb *ndb, const unsigned char *id, struct ndb_note_meta *meta); +size_t ndb_note_meta_total_size(struct ndb_note_meta *header); +int ndb_note_meta_builder_init(struct ndb_note_meta_builder *builder, unsigned char *, size_t); +void ndb_note_meta_build(struct ndb_note_meta_builder *builder, struct ndb_note_meta **meta); +uint16_t ndb_note_meta_entries_count(struct ndb_note_meta *meta); +struct ndb_note_meta_entry *ndb_note_meta_entries(struct ndb_note_meta *meta); +struct ndb_note_meta_entry *ndb_note_meta_add_entry(struct ndb_note_meta_builder *builder); +size_t ndb_note_meta_total_size(struct ndb_note_meta *meta); +void ndb_note_meta_reaction_set(struct ndb_note_meta_entry *entry, uint32_t count, union ndb_reaction_str str); +void ndb_note_meta_quotes_set(struct ndb_note_meta_entry *entry, uint32_t count); +uint32_t ndb_note_meta_reaction_count(struct ndb_note_meta_entry *entry); + +// META STRINGS +int ndb_reaction_set(union ndb_reaction_str *reaction, const char *str); +int ndb_reaction_str_is_emoji(union ndb_reaction_str); + // STATS int ndb_stat(struct ndb *ndb, struct ndb_stat *stat); void ndb_stat_counts_init(struct ndb_stat_counts *counts); From 299e2265556f2abefed11e0d0aa2c54ab129a325 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 24 Oct 2025 11:31:41 -0700 Subject: [PATCH 06/12] test: add initial metadata tests Signed-off-by: William Casarin --- test.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test.c b/test.c index 91630eb6..c6c89984 100644 --- a/test.c +++ b/test.c @@ -51,6 +51,48 @@ static void print_search(struct ndb_txn *txn, struct ndb_search *search) printf("\n"); } +static void test_metadata() +{ + unsigned char buffer[1024]; + union ndb_reaction_str str; + struct ndb_note_meta_builder builder; + struct ndb_note_meta *meta; + struct ndb_note_meta_entry *entry = NULL; + int ok; + + ok = ndb_note_meta_builder_init(&builder, buffer, sizeof(buffer)); + assert(ok); + + entry = ndb_note_meta_add_entry(&builder); + assert(entry); + + ndb_reaction_set(&str, "🏴‍☠️"); + ndb_note_meta_reaction_set(entry, 1337, str); + + ndb_note_meta_build(&builder, &meta); + + assert(ndb_note_meta_entries_count(meta) == 1); + assert(ndb_note_meta_total_size(meta) == 32); + + entry = ndb_note_meta_entries(meta); + assert(ndb_note_meta_reaction_count(entry) == 1337); + + printf("ok test_metadata\n"); +} + +static void test_reaction_encoding() +{ + union ndb_reaction_str reaction; + assert(ndb_reaction_set(&reaction, "👩🏻‍🤝‍👩🏿")); + assert(reaction.binmoji == 0x07D1A7747240B0D0); + assert(ndb_reaction_str_is_emoji(reaction) == 1); + assert(ndb_reaction_set(&reaction, "hello")); + assert(ndb_reaction_str_is_emoji(reaction) == 0); + assert(!strcmp(reaction.packed.str, "hello")); + printf("ok test_reaction_encoding\n"); +} + + static void test_filters() { struct ndb_filter filter, *f; @@ -2083,6 +2125,8 @@ int main(int argc, const char *argv[]) { test_custom_filter(); delete_test_db(); + test_metadata(); + test_reaction_encoding(); test_note_relay_index(); test_filter_search(); test_filter_parse_search_json(); From 3eeabca9589d3d0e276737e9d4a815b86faf5922 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 24 Oct 2025 19:19:32 -0700 Subject: [PATCH 07/12] wip metadata migration Signed-off-by: William Casarin --- TODO | 12 ++++- src/nostrdb.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 143 insertions(+), 3 deletions(-) diff --git a/TODO b/TODO index c26cc436..98cf07f7 100644 --- a/TODO +++ b/TODO @@ -1,2 +1,10 @@ -implement NDB_WRITER_NOTE_META -test write metadata +test write +metadata +(B) migrate reply stats +metadata +(B) migrate quote stats +metadata +online quote stat calculation +metadata +online reply stats calculation +metadata +(B) migrate reaction stats +metadata +online reaction stats +metadata +test reply stats +metadata +test reaction stats +metadata +test quote stats +metadata diff --git a/src/nostrdb.c b/src/nostrdb.c index 27a1d038..68d37e50 100644 --- a/src/nostrdb.c +++ b/src/nostrdb.c @@ -1992,10 +1992,141 @@ static int ndb_rebuild_note_indices(struct ndb_txn *txn, enum ndb_dbs *indices, return count; } +int ndb_cursor_start(MDB_cursor *cur, MDB_val *k, MDB_val *v); + +/* count all of the quote reposts for a note id */ +static int ndb_count_quotes(struct ndb_txn *txn, const unsigned char *note_id, uint32_t *count) +{ + MDB_val k, v; + MDB_cursor *cur; + MDB_dbi db; + int rc; + unsigned char *keybuf; + char buffer[41]; /* 1 + 32 + 8 */ + + *count = 0; + db = txn->lmdb->dbs[NDB_DB_NOTE_TAGS]; + + /* we will iterate q tags for this particular id */ + if ((rc = mdb_cursor_open(txn->mdb_txn, db, &cur))) { + fprintf(stderr, "ndb_count_quotes: mdb_cursor_open failed, error %d\n", rc); + return 0; + } + + buffer[0] = 'q'; + memcpy(&buffer[1], note_id, 32); + memset(&buffer[33], 0x00, 8); + + k.mv_data = buffer; + k.mv_size = sizeof(buffer); + v.mv_data = NULL; + v.mv_size = 0; + + if (mdb_cursor_get(cur, &k, &v, MDB_SET_RANGE)) + goto cleanup; + + for (;;) { + keybuf = (unsigned char *)k.mv_data; + if (k.mv_size < sizeof(buffer)) + break; + if (keybuf[0] != 'q') + break; + if (memcmp(&keybuf[1], note_id, 32) != 0) + break; + (*count)++; + + if (mdb_cursor_get(cur, &k, &v, MDB_NEXT)) + break; + } + +cleanup: + mdb_cursor_close(cur); + return 1; +} + +/* count quotes and add them to a metadata builder. + * we assume there is no existing quotes entry */ +static int ndb_note_meta_builder_count_quotes(struct ndb_txn *txn, + unsigned char *note_id, + struct ndb_note_meta_builder *builder) +{ + uint32_t count; + struct ndb_note_meta_entry *entry; + + if (!ndb_count_quotes(txn, note_id, &count)) + return 0; + + if (count == 0) + return 1; + + if (!(entry = ndb_note_meta_add_entry(builder))) + return 0; + + ndb_note_meta_quotes_set(entry, count); + + return 1; +} + +/* count all of the reactions on a note and add it to metadata in progress */ +static void ndb_note_meta_builder_count_reactions(struct ndb_txn *txn, struct ndb_note_meta_builder *builder) +{ +} + // Migrations // +/* switch from flatbuffer stats to custom v2 */ +static int ndb_migrate_reaction_stats(struct ndb_txn *txn) +{ + MDB_val k, k2, v, v2; + MDB_cursor *cur; + MDB_dbi db; + unsigned char *id; + unsigned char buffer[4096]; + int rc; + struct ndb_note_meta_builder builder; + struct ndb_note_meta *meta; + + db = txn->lmdb->dbs[NDB_DB_META]; + + if ((rc = mdb_cursor_open(txn->mdb_txn, db, &cur))) { + fprintf(stderr, "ndb_migrate_reaction_stats: mdb_cursor_open failed, error %d\n", rc); + return -1; + } + + /* loop through every metadata entry */ + while (mdb_cursor_get(cur, &k, &v, MDB_NEXT) == 0) { + ndb_note_meta_builder_init(&builder, buffer, sizeof(buffer)); + + id = (unsigned char *)k.mv_data; + ndb_note_meta_builder_count_reactions(txn, &builder); + ndb_note_meta_builder_count_quotes(txn, id, &builder); + ndb_note_meta_build(&builder, &meta); + + /* no counts found, just delete this entry */ + if (ndb_note_meta_entries_count(meta) == 0) { + if ((rc = mdb_del(txn->mdb_txn, db, &k, &v))) { + ndb_debug("delete old metadata entry failed: %s\n", mdb_strerror(rc)); + return -1; + } + continue; + } + + k2.mv_data = (unsigned char *)id; + k2.mv_size = 32; + v2.mv_data = meta; + v2.mv_size = ndb_note_meta_total_size(meta); + + /* set entry */ + if ((rc = mdb_put(txn->mdb_txn, db, &k2, &v2, 0))) { + ndb_debug("migrate metadata entry failed on write: %s\n", mdb_strerror(rc)); + } + } + + return 1; +} + // This was before we had note_profile_pubkey{,_kind} indices. Let's create them. static int ndb_migrate_profile_indices(struct ndb_txn *txn) { @@ -2329,6 +2460,7 @@ static struct ndb_migration MIGRATIONS[] = { { .fn = ndb_migrate_lower_user_search_indices }, { .fn = ndb_migrate_utf8_profile_names }, { .fn = ndb_migrate_profile_indices }, + //{ .fn = ndb_migrate_reaction_stats }, }; @@ -2450,7 +2582,7 @@ int ndb_write_last_profile_fetch(struct ndb *ndb, const unsigned char *pubkey, // When doing cursor scans from greatest to lowest, this function positions the // cursor at the first element before descending. MDB_SET_RANGE puts us right // after the first element, so we have to go back one. -static int ndb_cursor_start(MDB_cursor *cur, MDB_val *k, MDB_val *v) +int ndb_cursor_start(MDB_cursor *cur, MDB_val *k, MDB_val *v) { int rc; // Position cursor at the next key greater than or equal to the From 121162c397dc852fbb9f2fb362721d0201140a32 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Wed, 29 Oct 2025 12:54:23 -0700 Subject: [PATCH 08/12] ndb: add q tags Signed-off-by: William Casarin --- ndb.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/ndb.c b/ndb.c index cea658f4..f58d30b0 100644 --- a/ndb.c +++ b/ndb.c @@ -323,7 +323,32 @@ int main(int argc, char *argv[]) argv += 2; argc -= 2; - } else if (!strcmp(argv[0], "-a") || !strcmp(argv[0], "--author")) { + } else if (!strcmp(argv[0], "-q")) { + if (current_field != 'q') { + if (!ndb_filter_start_tag_field(f, 'q')) { + fprintf(stderr, "field already started\n"); + res = 44; + goto cleanup; + } + } + current_field = 'q'; + + if (len != 64 || !hex_decode(argv[1], 64, tmp_id, sizeof(tmp_id))) { + fprintf(stderr, "invalid hex id\n"); + res = 42; + goto cleanup; + } + + if (!ndb_filter_add_id_element(f, tmp_id)) { + fprintf(stderr, "too many event ids\n"); + res = 43; + goto cleanup; + } + + argv += 2; + argc -= 2; + } + else if (!strcmp(argv[0], "-a") || !strcmp(argv[0], "--author")) { if (current_field != NDB_FILTER_AUTHORS) { ndb_filter_end_field(f); ndb_filter_start_field(f, NDB_FILTER_AUTHORS); From e90d933c6730b37bf420f1a57344a6cdc15ff03b Mon Sep 17 00:00:00 2001 From: William Casarin Date: Wed, 29 Oct 2025 12:55:22 -0700 Subject: [PATCH 09/12] metadata: add online metadata counts Add online counting for: - quotes - thread replies - direct replies - total reactions only for kind1 and longform for now Signed-off-by: William Casarin --- TODO | 6 - src/metadata.c | 219 ++++++++++++----- src/metadata.h | 82 ++++++ src/nostrdb.c | 506 +++++++++++++++++++++++++++++++++----- src/nostrdb.h | 18 +- test.c | 133 ++++++++-- testdata/reactions.json | 3 - testdata/test_counts.json | 202 +++++++++++++++ 8 files changed, 1007 insertions(+), 162 deletions(-) create mode 100644 src/metadata.h create mode 100644 testdata/test_counts.json diff --git a/TODO b/TODO index 98cf07f7..7fe226e2 100644 --- a/TODO +++ b/TODO @@ -1,10 +1,4 @@ test write +metadata (B) migrate reply stats +metadata (B) migrate quote stats +metadata -online quote stat calculation +metadata -online reply stats calculation +metadata (B) migrate reaction stats +metadata -online reaction stats +metadata -test reply stats +metadata -test reaction stats +metadata -test quote stats +metadata diff --git a/src/metadata.c b/src/metadata.c index 93be0f4e..6794e166 100644 --- a/src/metadata.c +++ b/src/metadata.c @@ -1,55 +1,7 @@ #include "nostrdb.h" #include "binmoji.h" - -// these must be byte-aligned, they are directly accessing the serialized data -// representation -#pragma pack(push, 1) - -// 16 bytes -struct ndb_note_meta_entry { - // 4 byte entry header - uint16_t type; - uint16_t flags; - - // additional 4 bytes of aux storage for payloads that are >8 bytes - // - // for reactions types, this is used for counts - // normally this would have been padding but we make use of it - // in our manually packed structure - uint32_t aux; - - // 8 byte metadata payload - union { - uint64_t value; - - struct { - uint32_t offset; - uint32_t padding; - } offset; - - // the reaction binmoji[1] for reaction, count is stored in aux - union ndb_reaction_str reaction_str; - } payload; -}; -STATIC_ASSERT(sizeof(struct ndb_note_meta_entry) == 16, note_meta_entry_should_be_16_bytes); - -/* newtype wrapper around the header entry */ -struct ndb_note_meta { - // 4 bytes - uint8_t version; - uint8_t padding; - uint16_t count; - - // 4 bytes - uint32_t data_table_size; - - // 8 bytes - uint64_t flags; -}; -STATIC_ASSERT(sizeof(struct ndb_note_meta) == 16, note_meta_entry_should_be_16_bytes); - -#pragma pack(pop) +#include "metadata.h" int ndb_reaction_str_is_emoji(union ndb_reaction_str str) { @@ -96,6 +48,19 @@ static int ndb_reaction_set_str(union ndb_reaction_str *reaction, const char *st return 0; } +const char *ndb_reaction_to_str(union ndb_reaction_str *str, char buf[128]) +{ + struct binmoji binmoji; + + if (ndb_reaction_str_is_emoji(*str)) { + binmoji_decode(str->binmoji, &binmoji); + binmoji_to_string(&binmoji, buf, 128); + return (const char *)buf; + } else { + return (const char *)str->packed.str; + } +} + /* set the value of an ndb_reaction_str to an emoji or small string */ int ndb_reaction_set(union ndb_reaction_str *reaction, const char *str) { @@ -215,9 +180,16 @@ static int compare_entries(const void *a, const void *b) struct ndb_note_meta_entry *ndb_note_meta_entries(struct ndb_note_meta *meta) { /* entries start at the end of the header record */ - return (struct ndb_note_meta_entry *)((uint8_t*)meta + sizeof(*meta)); + return (struct ndb_note_meta_entry *)((unsigned char*)meta + sizeof(*meta)); } +struct ndb_note_meta_entry *ndb_note_meta_entry_at(struct ndb_note_meta *meta, int i) +{ + if (i >= ndb_note_meta_entries_count(meta)) + return NULL; + + return &ndb_note_meta_entries(meta)[i]; +} void ndb_note_meta_build(struct ndb_note_meta_builder *builder, struct ndb_note_meta **meta) { /* sort entries */ @@ -227,11 +199,15 @@ void ndb_note_meta_build(struct ndb_note_meta_builder *builder, struct ndb_note_ /* not initialized */ assert(builder->cursor.start != builder->cursor.p); - if (header->count > 0) { + if (header->count > 1) { entries = ndb_note_meta_entries(header); + /*assert(entries);*/ /* ensure entries are always sorted so bsearch is possible for large metadata * entries. probably won't need that for awhile though */ + + /* this also ensures our counts entry is near the front, which will be a very + * hot and common entry to hit */ qsort(entries, header->count, sizeof(struct ndb_note_meta_entry), compare_entries); } @@ -239,6 +215,11 @@ void ndb_note_meta_build(struct ndb_note_meta_builder *builder, struct ndb_note_ return; } +uint16_t *ndb_note_meta_entry_type(struct ndb_note_meta_entry *entry) +{ + return &entry->type; +} + /* find a metadata entry, optionally matching a payload */ struct ndb_note_meta_entry *ndb_note_meta_find_entry(struct ndb_note_meta *meta, uint16_t type, uint64_t *payload) { @@ -249,12 +230,19 @@ struct ndb_note_meta_entry *ndb_note_meta_find_entry(struct ndb_note_meta *meta, return NULL; entries = ndb_note_meta_entries(meta); + assert(((intptr_t)entries - (intptr_t)meta) == 16); for (i = 0; i < meta->count; i++) { entry = &entries[i]; + assert(((uintptr_t)entry % 8) == 0); + /* + assert(entry->type < 100); + printf("finding %d/%d q:%d q:%"PRIx64" entry_type:%d entry:%"PRIx64"\n", + i+1, (int)meta->count, type, payload ? *payload : 0, entry->type, entry->payload.value); + */ if (entry->type != type) continue; - if (payload && *payload != entry->payload.value) + if (payload && (*payload != entry->payload.value)) continue; return entry; } @@ -266,28 +254,135 @@ void ndb_note_meta_reaction_set(struct ndb_note_meta_entry *entry, uint32_t coun { entry->type = NDB_NOTE_META_REACTION; entry->flags = 0; - entry->aux = count; + entry->aux.value = count; entry->payload.reaction_str = str; } /* sets the quote repost count for this note */ -void ndb_note_meta_quotes_set(struct ndb_note_meta_entry *entry, uint32_t count) +void ndb_note_meta_counts_set(struct ndb_note_meta_entry *entry, + uint32_t total_reactions, + uint16_t quotes, + uint16_t direct_replies, + uint32_t thread_replies) { - entry->type = NDB_NOTE_META_QUOTES; - entry->flags = 0; - entry->aux = count; - /* unused */ - entry->payload.value = 0; + entry->type = NDB_NOTE_META_COUNTS; + entry->aux.total_reactions = total_reactions; + entry->payload.counts.quotes = quotes; + entry->payload.counts.direct_replies = direct_replies; + entry->payload.counts.thread_replies = thread_replies; +} + +/* clones a metadata, either adding a new entry of a specific type, or returing + * a reference to it + * + * [in/out] meta: pointer to an existing meta entry, can but overwritten to + * [out] entry: pointer to the added entry + * + * */ +enum ndb_meta_clone_result ndb_note_meta_clone_with_entry( + struct ndb_note_meta **meta, + struct ndb_note_meta_entry **entry, + uint16_t type, + uint64_t *payload, + unsigned char *buf, + size_t bufsize) +{ + size_t size, offset; + struct ndb_note_meta_builder builder; + + if (*meta == NULL) { + ndb_note_meta_builder_init(&builder, buf, bufsize); + *entry = ndb_note_meta_add_entry(&builder); + *meta = (struct ndb_note_meta*)buf; + + assert(*entry); + + ndb_note_meta_build(&builder, meta); + return NDB_META_CLONE_NEW_ENTRY; + } else if ((size = ndb_note_meta_total_size(*meta)) > bufsize) { + ndb_debug("buf size too small (%d < %d) for metadata entry\n", bufsize, size); + goto fail; + } else if ((*entry = ndb_note_meta_find_entry(*meta, type, payload))) { + offset = (unsigned char *)(*entry) - (unsigned char *)(*meta); + + /* we have an existing entry. simply memcpy and return the new entry position */ + assert(offset < size); + assert((offset % 16) == 0); + assert(((uintptr_t)buf % 8) == 0); + + memcpy(buf, *meta, size); + *meta = (struct ndb_note_meta*)buf; + *entry = (struct ndb_note_meta_entry*)(((unsigned char *)(*meta)) + offset); + return NDB_META_CLONE_EXISTING_ENTRY; + } else if (size + sizeof(*entry) > bufsize) { + /* if we don't have an existing entry, make sure we have room to add one */ + + ndb_debug("note metadata is too big (%d > %d) to clone with entry\n", + (int)(len + sizeof(*entry)), (int)scratch_size); + /* no room. this is bad, if this happens we should fix it */ + goto fail; + } else { + /* we need to add a new entry */ + ndb_note_meta_builder_init(&builder, buf, bufsize); + + memcpy(buf, *meta, size); + builder.cursor.p = buf + size; + + *entry = ndb_note_meta_add_entry(&builder); + assert(*entry); + (*entry)->type = type; + (*entry)->payload.value = payload? *payload : 0; + + *meta = (struct ndb_note_meta*)buf; + + assert(*entry); + assert(*meta); + + ndb_note_meta_build(&builder, meta); + + /* we re-find here since it could have been sorted */ + *entry = ndb_note_meta_find_entry(*meta, type, payload); + assert(*entry); + assert(*ndb_note_meta_entry_type(*entry) == type); + + return NDB_META_CLONE_NEW_ENTRY; + } + + assert(!"should be impossible to get here"); +fail: + *entry = NULL; + *meta = NULL; + return 0; +} + +uint32_t *ndb_note_meta_reaction_count(struct ndb_note_meta_entry *entry) +{ + return &entry->aux.value; +} + +uint16_t *ndb_note_meta_counts_direct_replies(struct ndb_note_meta_entry *entry) +{ + return &entry->payload.counts.direct_replies; +} + +uint32_t *ndb_note_meta_counts_total_reactions(struct ndb_note_meta_entry *entry) +{ + return &entry->aux.total_reactions; +} + +uint32_t *ndb_note_meta_counts_thread_replies(struct ndb_note_meta_entry *entry) +{ + return &entry->payload.counts.thread_replies; } -uint32_t ndb_note_meta_reaction_count(struct ndb_note_meta_entry *entry) +uint16_t *ndb_note_meta_counts_quotes(struct ndb_note_meta_entry *entry) { - return entry->aux; + return &entry->payload.counts.quotes; } void ndb_note_meta_reaction_set_count(struct ndb_note_meta_entry *entry, uint32_t count) { - entry->aux = count; + entry->aux.value = count; } union ndb_reaction_str ndb_note_meta_reaction_str(struct ndb_note_meta_entry *entry) diff --git a/src/metadata.h b/src/metadata.h new file mode 100644 index 00000000..f708d1b4 --- /dev/null +++ b/src/metadata.h @@ -0,0 +1,82 @@ +#ifndef NDB_METADATA_H +#define NDB_METADATA_H + +#include "nostrdb.h" + +enum ndb_meta_clone_result { + NDB_META_CLONE_FAILED, + NDB_META_CLONE_EXISTING_ENTRY, + NDB_META_CLONE_NEW_ENTRY, +}; + +enum ndb_meta_clone_result ndb_note_meta_clone_with_entry( + struct ndb_note_meta **meta, + struct ndb_note_meta_entry **entry, + uint16_t type, + uint64_t *payload, + unsigned char *buf, + size_t bufsize); + +// these must be byte-aligned, they are directly accessing the serialized data +// representation +#pragma pack(push, 1) + +// 16 bytes +struct ndb_note_meta_entry { + // 4 byte entry header + uint16_t type; + uint16_t flags; + + // additional 4 bytes of aux storage for payloads that are >8 bytes + // + // for reactions types, this is used for counts + // normally this would have been padding but we make use of it + // in our manually packed structure + union { + uint32_t value; + + /* if this is a thread root, this counts the total replies in the thread */ + uint32_t total_reactions; + } aux; + + // 8 byte metadata payload + union { + uint64_t value; + + struct { + uint32_t offset; + uint32_t padding; + } offset; + + struct { + /* number of direct replies */ + uint16_t direct_replies; + uint16_t quotes; + + /* number of replies in this thread */ + uint32_t thread_replies; + } counts; + + // the reaction binmoji[1] for reaction, count is stored in aux + union ndb_reaction_str reaction_str; + } payload; +}; +STATIC_ASSERT(sizeof(struct ndb_note_meta_entry) == 16, note_meta_entry_should_be_16_bytes); + +struct ndb_note_meta { + // 4 bytes + uint8_t version; + uint8_t padding; + uint16_t count; + + // 4 bytes + uint32_t data_table_size; + + // 8 bytes + uint64_t flags; +}; +STATIC_ASSERT(sizeof(struct ndb_note_meta) == 16, note_meta_entry_should_be_16_bytes); + +#pragma pack(pop) + +#endif /* NDB_METADATA_H */ diff --git a/src/nostrdb.c b/src/nostrdb.c index 68d37e50..bbe8880f 100644 --- a/src/nostrdb.c +++ b/src/nostrdb.c @@ -8,6 +8,7 @@ #include "bolt11/bolt11.h" #include "bolt11/amount.h" #include "lmdb.h" +#include "metadata.h" #include "util.h" #include "cpu.h" #include "block.h" @@ -64,6 +65,13 @@ typedef int (*ndb_migrate_fn)(struct ndb_txn *); typedef int (*ndb_word_parser_fn)(void *, const char *word, int word_len, int word_index); +/* parsed nip10 reply data */ +struct ndb_note_reply { + unsigned char *root; + unsigned char *reply; + unsigned char *mention; +}; + // these must be byte-aligned, they are directly accessing the serialized data // representation #pragma pack(push, 1) @@ -1994,8 +2002,19 @@ static int ndb_rebuild_note_indices(struct ndb_txn *txn, enum ndb_dbs *indices, int ndb_cursor_start(MDB_cursor *cur, MDB_val *k, MDB_val *v); +static int ndb_count_replies(struct ndb_txn *txn, const unsigned char *note_id, uint16_t *direct_replies, uint32_t *thread_replies) +{ + return 1; +} + +/* count all of the reactions for a note */ +static int ndb_count_reactions(struct ndb_txn *txn, const unsigned char *note_id, uint32_t *count) +{ + return 1; +} + /* count all of the quote reposts for a note id */ -static int ndb_count_quotes(struct ndb_txn *txn, const unsigned char *note_id, uint32_t *count) +static int ndb_count_quotes(struct ndb_txn *txn, const unsigned char *note_id, uint16_t *count) { MDB_val k, v; MDB_cursor *cur; @@ -2046,23 +2065,35 @@ static int ndb_count_quotes(struct ndb_txn *txn, const unsigned char *note_id, u /* count quotes and add them to a metadata builder. * we assume there is no existing quotes entry */ -static int ndb_note_meta_builder_count_quotes(struct ndb_txn *txn, - unsigned char *note_id, - struct ndb_note_meta_builder *builder) +static int ndb_note_meta_builder_counts(struct ndb_txn *txn, + unsigned char *note_id, + struct ndb_note_meta_builder *builder) { - uint32_t count; + uint32_t thread_replies, total_reactions; + uint16_t direct_replies, quotes; struct ndb_note_meta_entry *entry; + int rcs[3]; - if (!ndb_count_quotes(txn, note_id, &count)) + quotes = 0; + direct_replies = 0; + thread_replies = 0; + total_reactions = 0; + + if (!(entry = ndb_note_meta_add_entry(builder))) return 0; - if (count == 0) - return 1; + rcs[0] = ndb_count_reactions(txn, note_id, &total_reactions); + rcs[1] = ndb_count_quotes(txn, note_id, "es); + rcs[2] = ndb_count_replies(txn, note_id, &direct_replies, &thread_replies); - if (!(entry = ndb_note_meta_add_entry(builder))) + if (!rcs[0] && !rcs[1] && !rcs[2]) return 0; - ndb_note_meta_quotes_set(entry, count); + /* no entry needed */ + if (quotes == 0 && direct_replies == 0 && thread_replies == 0 && quotes == 0) + return 1; + + ndb_note_meta_counts_set(entry, total_reactions, quotes, direct_replies, thread_replies); return 1; } @@ -2101,7 +2132,7 @@ static int ndb_migrate_reaction_stats(struct ndb_txn *txn) id = (unsigned char *)k.mv_data; ndb_note_meta_builder_count_reactions(txn, &builder); - ndb_note_meta_builder_count_quotes(txn, id, &builder); + ndb_note_meta_builder_counts(txn, id, &builder); ndb_note_meta_build(&builder, &meta); /* no counts found, just delete this entry */ @@ -3509,7 +3540,7 @@ int ndb_writer_set_note_meta(struct ndb_txn *txn, const unsigned char *id, struc return 1; } -void *ndb_get_note_meta(struct ndb_txn *txn, const unsigned char *id, size_t *len) +struct ndb_note_meta *ndb_get_note_meta(struct ndb_txn *txn, const unsigned char *id) { MDB_val k, v; @@ -3517,84 +3548,157 @@ void *ndb_get_note_meta(struct ndb_txn *txn, const unsigned char *id, size_t *le k.mv_size = 32; if (mdb_get(txn->mdb_txn, txn->lmdb->dbs[NDB_DB_META], &k, &v)) { - //ndb_debug("ndb_get_note_meta: mdb_get note failed\n"); + ndb_debug("ndb_get_note_meta: mdb_get note failed\n"); return NULL; } - if (len) - *len = v.mv_size; - return v.mv_data; } -// When receiving a reaction note, look for the liked id and increase the -// reaction counter in the note metadata database -static int ndb_write_reaction_stats(struct ndb_txn *txn, struct ndb_note *note) +static void print_meta_data(struct ndb_note_meta *meta) { - size_t len; - void *root; - int reactions, rc; + struct ndb_note_meta_entry *entry; + int i; + + printf("\n", + meta->version, meta->count, meta->data_table_size, meta->flags); + + for (i = 0; i < meta->count; i++) { + entry = ndb_note_meta_entry_at(meta, i); + printf("type, entry->flags, entry->aux.value, entry->payload.value); + } +} + +/* write reaction stats if its a valid reaction */ +static int ndb_process_reaction( + struct ndb_txn *txn, + struct ndb_note *note, + unsigned char **liked, + unsigned char *scratch, + size_t scratch_size) +{ + const char *content; + int rc; + uint32_t *count; MDB_val key, val; - NdbEventMeta_table_t meta; - unsigned char *liked = ndb_note_last_id_tag(note, 'e'); + union ndb_reaction_str reaction_str; + struct ndb_note_meta *meta; + struct ndb_note_meta_entry *entry; + enum ndb_meta_clone_result cres; + char strbuf[128]; + + *liked = ndb_note_last_id_tag(note, 'e'); - if (liked == NULL) + if (*liked == NULL) return 0; - root = ndb_get_note_meta(txn, liked, &len); + meta = ndb_get_note_meta(txn, *liked); + + /* initial builder setup, build reaction string from reaction contents */ + content = ndb_note_content(note); + if (!ndb_reaction_set(&reaction_str, content)) { + ndb_debug("reaction string '%s' was too big\n", content); + /* string was too big, let's just record a `+` for now */ + rc = ndb_reaction_set(&reaction_str, "+"); + assert(rc); + } + + cres = ndb_note_meta_clone_with_entry(&meta, + &entry, + NDB_NOTE_META_REACTION, + &reaction_str.binmoji, + scratch, + scratch_size); + + switch (cres) { + case NDB_META_CLONE_FAILED: + return 0; + case NDB_META_CLONE_NEW_ENTRY: + ndb_reaction_to_str(&reaction_str, strbuf); + /* printf("initializing reaction stats for %s\n", strbuf); */ + ndb_note_meta_reaction_set(entry, 1, reaction_str); + break; + case NDB_META_CLONE_EXISTING_ENTRY: + count = ndb_note_meta_reaction_count(entry); + /* printf("increasing count from %d to %d\n", (int)*count, (int)*count+1); */ + (*count)++; + break; + } - flatcc_builder_t builder; - flatcc_builder_init(&builder); - NdbEventMeta_start_as_root(&builder); + key.mv_data = *liked; + key.mv_size = 32; - // no meta record, let's make one - if (root == NULL) { - NdbEventMeta_reactions_add(&builder, 1); - } else { - // clone existing and add to it - meta = NdbEventMeta_as_root(root); + val.mv_data = meta; + val.mv_size = ndb_note_meta_total_size(meta); + assert((val.mv_size % 8) == 0); - reactions = NdbEventMeta_reactions_get(meta); - NdbEventMeta_clone(&builder, meta); - NdbEventMeta_reactions_add(&builder, reactions + 1); + if ((rc = mdb_put(txn->mdb_txn, txn->lmdb->dbs[NDB_DB_META], &key, &val, 0))) { + ndb_debug("write reaction stats to db failed: %s\n", mdb_strerror(rc)); + return 0; } - NdbProfileRecord_end_as_root(&builder); - root = flatcc_builder_finalize_aligned_buffer(&builder, &len); - assert(((uint64_t)root % 8) == 0); + return 1; +} - if (root == NULL) { - ndb_debug("failed to create note metadata record\n"); - goto fail; +static int ndb_increment_total_reactions( + struct ndb_txn *txn, + unsigned char *liked, + unsigned char *scratch, + size_t scratch_size) +{ + MDB_val key, val; + uint32_t *total_reactions; + struct ndb_note_meta *meta; + struct ndb_note_meta_entry *entry; + int rc; + + meta = ndb_get_note_meta(txn, liked); + rc = ndb_note_meta_clone_with_entry(&meta, &entry, + NDB_NOTE_META_COUNTS, + NULL, /* payload to match. only relevant for reactions */ + scratch, + scratch_size); + + switch (rc) { + case NDB_META_CLONE_FAILED: + return 0; + case NDB_META_CLONE_NEW_ENTRY: + ndb_note_meta_counts_set(entry, 1, 0, 0, 0); + break; + case NDB_META_CLONE_EXISTING_ENTRY: + total_reactions = ndb_note_meta_counts_total_reactions(entry); + (*total_reactions)++; + break; } - // metadata is keyed on id because we want to collect stats regardless - // if we have the note yet or not key.mv_data = liked; key.mv_size = 32; - val.mv_data = root; - val.mv_size = len; - - // write the new meta record - //ndb_debug("writing stats record for "); - //print_hex(liked, 32); - //ndb_debug("\n"); + val.mv_data = meta; + val.mv_size = ndb_note_meta_total_size(meta); + assert((val.mv_size % 8) == 0); if ((rc = mdb_put(txn->mdb_txn, txn->lmdb->dbs[NDB_DB_META], &key, &val, 0))) { ndb_debug("write reaction stats to db failed: %s\n", mdb_strerror(rc)); - goto fail; + return 0; } - free(root); - flatcc_builder_clear(&builder); - return 1; +} -fail: - free(root); - flatcc_builder_clear(&builder); - return 0; + +// When receiving a reaction note, look for the liked id and increase the +// reaction counter in the note metadata database +static int ndb_write_reaction_stats(struct ndb_txn *txn, struct ndb_note *note, + unsigned char *scratch, + size_t scratch_size) +{ + unsigned char *liked; + /* we short circuit here since we only want to increment total reaction count + * if its a valid reaction */ + return ndb_process_reaction(txn, note, &liked, scratch, scratch_size) && + ndb_increment_total_reactions(txn, liked, scratch, scratch_size); } @@ -5448,6 +5552,282 @@ static int ndb_write_new_blocks(struct ndb_txn *txn, struct ndb_note *note, return 1; } + +// find the last id tag in a note (e, p, etc) +static unsigned char *ndb_note_first_tag_id(struct ndb_note *note, char tag) +{ + struct ndb_iterator iter; + struct ndb_str str; + + // get the liked event id (last id) + ndb_tags_iterate_start(note, &iter); + + while (ndb_tags_iterate_next(&iter)) { + if (iter.tag->count < 2) + continue; + + str = ndb_tag_str(note, iter.tag, 0); + + // assign liked to the last e tag + if (str.flag == NDB_PACKED_STR && str.str[0] == tag) { + str = ndb_tag_str(note, iter.tag, 1); + if (str.flag == NDB_PACKED_ID) + return str.id; + } + } + + return NULL; +} + +/* get reply information from a note */ +static void ndb_parse_reply(struct ndb_note *note, struct ndb_note_reply *note_reply) +{ + unsigned char *root, *reply, *mention, *id; + const char *marker; + struct ndb_iterator iter; + struct ndb_str str; + uint16_t count; + int any_marker, first; + + any_marker = 0; + first = 1; + root = NULL; + reply = NULL; + mention = NULL; + + // get the liked event id (last id) + ndb_tags_iterate_start(note, &iter); + while (ndb_tags_iterate_next(&iter)) { + if (root && reply && mention) + break; + + marker = NULL; + count = ndb_tag_count(iter.tag); + + if (count < 2) + continue; + + str = ndb_tag_str(note, iter.tag, 0); + if (!(str.flag == NDB_PACKED_STR && str.str[0] == 'e')) + continue; + + str = ndb_tag_str(note, iter.tag, 1); + if (str.flag != NDB_PACKED_ID) + continue; + id = str.id; + + /* if we have the marker, assign it */ + if (count >= 4) { + str = ndb_tag_str(note, iter.tag, 3); + if (str.flag == NDB_PACKED_STR) + marker = str.str; + } + + if (marker) { + any_marker = true; + if (!strcmp(marker, "root")) + root = id; + else if (!strcmp(marker, "reply")) + reply = id; + else if (!strcmp(marker, "mention")) + mention = id; + } else if (!any_marker && first) { + root = id; + first = 0; + } else if (!any_marker && !reply) { + reply = id; + } + } + + note_reply->reply = reply; + note_reply->root = root; + note_reply->mention = mention; +} + +static int ndb_is_reply_to_root(struct ndb_note_reply *reply) +{ + if (reply->root && !reply->reply) + return 0; + else if (reply->root && reply->reply) + return !memcmp(reply->root, reply->reply, 32); + else + return 0; +} + +static int ndb_increment_quote_metadata( + struct ndb_txn *txn, + unsigned char *quoted_note_id, + unsigned char *scratch, + size_t scratch_size) +{ + MDB_val key, val; + uint16_t *quotes; + struct ndb_note_meta *meta; + struct ndb_note_meta_entry *entry; + int rc; + + meta = ndb_get_note_meta(txn, quoted_note_id); + rc = ndb_note_meta_clone_with_entry(&meta, &entry, + NDB_NOTE_META_COUNTS, + NULL, /* payload to match. only relevant for reactions */ + scratch, + scratch_size); + + switch (rc) { + case NDB_META_CLONE_FAILED: + return 0; + case NDB_META_CLONE_NEW_ENTRY: + ndb_note_meta_counts_set(entry, 0, 1, 0, 0); + break; + case NDB_META_CLONE_EXISTING_ENTRY: + quotes = ndb_note_meta_counts_quotes(entry); + (*quotes)++; + break; + } + + key.mv_data = quoted_note_id; + key.mv_size = 32; + + val.mv_data = meta; + val.mv_size = ndb_note_meta_total_size(meta); + assert((val.mv_size % 8) == 0); + + if ((rc = mdb_put(txn->mdb_txn, txn->lmdb->dbs[NDB_DB_META], &key, &val, 0))) { + ndb_debug("write reaction stats to db failed: %s\n", mdb_strerror(rc)); + return 0; + } + + return 1; +} + +/* update reply count metadata for a specific note id */ +static int ndb_increment_direct_reply_metadata( + struct ndb_txn *txn, + unsigned char *id, + unsigned char *scratch, + size_t scratch_size) +{ + MDB_val key, val; + uint16_t *direct_replies; + struct ndb_note_meta *meta; + struct ndb_note_meta_entry *entry; + int rc; + + meta = ndb_get_note_meta(txn, id); + rc = ndb_note_meta_clone_with_entry(&meta, &entry, + NDB_NOTE_META_COUNTS, + NULL, /* payload to match. only relevant for reactions */ + scratch, + scratch_size); + + switch (rc) { + case NDB_META_CLONE_FAILED: + return 0; + case NDB_META_CLONE_NEW_ENTRY: + ndb_note_meta_counts_set(entry, 0, 0, 1, 0); + break; + case NDB_META_CLONE_EXISTING_ENTRY: + direct_replies = ndb_note_meta_counts_direct_replies(entry); + (*direct_replies)++; + break; + } + + key.mv_data = id; + key.mv_size = 32; + + val.mv_data = meta; + val.mv_size = ndb_note_meta_total_size(meta); + assert((val.mv_size % 8) == 0); + + if ((rc = mdb_put(txn->mdb_txn, txn->lmdb->dbs[NDB_DB_META], &key, &val, 0))) { + ndb_debug("write reaction stats to db failed: %s\n", mdb_strerror(rc)); + return 0; + } + + return 1; +} + +/* update reply count metadata for a specific note id */ +static int ndb_increment_thread_reply_metadata( + struct ndb_txn *txn, + unsigned char *id, + unsigned char *scratch, + size_t scratch_size) +{ + MDB_val key, val; + uint32_t *replies; + struct ndb_note_meta *meta; + struct ndb_note_meta_entry *entry; + int rc; + + meta = ndb_get_note_meta(txn, id); + rc = ndb_note_meta_clone_with_entry(&meta, &entry, + NDB_NOTE_META_COUNTS, + NULL, /* payload to match. only relevant for reactions */ + scratch, + scratch_size); + + switch (rc) { + case NDB_META_CLONE_FAILED: + return 0; + case NDB_META_CLONE_NEW_ENTRY: + ndb_note_meta_counts_set(entry, 0, 0, 0, 1); + break; + case NDB_META_CLONE_EXISTING_ENTRY: + replies = ndb_note_meta_counts_thread_replies(entry); + (*replies)++; + break; + } + + key.mv_data = id; + key.mv_size = 32; + + val.mv_data = meta; + val.mv_size = ndb_note_meta_total_size(meta); + assert((val.mv_size % 8) == 0); + + if ((rc = mdb_put(txn->mdb_txn, txn->lmdb->dbs[NDB_DB_META], &key, &val, 0))) { + ndb_debug("write reaction stats to db failed: %s\n", mdb_strerror(rc)); + return 0; + } + + return 1; +} + + +/* process quote and reply count metadata */ +static void ndb_process_note_stats( + struct ndb_txn *txn, + struct ndb_note *note, + unsigned char *scratch, + size_t scratch_size) +{ + unsigned char *quoted_note_id, *reply_id; + struct ndb_note_reply reply; + + reply_id = NULL; + + /* find q tag to see if we are quoting anything */ + if ((quoted_note_id = ndb_note_first_tag_id(note, 'q'))) { + ndb_increment_quote_metadata(txn, quoted_note_id, scratch, scratch_size); + } + + ndb_parse_reply(note, &reply); + if (ndb_is_reply_to_root(&reply)) { + reply_id = reply.root; + } else { + reply_id = reply.reply; + } + + if (reply_id) { + ndb_increment_direct_reply_metadata(txn, reply_id, scratch, scratch_size); + } + + if (reply.root) { + ndb_increment_thread_reply_metadata(txn, reply.root, scratch, scratch_size); + } +} + static uint64_t ndb_write_note(struct ndb_txn *txn, struct ndb_writer_note *note, unsigned char *scratch, size_t scratch_size, @@ -5505,8 +5885,10 @@ static uint64_t ndb_write_note(struct ndb_txn *txn, if (!ndb_flag_set(ndb_flags, NDB_FLAG_NO_NOTE_BLOCKS)) { ndb_write_new_blocks(txn, note->note, note_key, scratch, scratch_size); } + + ndb_process_note_stats(txn, note->note, scratch, scratch_size); } else if (kind == 7 && !ndb_flag_set(ndb_flags, NDB_FLAG_NO_STATS)) { - ndb_write_reaction_stats(txn, note->note); + ndb_write_reaction_stats(txn, note->note, scratch, scratch_size); } return note_key; diff --git a/src/nostrdb.h b/src/nostrdb.h index 05691c2a..86a7df28 100644 --- a/src/nostrdb.h +++ b/src/nostrdb.h @@ -49,8 +49,8 @@ struct bolt11; */ enum ndb_metadata_type { NDB_NOTE_META_RESERVED = 0, /* not used */ - NDB_NOTE_META_REACTION = 2, /* count of all the reactions on a post, grouped by different reaction strings */ - NDB_NOTE_META_QUOTES = 4, /* count of all the all the notes with q tag references to this note */ + NDB_NOTE_META_COUNTS = 2, /* replies, quotes, etc */ + NDB_NOTE_META_REACTION = 4, /* count of all the reactions on a post, grouped by different reaction strings */ }; // some bindings like swift needs help with forward declared pointers @@ -654,7 +654,7 @@ void ndb_text_search_config_set_limit(struct ndb_text_search_config *, int limit int ndb_query(struct ndb_txn *txn, struct ndb_filter *filters, int num_filters, struct ndb_query_result *results, int result_capacity, int *count); // NOTE METADATA -void *ndb_get_note_meta(struct ndb_txn *txn, const unsigned char *id, size_t *len); +struct ndb_note_meta *ndb_get_note_meta(struct ndb_txn *txn, const unsigned char *id); int ndb_set_note_meta(struct ndb *ndb, const unsigned char *id, struct ndb_note_meta *meta); size_t ndb_note_meta_total_size(struct ndb_note_meta *header); int ndb_note_meta_builder_init(struct ndb_note_meta_builder *builder, unsigned char *, size_t); @@ -664,12 +664,20 @@ struct ndb_note_meta_entry *ndb_note_meta_entries(struct ndb_note_meta *meta); struct ndb_note_meta_entry *ndb_note_meta_add_entry(struct ndb_note_meta_builder *builder); size_t ndb_note_meta_total_size(struct ndb_note_meta *meta); void ndb_note_meta_reaction_set(struct ndb_note_meta_entry *entry, uint32_t count, union ndb_reaction_str str); -void ndb_note_meta_quotes_set(struct ndb_note_meta_entry *entry, uint32_t count); -uint32_t ndb_note_meta_reaction_count(struct ndb_note_meta_entry *entry); +void ndb_note_meta_counts_set(struct ndb_note_meta_entry *entry, uint32_t total_reactions, uint16_t quotes, uint16_t direct_replies, uint32_t thread_replies); +uint32_t *ndb_note_meta_reaction_count(struct ndb_note_meta_entry *entry); +struct ndb_note_meta_entry *ndb_note_meta_find_entry(struct ndb_note_meta *meta, uint16_t type, uint64_t *payload); +uint16_t *ndb_note_meta_counts_quotes(struct ndb_note_meta_entry *entry); +uint32_t *ndb_note_meta_counts_total_reactions(struct ndb_note_meta_entry *entry); +uint16_t *ndb_note_meta_counts_direct_replies(struct ndb_note_meta_entry *entry); +uint32_t *ndb_note_meta_counts_thread_replies(struct ndb_note_meta_entry *entry); +uint16_t *ndb_note_meta_entry_type(struct ndb_note_meta_entry *entry); +struct ndb_note_meta_entry *ndb_note_meta_entry_at(struct ndb_note_meta *meta, int ind); // META STRINGS int ndb_reaction_set(union ndb_reaction_str *reaction, const char *str); int ndb_reaction_str_is_emoji(union ndb_reaction_str); +const char *ndb_reaction_to_str(union ndb_reaction_str *str, char buf[128]); // STATS int ndb_stat(struct ndb *ndb, struct ndb_stat *stat); diff --git a/test.c b/test.c index c6c89984..7a52b2d1 100644 --- a/test.c +++ b/test.c @@ -16,6 +16,7 @@ #include #include #include +#include #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) @@ -29,6 +30,22 @@ static void delete_test_db() { unlink(TEST_DIR "/data.lock"); } +static void db_load_events(struct ndb *ndb, const char *filename) +{ + size_t filesize; + int written; + char *json; + struct stat st; + + stat(filename, &st); + filesize = st.st_size; + + json = malloc(filesize + 1); // +1 for '\0' if you need it null-terminated + read_file(filename, (unsigned char*)json, filesize, &written); + assert(ndb_process_client_events(ndb, json, written)); + free(json); +} + static NdbProfile_table_t lookup_profile(struct ndb_txn *txn, uint64_t pk) { void *root; @@ -51,6 +68,77 @@ static void print_search(struct ndb_txn *txn, struct ndb_search *search) printf("\n"); } +static void test_count_metadata() +{ + struct ndb *ndb; + struct ndb_config config; + struct ndb_txn txn; + struct ndb_note_meta *meta; + //struct ndb_note_meta_entry *counts; + struct ndb_note_meta_entry *entry; + uint16_t count; + uint32_t total_reactions, reactions, replies; + int i; + + reactions = 0; + delete_test_db(); + + ndb_default_config(&config); + assert(ndb_init(&ndb, test_dir, &config)); + + const unsigned char id[] = { + 0xd4, 0x4a, 0xd9, 0x6c, 0xb8, 0x92, 0x40, 0x92, 0xa7, 0x6b, 0xc2, 0xaf, + 0xdd, 0xeb, 0x12, 0xeb, 0x85, 0x23, 0x3c, 0x0d, 0x03, 0xa7, 0xd9, 0xad, + 0xc4, 0x2c, 0x2a, 0x85, 0xa7, 0x9a, 0x43, 0x05 + }; + + db_load_events(ndb, "testdata/test_counts.json"); + + /* consume all events to ensure we're done processing */ + ndb_destroy(ndb); + ndb_init(&ndb, test_dir, &config); + + ndb_begin_query(ndb, &txn); + meta = ndb_get_note_meta(&txn, id); + assert(meta); + + count = ndb_note_meta_entries_count(meta); + entry = ndb_note_meta_entries(meta); + for (i = 0; i < count; i++) { + entry = ndb_note_meta_entry_at(meta, i); + if (*ndb_note_meta_entry_type(entry) == NDB_NOTE_META_REACTION) { + reactions += *ndb_note_meta_reaction_count(entry); + } + } + + entry = ndb_note_meta_find_entry(meta, NDB_NOTE_META_COUNTS, NULL); + + assert(entry); + assert(*ndb_note_meta_counts_quotes(entry) == 2); + + replies = *ndb_note_meta_counts_thread_replies(entry); + printf("\t# thread replies %d\n", replies); + assert(replies == 93); + + replies = *ndb_note_meta_counts_direct_replies(entry); + printf("\t# direct replies %d\n", replies); + assert(replies == 14); + + total_reactions = *ndb_note_meta_counts_total_reactions(entry); + printf("\t# total reactions %d\n", reactions); + assert(total_reactions > 0); + + printf("\t# reactions %d\n", reactions); + assert(reactions > 0); + assert(total_reactions == reactions); + + ndb_end_query(&txn); + ndb_destroy(ndb); + delete_test_db(); + + printf("ok test_count_metadata\n"); +} + static void test_metadata() { unsigned char buffer[1024]; @@ -75,7 +163,7 @@ static void test_metadata() assert(ndb_note_meta_total_size(meta) == 32); entry = ndb_note_meta_entries(meta); - assert(ndb_note_meta_reaction_count(entry) == 1337); + assert(*ndb_note_meta_reaction_count(entry) == 1337); printf("ok test_metadata\n"); } @@ -347,26 +435,21 @@ static void test_fetched_at() static void test_reaction_counter() { - static const int alloc_size = 1024 * 1024; - char *json = malloc(alloc_size); struct ndb *ndb; - size_t len; - void *root; - int written, reactions, results; - NdbEventMeta_table_t meta; + int reactions, results; struct ndb_txn txn; + struct ndb_note_meta_entry *entry; + struct ndb_note_meta *meta; struct ndb_config config; ndb_default_config(&config); static const int num_reactions = 3; uint64_t note_ids[num_reactions], subid; + union ndb_reaction_str str; assert(ndb_init(&ndb, test_dir, &config)); - - read_file("testdata/reactions.json", (unsigned char*)json, alloc_size, &written); - assert((subid = ndb_subscribe(ndb, NULL, 0))); - assert(ndb_process_client_events(ndb, json, written)); + db_load_events(ndb, "testdata/reactions.json"); for (reactions = 0; reactions < num_reactions;) { results = ndb_wait_for_notes(ndb, subid, note_ids, num_reactions); @@ -382,16 +465,19 @@ static void test_reaction_counter() 0x18, 0x76, 0xeb, 0x0f, 0x62, 0x2c, 0x68, 0xe8 }; - assert((root = ndb_get_note_meta(&txn, id, &len))); - assert(0 == NdbEventMeta_verify_as_root(root, len)); - assert((meta = NdbEventMeta_as_root(root))); - - reactions = NdbEventMeta_reactions_get(meta); - //printf("counted reactions: %d\n", reactions); - assert(reactions == 2); + assert((meta = ndb_get_note_meta(&txn, id))); + ndb_reaction_set(&str, "+"); + entry = ndb_note_meta_find_entry(meta, NDB_NOTE_META_REACTION, &str.binmoji); + assert(entry); + //printf("+ count %d\n", *ndb_note_meta_reaction_count(entry)); + assert(*ndb_note_meta_reaction_count(entry) == 1); + ndb_reaction_set(&str, "-"); + entry = ndb_note_meta_find_entry(meta, NDB_NOTE_META_REACTION, &str.binmoji); + assert(entry); + assert(*ndb_note_meta_reaction_count(entry) == 1); ndb_end_query(&txn); ndb_destroy(ndb); - free(json); + delete_test_db(); } static void test_profile_search(struct ndb *ndb) @@ -2070,6 +2156,7 @@ static void test_custom_filter() ndb_filter_destroy(f); ndb_filter_destroy(f2); ndb_destroy(ndb); + delete_test_db(); printf("ok test_custom_filter\n"); } @@ -2114,19 +2201,18 @@ void test_replay_attack() { ndb_filter_destroy(f); ndb_destroy(ndb); + delete_test_db(); } int main(int argc, const char *argv[]) { delete_test_db(); test_replay_attack(); - delete_test_db(); - test_custom_filter(); - delete_test_db(); - test_metadata(); + test_count_metadata(); test_reaction_encoding(); + test_reaction_counter(); test_note_relay_index(); test_filter_search(); test_filter_parse_search_json(); @@ -2151,7 +2237,6 @@ int main(int argc, const char *argv[]) { //test_migrate(); test_fetched_at(); test_profile_updates(); - test_reaction_counter(); test_load_profiles(); test_nip50_profile_search(); test_basic_event(); diff --git a/testdata/reactions.json b/testdata/reactions.json index 7514fc30..2d8a05d3 100644 --- a/testdata/reactions.json +++ b/testdata/reactions.json @@ -1,6 +1,3 @@ ["EVENT", {"id": "1a4156303109bb4a660a6a9004b0cdce8d83c3991de7864f1876eb0f622c68e8","pubkey": "c511ed64e93f3aa053f85c82ee5f1ef9be6b61254606b88b8656f47091dd6e52","created_at": 1696738537,"kind": 1,"tags": [],"content": "hello!","sig": "8f215d3a1673c7e857fa3a23b4e9c93c0770e03c2c90dc2d0f00ed29ed7e0b54f99eacbdd2abb5ade3c98e13f2218f746b51d88df7617552f59f276e50ba020a"}] ["EVENT", {"id": "028a90d81a1379ec07141e4cef36f0c993140c807f8bc179bea213c80ef8f807","pubkey": "5b1affd872a5b42fa613e96d7a898211bd8a284e246e1f06713b8ecc1d123d83","created_at": 1696738664,"kind": 7,"tags": [["e","2a4156303109bb4a660a6a9004b0cdce8d83c3991de7864f1876eb0f622c68e9"],["p","c511ed64e93f3aa053f85c82ee5f1ef9be6b61254606b88b8656f47091dd6e52"],["e","1a4156303109bb4a660a6a9004b0cdce8d83c3991de7864f1876eb0f622c68e8"]],"content": "+","sig": "1556911a9208f315d63deb994dcdbc9e73ad5cbb7c357d43e35946610f413c9d83c6304af624bb8c71cd254c2a89ccdca1fe74d4b1f8dbab69a9e2805a0738e8"}] -["EVENT", {"id": "028a90d81a1379ec07141e4cef36f0c993140c807f8bc179bea213c80ef8f807","pubkey": "5b1affd872a5b42fa613e96d7a898211bd8a284e246e1f06713b8ecc1d123d83","created_at": 1696738664,"kind": 7,"tags": [["e","2a4156303109bb4a660a6a9004b0cdce8d83c3991de7864f1876eb0f622c68e9"],["p","c511ed64e93f3aa053f85c82ee5f1ef9be6b61254606b88b8656f47091dd6e52"],["e","1a4156303109bb4a660a6a9004b0cdce8d83c3991de7864f1876eb0f622c68e8"]],"content": "+","sig": "1556911a9208f315d63deb994dcdbc9e73ad5cbb7c357d43e35946610f413c9d83c6304af624bb8c71cd254c2a89ccdca1fe74d4b1f8dbab69a9e2805a0738e8"}] -["EVENT", {"id": "028a90d81a1379ec07141e4cef36f0c993140c807f8bc179bea213c80ef8f807","pubkey": "5b1affd872a5b42fa613e96d7a898211bd8a284e246e1f06713b8ecc1d123d83","created_at": 1696738664,"kind": 7,"tags": [["e","2a4156303109bb4a660a6a9004b0cdce8d83c3991de7864f1876eb0f622c68e9"],["p","c511ed64e93f3aa053f85c82ee5f1ef9be6b61254606b88b8656f47091dd6e52"],["e","1a4156303109bb4a660a6a9004b0cdce8d83c3991de7864f1876eb0f622c68e8"]],"content": "+","sig": "1556911a9208f315d63deb994dcdbc9e73ad5cbb7c357d43e35946610f413c9d83c6304af624bb8c71cd254c2a89ccdca1fe74d4b1f8dbab69a9e2805a0738e8"}] -["EVENT", {"id": "028a90d81a1379ec07141e4cef36f0c993140c807f8bc179bea213c80ef8f807","pubkey": "5b1affd872a5b42fa613e96d7a898211bd8a284e246e1f06713b8ecc1d123d83","created_at": 1696738664,"kind": 7,"tags": [["e","2a4156303109bb4a660a6a9004b0cdce8d83c3991de7864f1876eb0f622c68e9"],["p","c511ed64e93f3aa053f85c82ee5f1ef9be6b61254606b88b8656f47091dd6e52"],["e","1a4156303109bb4a660a6a9004b0cdce8d83c3991de7864f1876eb0f622c68e8"]],"content": "+","sig": "1556911a9208f315d63deb994dcdbc9e73ad5cbb7c357d43e35946610f413c9d83c6304af624bb8c71cd254c2a89ccdca1fe74d4b1f8dbab69a9e2805a0738e8"}] ["EVENT", {"id": "9c350d1f3822be358abbd5654721bcf45e5919c95a3835517a9290c45b5278ab","pubkey": "0e78f7d0620f726f41eb1a1e97df02312710fcf59acf6c303bfe2e00fe50be0e","created_at": 1696738688,"kind": 7,"tags": [["p","c511ed64e93f3aa053f85c82ee5f1ef9be6b61254606b88b8656f47091dd6e52"],["e","1a4156303109bb4a660a6a9004b0cdce8d83c3991de7864f1876eb0f622c68e8"]],"content": "-","sig": "08e72aa3d2f1d6a2b7cb18c4ddff06e46e895670d1c9821c1b0b1ba3d53dca9de07cfc1829b6c3bc5e8d733b2dcc239395a2826af45245aca96722f01196960a"}] diff --git a/testdata/test_counts.json b/testdata/test_counts.json new file mode 100644 index 00000000..26dce3a8 --- /dev/null +++ b/testdata/test_counts.json @@ -0,0 +1,202 @@ +["EVENT", {"id":"4433f14d7b79a313ffcdd744eb69e16761780b5811cb92917379ac14447b1eb2","pubkey":"45835c36f41d979bc8129830f2f5d92562f5343d6feddd6f30aa79480730f26e","created_at":1761586084,"kind":1,"tags":[["client","Yakihonne","31990:20986fb83e775d96d188ca5c9df10ce6d613e0eb7e5768a0f0b12b37cdac21b3:1700732875747"],["q","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"i was holding myself to not mute odel.\nbut now he has gone full coretard (retard core supporter).\n\ntrying to hit at Luke with one liners.\nbut never ever hit at a shady shit core did.\n\nthere are a lot of things to call \"fucked up\".\nhe decides to call a fact \"fucked up\".\n\nyou know what is fucked up?\nthe fact that we are actually here rn,\nbecause core couldn't stop removing policies from their client. because of their ego and superiority complex they formed in their little minds. you can't change a retard's mind with a thousand facts\n\ntheir addiction to add \"features\" to bitcoin that has nothing to do with bitcoin.\n\ncoretard devs don't have principles for protecting bitcoin and keeping it decentralized money. they dont have a hard limit that stops them from adding \"features\". if the \"feature\" is cool enough they have to bend everything and add it. such a childish retarded behavior. \n\nbitcoin is for bitcoin only. nothing else.\n\nbitcoin is bitcoin. nostr:nevent1qqsdgjkedjufysyj5a4u9t7aavfwhpfr8sxs8f7e4hzzc25957dyxpghdqx0d","sig":"6cb0fbe74008aaf3aa38cb59a95060a6e3850482a2b35d498ca4df74b572bb9ed5c722c9bdd35667f2a44a6c58fe15bda614fe8ca6e9558fcf8d4d2582e6f92d"}] +["EVENT", {"id":"a873aa612e4b90da8a87d56b11ffe064b5c1e483f29af07798ef8080db00547a","pubkey":"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245","created_at":1761527119,"kind":1,"tags":[["q","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"yup\n\nnostr:nevent1qqsdgjkedjufysyj5a4u9t7aavfwhpfr8sxs8f7e4hzzc25957dyxpgzyqzvj9w6alhrsvtl5u6ygjkwuwg2sf5lukqskgjpuhnd6dpal0kvjqcyqqqqqqgcd209e","sig":"c6f2826c8a046419afb7ba8410b5c6562690a275de92121eb179759266df6cd8d94984a845fadef8cb14f32d20739d2e16b7d41052cf4c182c520aee01ac5681"}] +["EVENT", {"id":"e72057669be4b18b2117fffff63a7ee4f49b6640caf3a88bb6b945c922b4523d","pubkey":"bd402c1b205e1ccce96a50f9f63bd6337eb8e778735050387f0151fbb6d5143b","created_at":1761596546,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","root"],["e","bc367630aae0af9ae390bcbf08c511ce105c186268da21374a2df44031f1da73","","reply"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","f728d9e6e7048358e70930f5ca64b097770d989ccd86854fe618eda9c8a38106"]],"content":"and your alternative is ? to keep the first attack vector as is ?","sig":"f69a6aa98dd55caad4c45238f8aedba751aed6adde786310d1f4c0af19f420db5dc3ba3d2f1a8b5df53f6e8a9a34bb6ee4ea9ac22cc877571d212aaf9df58f94"}] +["EVENT", {"id":"0dc8668a4f1561adbffb3fdbad532b3aa4893dd2654a1a86044b258eb62ac2e1","pubkey":"c572c9066388ec46172a487e6d3b2233b95c567c080fb1cf1d03d9cd71179456","created_at":1761595426,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"He should have wrote \"a lightning strike\". \n\nMissed oppurtunities.","sig":"d37f6550603fbd8b91b4e626879a32810ff9018e1a93984f021ef20f9aa47784ce7d5cf90cec111235fa1153596f6fcdab7ec58f73e16d8fe52d3665ccbd0da5"}] +["EVENT", {"id":"d890efa260ede0329b97268fef7e595868059287c317ec253e45f915cca7c38d","pubkey":"fb6f1ca6c1548931832d03a638d8ab7f24b29b7a75235c9d469ef149a1d7c38f","created_at":1761594369,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","00000009ce836fd52689667f542e4f50d137160427195b808e41f93e61182e62"],["p","fb6f1ca6c1548931832d03a638d8ab7f24b29b7a75235c9d469ef149a1d7c38f"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["p","aab93e8e3fa6a8974e1c1f3199e5f3d9afb7aaa70b8236e93a5b2fafeafcbd3a"],["e","a7fc3fac995e3a12b19b38371cf5614b1899dd665b265be036b750236f3dc8a0","","reply","aab93e8e3fa6a8974e1c1f3199e5f3d9afb7aaa70b8236e93a5b2fafeafcbd3a"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"Nnnnnnnooooooooooooooooooooooo! \n\n","sig":"421fb0bf3962bf809aef6852cd01777cf2968aaf659b2f2302f0c6d09a3f7a0039c3d0bf5bd43a8e5fde570fbdd672a9ee6226478ab69436e0c895eb6261f36b"}] +["EVENT", {"id":"bd614a357b1de53719a554b26508eae31c0573cde03a9b7e8be1418190eee934","pubkey":"314072c16fa9433e1374f62e5b02c8163946ed298a9cde3b1541513c29d19fff","created_at":1761594270,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","mention"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"]],"content":"At what point you idiots will call out this clown for his clownery? \nhttps://blossom.primal.net/287f816e33cd8227660ef3e71828e8ac0ca509135958cb62c2bd395d7f4e3e32.png\nnostr:nevent1qqsdgjkedjufysyj5a4u9t7aavfwhpfr8sxs8f7e4hzzc25957dyxpgpzemhxue69uhhyetvv9ujuurjd9kkzmpwdejhgeezchc","sig":"ed5bcfa949276526eb1789ae8e347a85958fd5aff340937c9f015c8d55d0262017b50d29e35b954ea983d0c1753d0208a979caff11becb8f8dfb7b8d12fd214b"}] +["EVENT", {"id":"56313cbbc32a18d4e0730a5ed31db641f661fbe25a2a84008339b51dc9e9ce1b","pubkey":"314072c16fa9433e1374f62e5b02c8163946ed298a9cde3b1541513c29d19fff","created_at":1761594008,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"]],"content":"You seem fucked up.","sig":"c1d842d67d7f1e41792bc6d92a48c3fd419a6f6b75abdc1598f3cb26ca2a6c2330c3862b31fdc8d728c1357a6558738c49ec73857a15025e7b070ef46cbbe66b"}] +["EVENT", {"id":"2717045cfe93347daca097869306f203dec09616dd8423812d7235b15191fc7c","pubkey":"7520be7cbe89dc050179d08a219bf479429646bea98813bb24eb6a050757d822","created_at":1761593208,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["r","wss://haven.tealeaf.dev/inbox"],["r","wss://relay.primal.net/"],["r","wss://nostr.huszonegy.world/"]],"content":"What is that?","sig":"81d09c28c89fb6dca72a4566eac9810138a77ea80c26c8ac9eb88e0d0cbbda3a29fe5e38b33d917113770296459ea392889dd5be4002c02a616231058eb220db"}] +["EVENT", {"id":"935886ca8a047787eebe17f4841717c5652e52e8d605855f6612b0aa7f7deed1","pubkey":"d3623c0dada22449011d613eeb32f7dc1aec6b135264b994078039324f5e23ad","created_at":1761590869,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["imeta","url https://blossom.primal.net/fcf58b68f5d018fa2569261094b7823ccf63f41412d6bd38e0f39c6da137f757.jpg","m image/jpeg","ox fcf58b68f5d018fa2569261094b7823ccf63f41412d6bd38e0f39c6da137f757","dim 300x225"]],"content":" \n\n\n\nhttps://blossom.primal.net/fcf58b68f5d018fa2569261094b7823ccf63f41412d6bd38e0f39c6da137f757.jpg","sig":"f2846afe44a04d0558c0fe3b66609f0088e12dfafabe646183c07f9b8a806a0e81f4d4732dfae8e5cf0354e378973559661f6dca883315028b98811e455f7391"}] +["EVENT", {"id":"071a1d08845bec7d037a0117de1bec4b1b7b6ef0d57d9459a36b302046d4ce4b","pubkey":"d3af45388825d5eb21bf89b4dcb9262e5f9fd65cba6125e3f2a78ce9e22085d4","created_at":1761586692,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["r","wss://nos.lol/"],["r","wss://relay.damus.io/"]],"content":"I swear to GOD I know of any of those shitcoiners are coming to Tokyo I will kick their ass so hard here that they will never visit again, in Japan is just your fist or knife, not the US I will fuck this Lopp mother fucker and his shitcoiner buddies too. I am not loosing my life savings because of the greed of some shitcoiners trying to fuck with Bitcoin. and I am ready everyday ready.","sig":"1e6bffded2fb247b26a6a1dbcb42fbcac319bd7e0c17e4e71304981c3738424fd0c059fb2ab339f654e7d99737d2717e94d01ec0d569ad9e35d8e8e11815a781"}] +["EVENT", {"id":"ce2968d17c9eab002d0a01a18034b717d2f7f435d43bcf121cce67b5e481f333","pubkey":"45835c36f41d979bc8129830f2f5d92562f5343d6feddd6f30aa79480730f26e","created_at":1761585048,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["e","00f3bff68ef3220592b424eac8e63bd6effb9558dff5f6b4eaef88bd2000615d","","reply"],["p","00000009ce836fd52689667f542e4f50d137160427195b808e41f93e61182e62"]],"content":"and you call yourself a bitcoiner","sig":"2975f9cceb8ce69ff2f78eb6b87342a3a09e92e60b7505344e04b162af8b767a4306f5b71e2eaacf0a4a6e7520d386adeaf1cf1835b50fac1e59ef3a853de7ca"}] +["EVENT", {"id":"c81e89f11f189ce9f14ddd9a21eabfdd31a87a4508d1cd6ecbaf288a08e321f9","pubkey":"45835c36f41d979bc8129830f2f5d92562f5343d6feddd6f30aa79480730f26e","created_at":1761584930,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"yeah core fucked it up","sig":"abd5d4a5713668d292b6192d419a647dd7e20bf0322133c882dea19beb6f8548e6362e78c9662e041ad68dbb8e0c47d3a93511cbff9799591b79ce6651d214ad"}] +["EVENT", {"id":"898af33ea93c98056f68c3c1b6bb1265080fb0b17a9a2ae67520c291ad53e7de","pubkey":"2588a7cd68112fdf0748db6d45877e47ea6a9aad9119ae7376f94c5c2709c70f","created_at":1761584772,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["r","wss://relay.primal.net/"],["r","wss://relay.snort.social/"],["r","wss://relay.nostr.band/"],["r","wss://relay.damus.io/"],["r","wss://nostr.wine/","read"],["r","wss://nostr.spaceshell.xyz/"],["r","wss://nos.lol/"],["r","wss://nostr.bitcoiner.social/"],["r","wss://nostr.mom/"],["r","wss://nostr.sudocarlos.com/"],["r","wss://nostr.thank.eu/"],["r","wss://purplepag.es/"]],"content":"Why don't we just quickly soft fork to disallow large OP_RETURN outputs (i.e. keep them capped to 80 bytes) only, with forward-looking activation (no retroactive block invalidation)? This would give the market a clear and simple way to decide whether such transactions are acceptable or not. It wouldn't have any of the issues mentioned so far in the discussion like creating an incentive to put CSAM or other illegal content to attempt a reorg double spend due to the complex deployment mechanism proposed in BIP-444 or like putting in jeopardy existing Taproot functionality by touching other primitives. We don't actually need to remove all the mechanisms that can be used for embedding data (which has been demonstrated to be impossible). Rather what we need is to give users the opportunity to signal whether arbitrary data is welcome or not.\n\nIf the legal concerns are compelling enough to warrant consensus changes, miners should support a clean soft fork immediately. If the market quickly adopts the new consensus-level OP_RETURN restrictions it would announce sufficiently clearly to the world that arbitrary data is not welcome and deter future attempts to bring that to Bitcoin by showing the network's readiness to resist such efforts. I believe that this PR is too dramatic and needlessly complicated for what we need: letting the market decide whether to accept or reject the explicit invitation for arbitrary data storage that Core v30 has created. Technical changes should be limited to this question only and simple enough for people to make a decision without being distracted or overwhelmed by adjacent issues.","sig":"05d939ae93ed864ebd34d977423061215aeec04ff83ab68aeb93f37571e8f318710e720db42dfeff5380bbefa0eaee06e9a4e8c4358de6cc6d0b966a56a7b200"}] +["EVENT", {"id":"c2acfe7de6a449be56649d316219d487ba36b5bce8e23cdc9e762638c43b06d7","pubkey":"cf8f07ebffbdce4976ea8ab830cfd6036ffb6203e67ba8eb7a9a448a742a6eaa","created_at":1761577747,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://nostr21.com/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://nostr21.com/","reply","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["client","noStrudel","31990:266815e0c9210dfa324c6cba3573b14bee49da4209a9456f9484e5106cd408a5:1686066542546"]],"content":"that gets an F","sig":"983f6966b5f55012352ae7bad817a98da894c9baa846e0a63629f890737a0fc510f6922fae003fa0f237ae1f481ab27738b0fe15183dd42337e978724f6578d6"}] +["EVENT", {"id":"2558cd8d3610fdf4154808db3f3e16bd3bfb8959a31f162273afe4aac9f2403e","pubkey":"43f2116eb9424dadb7be0f1051d52e00402b135455452cb2255ce430eb48f3f2","created_at":1761573819,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"]],"content":"Why? They are describing why they think the soft fork is needed. \n\nA description is not a threat. \n\nMaybe it could be worded better, but I think a lot of outrage over this one line is from people taking it out of context and looking for something to dunk on. ","sig":"c32d7a329e0fb5143032149ab7e37c10bcacbf0038ebd487885d4b77f39edd23f7b06fc6edf0be0e535a2545842fdb76bc16b03b3f8b89e145da30f6295747ba"}] +["EVENT", {"id":"16c7141a33719cf71e2e32e2d00201d9987da9aa850cbb39ba8f6b80bd8b1399","pubkey":"a396e36e962a991dac21731dd45da2ee3fd9265d65f9839c15847294ec991f1c","created_at":1761572144,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["r","wss://nos.lol/"],["r","wss://pyramid.fiatjaf.com/"],["r","wss://relay.damus.io/"],["r","wss://relay.primal.net/"]],"content":"is it not true?","sig":"d53649d29c2a1ad65dca38f9792e48dc5fb27942accbe14904867ec9ddaebb0d1e6093f8c21415f0affdb16184f3814ea88f1cdeedb8910b76428a2c8a68ae6a"}] +["EVENT", {"id":"ac4fc53fa10546375ece5fafcf649d169b5473a64f58b8953f02230a42371ddd","pubkey":"27cb1b4c5c881c5ecc3dc5029faa119669207d83ce6fd9a9e828253710756b8a","created_at":1761571466,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","mention"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"]],"content":"The body is dead, bloated and stinking. Search and rescue has yet to locate it, but you can smell it. Once it’s located, people will be held to account. Interesting times.\nnostr:nevent1qqsdgjkedjufysyj5a4u9t7aavfwhpfr8sxs8f7e4hzzc25957dyxpgpzemhxue69uhhyetvv9ujuurjd9kkzmpwdejhgeezchc","sig":"04e9a5ec859b25a11c80ff2ed3f73fb38a2fd297fe143e50eb0c9977a66c0909f2bc0b858dfbd729efd381463988901784059f44b66d7243e079ce0692c3628a"}] +["EVENT", {"id":"d38894e1428ca217cb0dd45ac043a037507cede5daa829fc6f5ef7312812872f","pubkey":"27cb1b4c5c881c5ecc3dc5029faa119669207d83ce6fd9a9e828253710756b8a","created_at":1761568688,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"]],"content":"A lot of things are fucked up. One liners without context are fucked up. Removing a moat that somewhat prevents shit on the network of humanity’s first shot at true money, is fucked up…. In my opinion. ","sig":"2b6e2468f684cd671048f547c8f658ec79ca3a689d490790c685a7ffef66736dbd25ae8c60e035d54a0f52b783be08c2c627a96ad4c559101ed57b4cbc305e76"}] +["EVENT", {"id":"caaf49bb907cf7ce7ae42d7621af64ff506d57a2a24dba266231212857927cd7","pubkey":"13cb9f915251404603a2ac5c41805b5a4de57f630205a359ffd95ca11739b133","created_at":1761567963,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","mention","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","74ffc51cc30150cf79b6cb316d3a15cf332ab29a38fec9eb484ab1551d6d1856","wss://nos.lol/","mention"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://premium.primal.net","mention"],["t","BIP444"],["imeta","url https://blossom.primal.net/2d6248bb32f371734756385e75a9ea979fb9031b97a7a7bef725946d870bf095.png","m image/png","ox 2d6248bb32f371734756385e75a9ea979fb9031b97a7a7bef725946d870bf095","dim 1013x1199"]],"content":"If this suggestion made from nostr:nprofile1qqs8fl79rnpsz5x00xmvkvtd8g2u7ve2k2dr3lkfadyy4v24r4k3s4sppemhxue69uhkummn9ekx7mp0qy08wumn8ghj7mn0wd68yttsw43zuam9d3kx7unyv4ezumn9wshscy566r gets implemented in the BIP then I don't know if nostr:nprofile1qqsqfjg4mth7uwp307nng3z2em3ep2pxnljczzezg8j7dhf58ha7ejgprpmhxue69uhhqun9d45h2mfwwpexjmtpdshxuet5qyt8wumn8ghj7un9d3shjtnswf5k6ctv9ehx2aqnz0fd0 will have anything left to FUD about BIP 444. \n\nLet's see what else he comes up with to FUD about BIP 444. I can't wait for the next FUD \n\nRegardless the outcome of BIP 444, plebs can now realize that he is TRULY CORECUCK (& COWARD TOO FOR NOT RAISING THE CONCERNS AGINNST TYRANNY OF SHITCOIN CORE DEVS)\n\n#BIP444 \n\nnostr:nevent1qqsdgjkedjufysyj5a4u9t7aavfwhpfr8sxs8f7e4hzzc25957dyxpgpzemhxue69uhhyetvv9ujuurjd9kkzmpwdejhgq3qqny3tkh0acurzla8x3zy4nhrjz5zd8l9sy9jys09umwng00manysxpqqqqqqzfukknp\n\nhttps://blossom.primal.net/2d6248bb32f371734756385e75a9ea979fb9031b97a7a7bef725946d870bf095.png","sig":"a556f61ab089ed9744786bdd9ab7d02d0d38f71e2ca2032045e87b2ee46b51d15f8a5375941df3c86cd372a9348761f8b26072813298ea3f99398740f47dc1c8"}] +["EVENT", {"id":"b2cfe7a4e1f2e7d7fd9097cfc28e8a8d825abf3cbc2de7f2c0970d2d5f66e36e","pubkey":"0861144c765ea10e39a48473a51bee604886e18abd0f831cc5ed7651e68a1caf","created_at":1761567907,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","","mention"],["r","wss://creatr.nostr.wine/"],["r","wss://pyramid.fiatjaf.com/"],["r","wss://relay.bullishbounty.com/"],["r","wss://relay.primal.net/"],["r","wss://relay.damus.io/"]],"content":"Finally, Knots supporters showing their true intentions\n\nnostr:nevent1qvzqqqqqqypzqpxfzhdwlm3cx9l6wdzyft8w8y9gy607tqgtyfq7tekaxs7lhmxfqqsdgjkedjufysyj5a4u9t7aavfwhpfr8sxs8f7e4hzzc25957dyxpgv5nppq ","sig":"d06c288b1d09d2f4ae3f612073752b1bc6dda20fe542174d87adea192856089563d8fc56a59793fbe27b35b7cf734a2ac3590add5299d389dcdcb4def8436102"}] +["EVENT", {"id":"26e8923ab85944eac73135a238693dc6366a09aa2c9868db6864cc47ddfc1829","pubkey":"08bfc00b7f72e015f45c326f486bec16e4d5236b70e44543f1c5e86a8e21c76a","created_at":1761567638,"kind":1,"tags":[["alt","A short note: Acting glowie AF"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.nostr.band/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"]],"content":"Acting glowie AF","sig":"e764d857b30523382fa1e7952fb050cb34d52659e81bd7442e79e5f1d01e89101c428b031e4e03c505fb09c74ec50527f01eeb7d833640a36a4ae6064223b5e8"}] +["EVENT", {"id":"1a67f7140520e05929f816d2574765ba96098948e1eaa0e4cc09878c81efd493","pubkey":"a60e79e0edad5100d7543b669e513dbc1c2170e8e9b74fdb8e971afd1e0e6813","created_at":1761566755,"kind":6,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"{\"id\":\"d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305\",\"sig\":\"f60bfd25b83837e0360f8d651192cbebf8bde85662bed9b6e06c6fd72892d3f85a638d6b44bcd8f9c8ffee3b94dd23bca04865c145b3441ea7975f2936ce8cac\",\"kind\":1,\"tags\":[],\"pubkey\":\"04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9\",\"content\":\"this seems fucked up?\\n\\nhttps://blossom.primal.net/1c80f09784489607cf37e6aad656e7b2257fa43995bb73aaf25dffb2b50852cc.jpg\",\"created_at\":1761514374}","sig":"950d9f0317b7afb503be852563ef955b68f2a8e5256709f8f2ba266695453b11d5179546927be5d863351c87625f2769b3706532db2bab15743c9c4c268ebb96"}] +["EVENT", {"id":"a7fc3fac995e3a12b19b38371cf5614b1899dd665b265be036b750236f3dc8a0","pubkey":"aab93e8e3fa6a8974e1c1f3199e5f3d9afb7aaa70b8236e93a5b2fafeafcbd3a","created_at":1761563826,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","00000009ce836fd52689667f542e4f50d137160427195b808e41f93e61182e62"],["p","fb6f1ca6c1548931832d03a638d8ab7f24b29b7a75235c9d469ef149a1d7c38f"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","10952083e0ec3cd6e4ede2799bfff655171c467a744068ab5b80f08468cc1843","","reply","fb6f1ca6c1548931832d03a638d8ab7f24b29b7a75235c9d469ef149a1d7c38f"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"Worse because you can't fucking spell \n\n","sig":"c5d226c92c04489a9f0f76b11b5233fbfca43fee46d044611547983343b43ca284ed5874a8e71ffba6459bc00400c0061c04cec74635d9143c8c2f852144ed1b"}] +["EVENT", {"id":"10952083e0ec3cd6e4ede2799bfff655171c467a744068ab5b80f08468cc1843","pubkey":"fb6f1ca6c1548931832d03a638d8ab7f24b29b7a75235c9d469ef149a1d7c38f","created_at":1761553533,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://nostr.einundzwanzig.space","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","00f3bff68ef3220592b424eac8e63bd6effb9558dff5f6b4eaef88bd2000615d","wss://nostr.einundzwanzig.space","reply"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","00000009ce836fd52689667f542e4f50d137160427195b808e41f93e61182e62"],["r","wss://relay.primal.net/"]],"content":"Worse cos we fight to keep Bitcoin staying as money? Bcashers ain't got nothing on uz!","sig":"0db553ff3d7c3a3bcb29d04ac33524e65a91c02fc0e598b200766c3d4a4aac55ffa00ad98c58a3f1c53e269a6b321f060950d5f7a42bf67ef28af32f8e9a9820"}] +["EVENT", {"id":"91dbfdc1d183effa936d31c46934944f4895cd68609227d1dac941b21b67b297","pubkey":"d986b8a48cef4950fc62f7dc2e0d277ca505757b5aa73f0959b20659e71f7cac","created_at":1761549479,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","d986b8a48cef4950fc62f7dc2e0d277ca505757b5aa73f0959b20659e71f7cac","wss://nos.lol/"],["p","deba271e547767bd6d8eec75eece5615db317a03b07f459134b03e7236005655","wss://ftp.halifax.rwth-aachen.de/nostr"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","be7e0bfbad2a60f778fc6455a354b8483a67d216479f30fd31584575885ca9e9","wss://ftp.halifax.rwth-aachen.de/nostr","reply","deba271e547767bd6d8eec75eece5615db317a03b07f459134b03e7236005655"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"Running a node behind TOR or I2P only maybe solves one problem here, which is the node runner getting arrested for CP possession or such.\n\nIt can't save the Bitcoin projects reputation, or protect Bitcoin's market value in such an environment. Its still a disaster. \n\n","sig":"368b16cb35d3dd5e9282252082966d172ba21eba456892646f7e64d3c476b6431c0515b5180dcb4d02ce68d79db5840dfb39f41dd48a401e446084a55e56df6f"}] +["EVENT", {"id":"575188eb8950435de2922e78cfa9f075f5b9eaecb44a5a4c3f5a26d750405ab7","pubkey":"bf79eed01d8b5d73f1de1312368d590f77070ce2e8183f43b2e51d064bff54f9","created_at":1761548097,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"Why softforking? Go big or go home \n\n","sig":"9d8ca72ba130ba326eaa62075f34ac5b915c3aa6a84153dc6420279b1a40a96c8698f44410681dcec96f07f0d5f0ca96f06242ea4c3ef93a5f380479851e3fe8"}] +["EVENT", {"id":"6a013d6f038989497ecfeaf2ef15f312f75f737399f9af2e21bae9301ac620ca","pubkey":"1408bad049bab8a38b976075affe413c3521bbeef62cc4ce3555299f4971f2ca","created_at":1761546078,"kind":1,"tags":[["alt","A short note: Legend"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","a072c07ed3c574859db1fd3d4df1cd630ed1cbc4d2f9a34667a331f83ec38c25","wss://relay.primal.net/","","1408bad049bab8a38b976075affe413c3521bbeef62cc4ce3555299f4971f2ca"],["e","dc733cf4fb77ebd1ea8a8800ec62c1a09b04eb03bd49d01aa273a8dce73737c7","wss://relay.primal.net/","reply","aab93e8e3fa6a8974e1c1f3199e5f3d9afb7aaa70b8236e93a5b2fafeafcbd3a"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","1408bad049bab8a38b976075affe413c3521bbeef62cc4ce3555299f4971f2ca","wss://hist.nostr.land/"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","aab93e8e3fa6a8974e1c1f3199e5f3d9afb7aaa70b8236e93a5b2fafeafcbd3a","wss://relay.primal.net/"]],"content":"Legend","sig":"7e4418c876b3a8b62ce0d2987f02820286931c282ef2008f94dc87fcb8d5236b76b19aa5bbae6b1ba0bcc9cbb0ca86bdf92e417d800b04612111d583400c67d9"}] +["EVENT", {"id":"00f3bff68ef3220592b424eac8e63bd6effb9558dff5f6b4eaef88bd2000615d","pubkey":"00000009ce836fd52689667f542e4f50d137160427195b808e41f93e61182e62","created_at":1761543052,"kind":1,"tags":[["alt","A short note: Because it is, extremely. Knotzis are worst than b..."],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"]],"content":"Because it is, extremely. Knotzis are worst than bcashers at this point.","sig":"879d0fa0cc93cadc087bb3e85ae6917b25745a7fde905f9edaa4ad29b09a5e8b6cd99c2078ae8d6376a829baa59998042938cab8bc1021c77f4c69c0f0dc6841"}] +["EVENT", {"id":"6249993be4acf9929da439d684ef06fd829b8cf88ab86e1ca2b5eee4ccd2c55f","pubkey":"e8214937bcf88708a16e7b0ed6ad1b92c2cad49ba11e01ba61d63a9d3e377b5a","created_at":1761540178,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"No. Just a true statement.","sig":"77d5b9a7be352934e3bc9d68e7ba976dc32422272e65458753ecb9801050539aa91b8c527d9767cfea0d8eec86692e650cb5d737cabed1423dbabe3677318908"}] +["EVENT", {"id":"335a2c30267a6f1e427faedc9d1becc84ea6330658fc8c5fabd6f2b57167121a","pubkey":"05972259ec683e9843ce12fae6cedda06e65062cfb014799e2abd52b2932bae6","created_at":1761533966,"kind":1,"tags":[["alt","A short note: Welcome to choosing between crazy and stupid. "],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"]],"content":"Welcome to choosing between crazy and stupid. ","sig":"452522850ee8d12034ea0590de9a6b75a83badc36b41221902b467797a78b535931f641349cc8e186bd1f17c40fc02420863329ef9f2b771c7d471ab1038dc86"}] +["EVENT", {"id":"b89cae5b6410dcef5581590fa5463aebd86fdf750e4c021176ec8e7f4c37d37e","pubkey":"6e75f7972397ca3295e0f4ca0fbc6eb9cc79be85bafdd56bd378220ca8eee74e","created_at":1761533963,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.snort.social/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.snort.social/","reply","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["client","noStrudel","31990:266815e0c9210dfa324c6cba3573b14bee49da4209a9456f9484e5106cd408a5:1686066542546"]],"content":"Wall street is taking supply out of circulation and people call it mass-adoption\nCore turns the electronic cash system into a digital photo album.\n\nOne hell of an epoch so far","sig":"2bb9b1f35aafbbacd878486a17a50f03d8805ac4c1f875ef8f62ebe772436eda744f093c129f0b35144f361996d252756936a4f60eebde3be62c42ae8c940bdb"}] +["EVENT", {"id":"078ba1be0439f337f81feba9c717132d36b058dc5959746748a7662680059916","pubkey":"e7c5f523341649c4dc6d8f2a599e5d141ce6f289bb58f80e528fe7d71fcac519","created_at":1761533918,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","431116c2cdb5f6bd0209e9c09623e4a860198721b81c6551a3787140ced506c3","wss://relay.damus.io","reply"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"],["p","d68cdf0fbb490c42d98f8cf8594f7629790c2e04d5562a11a766b2ed50514026","","mention"]],"content":"\nhttps://blossom.primal.net/1925e61e5a58b65411038ab4041862778ce3136c692caa5cb87b8f14e640cfcd.jpg","sig":"563c178cd2192c5722f23d78f1249b3d10d0f39617f6370cb601d952ade974bb6652291ac7c4f3b6204f703e6294bc5e67a7951f46a0f1dd01f998c87a22c2db"}] +["EVENT", {"id":"c4f77fd24bd9daceb8b4bf69106e67d0591e3e6934a6a085a992b4095f62947b","pubkey":"dab5abda0115c5af5192aeae980743333d4f8690e71526ce4f807501b889c516","created_at":1761532787,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","c07310763926b41c488d475b42a84171dc705bd196541ae302167174afc19859","wss://ftp.halifax.rwth-aachen.de/nostr","reply"],["p","deba271e547767bd6d8eec75eece5615db317a03b07f459134b03e7236005655","","mention"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"]],"content":"It’s being used against us in Australia right now with age verification laws.","sig":"24dbded053c3363e86f0f1ac6dafcd28336d0061bce88c5acc7a424b78273474fb5f2c6b4d76f80225bacf0e7c64c4dec30908bea775de45b5fdc0e261539c26"}] +["EVENT", {"id":"c07310763926b41c488d475b42a84171dc705bd196541ae302167174afc19859","pubkey":"deba271e547767bd6d8eec75eece5615db317a03b07f459134b03e7236005655","created_at":1761532464,"kind":1,"tags":[["alt","A short note: I agree that this would be a valid spook tactic."],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","1958a3d231980c0e02dec3213e3223967a2ee48dfdafbd4c5d18f79d05738fdd","wss://relay.nostr.vet/","","deba271e547767bd6d8eec75eece5615db317a03b07f459134b03e7236005655"],["e","59016b299d6ccaa9c160321c66d58066f5406976c7dccfb81b3a73baf8db5e0b","wss://relay.primal.net/","reply","dab5abda0115c5af5192aeae980743333d4f8690e71526ce4f807501b889c516"],["p","deba271e547767bd6d8eec75eece5615db317a03b07f459134b03e7236005655","wss://relay.primal.net/"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","dab5abda0115c5af5192aeae980743333d4f8690e71526ce4f807501b889c516","wss://relay.damus.io/"]],"content":"I agree that this would be a valid spook tactic.","sig":"0a12ba06ee0e728d94f2471a5c6db23bb2c6a1005777a48d498eb7dd9d74f8e6faba67a6ca27c7e7eb41fe1afda76ba0271bea13d0bba2231aa528200f4937ed"}] +["EVENT", {"id":"59016b299d6ccaa9c160321c66d58066f5406976c7dccfb81b3a73baf8db5e0b","pubkey":"dab5abda0115c5af5192aeae980743333d4f8690e71526ce4f807501b889c516","created_at":1761532236,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","1958a3d231980c0e02dec3213e3223967a2ee48dfdafbd4c5d18f79d05738fdd","wss://ftp.halifax.rwth-aachen.de/nostr","reply"],["p","deba271e547767bd6d8eec75eece5615db317a03b07f459134b03e7236005655","","mention"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"]],"content":"It’s escalating though. The debate at Lugano was ridiculous. He shouted “CP” and “Bad actor” in between some debate.\n\nThis is in no way the way to get others on board. If anything it pushes them in the other direction. It’s a spook tactic.","sig":"c63094d6f1c0d0613eb5f29355f7af42e18f257d8f6c1ecf478056b766d47021f7bc53614288284061c3583d7f8b4b77ae9ef1443a705165cdedec2e535daf9e"}] +["EVENT", {"id":"42321bd1e3b07896b70c4edeb061a51d58b792514fb9497c994927d171c957cd","pubkey":"dd1f9d502c7951df47e8f8ed245e8bfa24f7e82c28f19399a8f0e74b06113a21","created_at":1761530565,"kind":1,"tags":[["alt","A short note: No. I would run an I2P node."],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","a61b6b67bbea65632992da1ba780ce677dc66a9bfc6c5e69d67ccb8b6929fbea","wss://relay.primal.net/","","d986b8a48cef4950fc62f7dc2e0d277ca505757b5aa73f0959b20659e71f7cac"],["e","be7e0bfbad2a60f778fc6455a354b8483a67d216479f30fd31584575885ca9e9","wss://relay.primal.net/","","deba271e547767bd6d8eec75eece5615db317a03b07f459134b03e7236005655"],["e","a3f878c4ed7ce0ed106c50baeb877b7224dbd88b0b0f46bef1a52452ca401403","wss://relay.damus.io/","","dd1f9d502c7951df47e8f8ed245e8bfa24f7e82c28f19399a8f0e74b06113a21"],["e","7956870b0c62cf61fd68704467b74f2d52ac7a3bd36ae165f5ed4de362c2b133","wss://nostr-02.yakihonne.com/","reply","deba271e547767bd6d8eec75eece5615db317a03b07f459134b03e7236005655"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","d986b8a48cef4950fc62f7dc2e0d277ca505757b5aa73f0959b20659e71f7cac","wss://nos.lol/"],["p","deba271e547767bd6d8eec75eece5615db317a03b07f459134b03e7236005655","wss://relay.primal.net/"],["p","dd1f9d502c7951df47e8f8ed245e8bfa24f7e82c28f19399a8f0e74b06113a21","wss://relay.damus.io/"]],"content":"No. I would run an I2P node.","sig":"19d979b1bbc40ad365b857029fd8e2da86d2e53587f1dd3cbf651f8ac86e30a80e3b485c8681900e120a740eb4417b096ead8e6543db48ae666acf0fa95163f5"}] +["EVENT", {"id":"7956870b0c62cf61fd68704467b74f2d52ac7a3bd36ae165f5ed4de362c2b133","pubkey":"deba271e547767bd6d8eec75eece5615db317a03b07f459134b03e7236005655","created_at":1761530515,"kind":1,"tags":[["alt","A short note: Would you personally run a Tor node?"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","a61b6b67bbea65632992da1ba780ce677dc66a9bfc6c5e69d67ccb8b6929fbea","wss://relay.primal.net/","","d986b8a48cef4950fc62f7dc2e0d277ca505757b5aa73f0959b20659e71f7cac"],["e","be7e0bfbad2a60f778fc6455a354b8483a67d216479f30fd31584575885ca9e9","wss://ftp.halifax.rwth-aachen.de/nostr","","deba271e547767bd6d8eec75eece5615db317a03b07f459134b03e7236005655"],["e","a3f878c4ed7ce0ed106c50baeb877b7224dbd88b0b0f46bef1a52452ca401403","wss://relay.primal.net/","reply","dd1f9d502c7951df47e8f8ed245e8bfa24f7e82c28f19399a8f0e74b06113a21"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","d986b8a48cef4950fc62f7dc2e0d277ca505757b5aa73f0959b20659e71f7cac","wss://nos.lol/"],["p","deba271e547767bd6d8eec75eece5615db317a03b07f459134b03e7236005655","wss://relay.primal.net/"],["p","dd1f9d502c7951df47e8f8ed245e8bfa24f7e82c28f19399a8f0e74b06113a21","wss://relay.damus.io/"]],"content":"Would you personally run a Tor node?","sig":"b09d089c2533b8bbf9f21ccf58e0ded2f7a492945938521afb04562d37a09346274aafa43788d3962c9c284a31bc4b123184cba152b2085ad368ddb7a7f93904"}] +["EVENT", {"id":"a3f878c4ed7ce0ed106c50baeb877b7224dbd88b0b0f46bef1a52452ca401403","pubkey":"dd1f9d502c7951df47e8f8ed245e8bfa24f7e82c28f19399a8f0e74b06113a21","created_at":1761530286,"kind":1,"tags":[["alt","A short note: That they aren't money and are literally meant for..."],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","a61b6b67bbea65632992da1ba780ce677dc66a9bfc6c5e69d67ccb8b6929fbea","wss://relay.primal.net/","","d986b8a48cef4950fc62f7dc2e0d277ca505757b5aa73f0959b20659e71f7cac"],["e","be7e0bfbad2a60f778fc6455a354b8483a67d216479f30fd31584575885ca9e9","wss://relay.primal.net/","reply","deba271e547767bd6d8eec75eece5615db317a03b07f459134b03e7236005655"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","d986b8a48cef4950fc62f7dc2e0d277ca505757b5aa73f0959b20659e71f7cac","wss://nos.lol/"],["p","deba271e547767bd6d8eec75eece5615db317a03b07f459134b03e7236005655","wss://relay.primal.net/"]],"content":"That they aren't money and are literally meant for data transference. ","sig":"6e302674c868b99decbd035c6eeebe5ab7aa0469ced40884a83a3dcd3e665f1135420386eace01c0992c65b2d2215a7ecc69629a450e626e5d0385bde5369f23"}] +["EVENT", {"id":"1958a3d231980c0e02dec3213e3223967a2ee48dfdafbd4c5d18f79d05738fdd","pubkey":"deba271e547767bd6d8eec75eece5615db317a03b07f459134b03e7236005655","created_at":1761530130,"kind":1,"tags":[["alt","A short note: He has always been doing things like this. What do..."],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","3b2ead0dcf02beeb4126d33e284b8aee4a1e4cc4c8ef6f595a114a1e607e6877","wss://relay.primal.net/","reply","dab5abda0115c5af5192aeae980743333d4f8690e71526ce4f807501b889c516"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","dab5abda0115c5af5192aeae980743333d4f8690e71526ce4f807501b889c516","wss://relay.damus.io/"]],"content":"He has always been doing things like this. What do you think is new?","sig":"9f8c3ad0f6883ee3db8386c1ad0d23e40029ebca86d3de081304a866dd7a55f1ed3d2ede58cfe9b95742ccd48a5502a68fe6db503a538bb286e5417f921a5f50"}] +["EVENT", {"id":"7ae989f4d753c805354e05d3dc4520f9c4c836b374dbdb36ab6bc3a63170ae39","pubkey":"a4cb51f4618cfcd16b2d3171c466179bed8e197c43b8598823b04de266cef110","created_at":1761530088,"kind":1,"tags":[["alt","A short note: Confess. \nhttps://image.nostr.build/bc3aef79959ece..."],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://nostr.wine/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["r","https://image.nostr.build/bc3aef79959ece2ed39c33db3271c972627776fe9ad00b9f170f177ec60fe8d1.jpg"],["imeta","url https://image.nostr.build/bc3aef79959ece2ed39c33db3271c972627776fe9ad00b9f170f177ec60fe8d1.jpg","x a7d67fcc9f1e0d1acce9c07a9e2e478002ce563355428acb2d53ebb25957fcd3","size 59245","m image/jpeg","dim 1229x1160","blurhash ;kQvd$oy%Mxu.RjaX8jZR6_NaeR*WBIAozaekCtRxuofWBWBa{ayofayWBD%a|t7ofozRjW;ofs:%MWBM{oLt6ofjZWVWBxuoft7R*M{ofoMaeayM{bGofoLofRjWBt7oft7ayRjj[t7j[ofWBWB","ox a7d67fcc9f1e0d1acce9c07a9e2e478002ce563355428acb2d53ebb25957fcd3","alt "]],"content":"Confess. \nhttps://image.nostr.build/bc3aef79959ece2ed39c33db3271c972627776fe9ad00b9f170f177ec60fe8d1.jpg","sig":"dc3c6156a9345f0da07909991a02aa7196f3f5f42954fcd3df09b9a55a3dfe509cb21d7a12aef1cb428200284a62bc416401095e4f58276df2a867864ec558b7"}] +["EVENT", {"id":"94354a4a0732a09d20fee893d7508f8b14e0cb43abc3a1338a825ef8d7881fae","pubkey":"1bc70a0148b3f316da33fe3c89f23e3e71ac4ff998027ec712b905cd24f6a411","created_at":1761530057,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"💯","sig":"442cacc569029a5e4f0c35680410cf5042bd70d10444acde0f9b8907a81c57d481ec268666d39181e93b7a593f3aaa51ad6e1342a678563f82d428494b2b5b6e"}] +["EVENT", {"id":"be7e0bfbad2a60f778fc6455a354b8483a67d216479f30fd31584575885ca9e9","pubkey":"deba271e547767bd6d8eec75eece5615db317a03b07f459134b03e7236005655","created_at":1761529723,"kind":1,"tags":[["alt","A short note: How do you feel about Tor and Tor nodes?"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","a61b6b67bbea65632992da1ba780ce677dc66a9bfc6c5e69d67ccb8b6929fbea","wss://relay.primal.net/","reply","d986b8a48cef4950fc62f7dc2e0d277ca505757b5aa73f0959b20659e71f7cac"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","d986b8a48cef4950fc62f7dc2e0d277ca505757b5aa73f0959b20659e71f7cac","wss://nos.lol/"]],"content":"How do you feel about Tor and Tor nodes?","sig":"e196ef17512b9f68eb581e0f7d307435e92481183b9b49a8abfe426772cb02721ca0920fe58be8d7e89d60bc4a88da72a7c2d46288b5b468570f04e251d0782d"}] +["EVENT", {"id":"353650488951095ef21800bb2cbc3ce63736fe3260859585c662140a99108ceb","pubkey":"87e98bb670527d44379bd73a6c920afeb115297c0c0650cc182138388d6616f4","created_at":1761529103,"kind":1,"tags":[["alt","A short note: It may be worded poorly, but if you read it in con..."],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"]],"content":"It may be worded poorly, but if you read it in context, you know exactly what it means.\n\nIf you do illegal things, will have to face the consequences of doing or allowing those illegal things. It's not a threat, it's a warning.\n\nLike, \"don't climb over that fence, or bad things will happen\". When the dog attacks you, don't come back crying about how you didn't realise.","sig":"b55de1313e0f35a537f1dc890811b2a55d399bdd6837d89996daf75982174495d4ecb76128ef7e96a70a3e852c9db283a1260a3b03e5c96e3b7d2adb34501d22"}] +["EVENT", {"id":"ce1f600b1b9ebb07354f0aa554a8612cba86ed3e97697b30a55cb454cc4f40b2","pubkey":"87e98bb670527d44379bd73a6c920afeb115297c0c0650cc182138388d6616f4","created_at":1761528526,"kind":1,"tags":[["alt","A short note: It's either a slippery slope or there's no chance ..."],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","10caddb9d95d4f5aa69b026db2b11ca45ac6a7085806296db30cca8c5c4d198e","wss://relay.primal.net/","","493a5a07be1dc06a1dd2d8e943d8924fd469f057857e26f7b99ebdebe00c2557"],["e","3fe6548807dd650a886e91c0512a91aba09226b6f97a95762342ba35e38936e0","wss://relay.primal.net/","reply","aab93e8e3fa6a8974e1c1f3199e5f3d9afb7aaa70b8236e93a5b2fafeafcbd3a"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","493a5a07be1dc06a1dd2d8e943d8924fd469f057857e26f7b99ebdebe00c2557","wss://relay.wavlake.com/"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","aab93e8e3fa6a8974e1c1f3199e5f3d9afb7aaa70b8236e93a5b2fafeafcbd3a","wss://relay.primal.net/"]],"content":"It's either a slippery slope or there's no chance it's going to be an issue.","sig":"5eb66d510f3a8bdc18872b0862c8f2d5037b1cf1f852108a523bad22c12fcc09b4026993d63864855649a8281cc56fb14d6ad2c177ad0649c1cd4c6a21c7bea0"}] +["EVENT", {"id":"66185d5f76f4a26dc3ddefb0606c8e48ad409d793da97f95744093c0b25c859e","pubkey":"0597e028311169195256616e68a0de3537d2b42d3acfd5cc638a04daa2f723b6","created_at":1761528381,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","a7eb078681a447b0546b22b432f20c22a050aec92e5f96c59eafd80d1d200008","wss://relay.primal.net","reply"],["p","165b9d37a28903103af9ad24036bdc54270882c01add515791fc9666167cfac1","","mention"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"]],"content":"My bullshit meter 🧭 just exploded after reading this note. Hyperbole / 10 😂 ","sig":"e75a328c3eca6efeadc26d88371d970072824a9b9e965af85d3d67e409bf049d6ae154af63c285799956e1798d0428a600bc86add2e86acf894356b02b18b0e2"}] +["EVENT", {"id":"791830ff7788994a452cd2308d9a44190b628669ee716bae0467d59975e64118","pubkey":"e7c5f523341649c4dc6d8f2a599e5d141ce6f289bb58f80e528fe7d71fcac519","created_at":1761528214,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"]],"content":"\nhttps://blossom.primal.net/e0be50afaf42ec4c70621894a737198f4945036234cb8823b2b2761770d7bbbe.jpg","sig":"745833fcceecf1f7afa4ab86841ad009da136c319e4246e2716273ef6d8e22edcc3c32c7929959e339ae3792524720b7bc511739585924596f792da5c3cc58bf"}] +["EVENT", {"id":"e763cb55c49236557d33eb42211afcea87ec9011e9697d58a7b21601f7902c2f","pubkey":"c3c7122c63af5281fee3b9338d10ae65b166ffe6f73f1c272c5c1120607731d7","created_at":1761528070,"kind":1,"tags":[["alt","A short note: There is only one correct response\nhttps://copy.co..."],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["r","https://copy.corey.lol/shares/4uaniygai9cu/signal-2025-08-17-17-57-00-731.mp4"]],"content":"There is only one correct response\nhttps://copy.corey.lol/shares/4uaniygai9cu/signal-2025-08-17-17-57-00-731.mp4","sig":"59b587fe35a5666cb6769d50e139bc0cf4d54067bc9c2b218d75040f72c74a92ab1e5d63b8fef8aaa8256091b0a0e77f29d5b9f5976bf28753e00728a0bb251c"}] +["EVENT", {"id":"c35f62ad79106875099db2e11f74f2f936960e30d488d51252fdeff186d3c1cf","pubkey":"4f4f82846698ff66ae5fa9fad4c0c4eb7823afb07fa9f54ea9d15f1217ae96cc","created_at":1761527830,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","","root"],["e","8c88d5d84f60e0eb027abdd89eaa7ffce0b1d7468bae6189cf6aa4946581cb26","","reply"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","958b754a1d3de5b5eca0fe31d2d555f451325f8498a83da1997b7fcd5c39e88c"]],"content":"Seems like the powers that be are going to go after people for using bitcoin….this is like the “we warned you” phase and good for narrative building in the court of public opinion…I maintain we still haven’t truly seen the “then they fight you stage”","sig":"423b831c2ccf0da7c0bb731a917abf811aa0939e13b1b5e9a5dfe8178fab92d077fcac51fa5b4467891e9a8917e2341330da587bc0942898d3e6ec73165d1668"}] +["EVENT", {"id":"ed17ec39346622ee4faa7bce61c356a83bdf8f8e2be18da65c742ad85abafbb5","pubkey":"357efc15a0a2a39919300301d29b1db43e537bf19b7846ab16466f2089c5446a","created_at":1761527362,"kind":1,"tags":[["alt","A short note: I'm ready for both. Bring it. "],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"]],"content":"I'm ready for both. Bring it. ","sig":"945f70ff8a4efbb23ace6704ee6838b50b40898ea4f09a2901906eb1630e6787d08f398a36523456c61fc300d6e3fc7178fa7274501f4c281f5fd33b7607fc26"}] +["EVENT", {"id":"3b2ead0dcf02beeb4126d33e284b8aee4a1e4cc4c8ef6f595a114a1e607e6877","pubkey":"dab5abda0115c5af5192aeae980743333d4f8690e71526ce4f807501b889c516","created_at":1761527211,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"]],"content":"Starting to find it hard to support Luke 100% at this point.","sig":"fdc101d1d3ea0774c3971b83280a5bf3821c4b29b0590f52b91d9404eac3da6551a83328991b76db566ba8da3fc61e351230d5e5a01c31429ca2be9ab23f719f"}] +["EVENT", {"id":"2c30801614337350b8f5bd3b2c485ede4c0c41d88bd16b4a1c146702e6f8498a","pubkey":"adeddcaed09462b291f1dd54dbb5806198710eb0b520fc0ed7b0629147cee262","created_at":1761527099,"kind":6,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"{\"sig\":\"f60bfd25b83837e0360f8d651192cbebf8bde85662bed9b6e06c6fd72892d3f85a638d6b44bcd8f9c8ffee3b94dd23bca04865c145b3441ea7975f2936ce8cac\",\"created_at\":1761514374,\"tags\":[],\"pubkey\":\"04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9\",\"kind\":1,\"content\":\"this seems fucked up?\\n\\nhttps:\\/\\/blossom.primal.net\\/1c80f09784489607cf37e6aad656e7b2257fa43995bb73aaf25dffb2b50852cc.jpg\",\"id\":\"d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305\"}","sig":"22478b0b9f6c2574a43d365d7ebebbb7cd3037df9c20f2bb9ade322a39698702c04c5d430ead594da8c61bb26794a7f0d713373a236344c1c3abb8af4d5d7d4d"}] +["EVENT", {"id":"601a352446948815bcd83c54d16a5820a2398cc1dca63699805d7ac1eef28cf8","pubkey":"2f29aa33c2a3b45c2ef32212879248b2f4a49a002bd0de0fa16c94e138ac6f13","created_at":1761522946,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://premium.primal.net","mention"]],"content":"nostr:nprofile1qqsqfjg4mth7uwp307nng3z2em3ep2pxnljczzezg8j7dhf58ha7ejgprpmhxue69uhhqun9d45h2mfwwpexjmtpdshxuet5qyt8wumn8ghj7un9d3shjtnswf5k6ctv9ehx2aqnz0fd0 \nhttps://media.tenor.com/WEZkNbUwN6kAAAAC/zoolander-gas.gif","sig":"7aa0eb0270b57892952d0363edab721c864d68319e90b90afb77b95f0ca2228d4a3668a6b38cc800bf26832368af578530ea1b54763fbe137377a532b44e98f4"}] +["EVENT", {"id":"c8595721c4f5f9709be00372bd863c6be1bc12d461facd7d4ff8038e033d8a2e","pubkey":"6c1a7984267f31c71761085a91cfd74a8310286e915c2f1a0f2638092567f6a4","created_at":1761522911,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","a7eb078681a447b0546b22b432f20c22a050aec92e5f96c59eafd80d1d200008","wss://relay.primal.net","reply"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"],["p","165b9d37a28903103af9ad24036bdc54270882c01add515791fc9666167cfac1","","mention"]],"content":"It makes sense to me","sig":"ccd258aba3934ae20d07e857920ee666d427f12068011a41701e8c5771d754749ec595c8108951946ec8f90dcca6ee53a607cacf5c94468c90f9b151253e0210"}] +["EVENT", {"id":"7a3dbaa663160692f7e63b4f178b21e5f69b028f0c0f2cd1b418aeb12a5d47fc","pubkey":"6c1a7984267f31c71761085a91cfd74a8310286e915c2f1a0f2638092567f6a4","created_at":1761522641,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"]],"content":"Core v30 brings BS we need UASF for sure. Bitcoin is money.","sig":"ee99053ffc1a3ff382230539e5400ed9e868d522e75299df4a480c9ef0318d9138fe5c2fecf7b1c869a7ff345a5b1fc234e5d5c78377a97aa3d91d19637cc823"}] +["EVENT", {"id":"93a557b84fd5193229ad2ecc473494d347119da77f461fb8fb735bd53369f0bf","pubkey":"273d77da30025faa363e40b61155afc83d9bf76b77a937c23357f3fd92f85aca","created_at":1761522532,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"No question mark required. You can just say things. \n\n","sig":"09d362abb8e3e6d9059ea49517995a02446970d5b9d5593e2a4d30fe6e27237d314d71c2763dadef6d9a671074a991385c85576898d310ba5742a3c69d4e1f52"}] +["EVENT", {"id":"dd7ae39a7bac0c3b456aa5fe07539c2999c635430313a22114b587c88497f985","pubkey":"ee6ea13ab9fe5c4a68eaf9b1a34fe014a66b40117c50ee2a614f4cda959b6e74","created_at":1761522451,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","","root"],["e","580c0d214b68ae4cfa8b53e1d5af5f6a8a81975c4f73dc93c54306be59a85234","","reply"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","846307688a09ec112967eb0544e78a9e88c48f50712c39ed491ff2b6fa019ae8"],["p","c13d020bbca9a27d651099809af9d4b61ffd46e844f20e477c17cd2a0304d8a3"]],"content":"Good look with your BLV coin! ✌️","sig":"cecafae4d088e7472b66e95db23c76642cbfc2a2d2e8f10c755b40a1c3a571f3343bfc5256947e69b3c242602f003bad1b984bb51b19a43bb8818286158d6492"}] +["EVENT", {"id":"941415bf638307135242ed24d4bd9bb3b1ea3a6b8cda3781005f6b893066a290","pubkey":"e37fa6726260cc23575f1fc225cad37124dc7d0599c01f79831cb97bd6264125","created_at":1761522187,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","a4557aa997e2952f26b8ef7389c34e5c66f141c75eada8c00c5e6e575ade55f5","wss://relay.primal.net","reply"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","77f56243a824d22573fb755dd52c73c14986d15c0c98512d45f4deb08e9f879a"],["r","wss://bitcoiner.social/"],["r","wss://eden.nostr.land/","read"],["r","wss://nos.lol/"],["r","wss://nostr.bitcoiner.social/"],["r","wss://nostr.wine/","read"],["r","wss://offchain.pub/"],["r","wss://purplepag.es/"],["r","wss://relay.nostr.band/"],["r","wss://relay.primal.net/"]],"content":"Bang, Bang. \n","sig":"802e5c157665ea03bc7d245c83ecc3de0c25f7d1e76c13cd79dce9de9fe7a1903d243b75efb02cbd93524267582f7841cfeffba0f4f46f376391b13481a528e9"}] +["EVENT", {"id":"0712d5c6ebebf67c8f6f835f4d8d10e8cd3dc688b51edac37bbd0101c402bcaf","pubkey":"df5b213d8eeda721796ba42cd7ea6cfccadb99ba2b382e2c089d8ed8b08a9ba1","created_at":1761521494,"kind":1,"tags":[["alt","A short note: This is just spin\nnostr:nevent1qqsdl74nqgtm6sn92r6..."],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","38f16341d6b1a1225994b1810f7a3c62153eaacf27bb643dc2d6b870819b5430","wss://wheat.happytavern.co/","reply","61066504617ee79387021e18c89fb79d1ddbc3e7bff19cf2298f40466f8715e9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","61066504617ee79387021e18c89fb79d1ddbc3e7bff19cf2298f40466f8715e9","wss://wot.sudocarlos.com/"],["p","df5b213d8eeda721796ba42cd7ea6cfccadb99ba2b382e2c089d8ed8b08a9ba1","wss://nostr.bitcoiner.social/"],["q","dffab30217bd426550f46eb608995cfc4ac343130a3337d1d64b9543bad87c75","wss://nostr.bitcoiner.social/","df5b213d8eeda721796ba42cd7ea6cfccadb99ba2b382e2c089d8ed8b08a9ba1"]],"content":"This is just spin\nnostr:nevent1qqsdl74nqgtm6sn92r6xadsgn9w0cjkrgvfs5veh68tyh92rhtv8cagpr4mhxue69uhkummnw3ezucnfw33k76twv4ezuum0vd5kzmp0qgsd7kep8k8wmfep0946gtxhafk0ejkmnxazkwpw9syfmrkckz9fhggrqsqqqqqpmzzvf4","sig":"3da11648118451da7d64a742e2da07e88e8067c951d2d3dfa64fa67d10b941621ff0e2d9b4353be77ea2784b12c5c80b47353ffeb648d79d9c8891b55aa4e2cf"}] +["EVENT", {"id":"580c0d214b68ae4cfa8b53e1d5af5f6a8a81975c4f73dc93c54306be59a85234","pubkey":"c13d020bbca9a27d651099809af9d4b61ffd46e844f20e477c17cd2a0304d8a3","created_at":1761521406,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","","root"],["e","beb732c0f7448c8afe470f1c8626cf02ecc1ad868184b2278a30d54f7251b54a","wss://espelho.girino.org/","reply"],["p","ee6ea13ab9fe5c4a68eaf9b1a34fe014a66b40117c50ee2a614f4cda959b6e74","","mention"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"],["p","846307688a09ec112967eb0544e78a9e88c48f50712c39ed491ff2b6fa019ae8","","mention"]],"content":"We’re trying! nostr:npub1s33sw6y2p8kpz2t8avz5feu2n6yvfr6swykrnm2frletd7spnt5qew252p ","sig":"66b30afd7f21623b47029282a2c1d5c4d4496c63a10572ab8175c32aabe1058f9d1f781654a0769e2f7066f4f0c6db6facd973a8881d2fa19c954916764c7831"}] +["EVENT", {"id":"9c79ced0521f7de9a0bf41762c20de17c0c9bd3d7aabbe75b688b08a68044f4c","pubkey":"c13d020bbca9a27d651099809af9d4b61ffd46e844f20e477c17cd2a0304d8a3","created_at":1761521355,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","3fe6548807dd650a886e91c0512a91aba09226b6f97a95762342ba35e38936e0","wss://relay.primal.net","reply"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"],["p","493a5a07be1dc06a1dd2d8e943d8924fd469f057857e26f7b99ebdebe00c2557","","mention"],["p","aab93e8e3fa6a8974e1c1f3199e5f3d9afb7aaa70b8236e93a5b2fafeafcbd3a","","mention"]],"content":"You’re a joo, of course you support CSAM.","sig":"0da8b50c4b5a0e6fcc5ba826a77d1a4fcdef37cead2feebdd28fbbe7936dbdcb0059364cc8ee1d7337918273e32b6f3e6e1d2d9413edeeb913f793236b20d02b"}] +["EVENT", {"id":"a61b6b67bbea65632992da1ba780ce677dc66a9bfc6c5e69d67ccb8b6929fbea","pubkey":"d986b8a48cef4950fc62f7dc2e0d277ca505757b5aa73f0959b20659e71f7cac","created_at":1761521254,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["r","wss://nos.lol/"],["r","wss://nostr.mom/"],["r","wss://nostr.stakey.net/"],["r","wss://offchain.pub/"],["r","wss://purplepag.es/"],["r","wss://relay.nostrdice.com/"],["r","wss://relay.primal.net/"],["r","wss://relay.damus.io/"]],"content":"Yes, running a node with large data carrying will almost certainly mean you're hosting illegal content. Doing this comes with real life consequences. I don't make the rules.","sig":"b25845e6eba00f5567275ffa431a3924fd6d73d662c8b2f6f85f0e15b2ffbd5b04c70b249c28531d2f693b4e3536773f70c025f540d2808429287f44b4f23aff"}] +["EVENT", {"id":"554f937cf7515ace5c7bcba56aa69a0acb52936f91876b472ccede6bef4e418d","pubkey":"bb1c5151439e9abb680c15b191c1606b03333db3acc35a713091d9913751c3b3","created_at":1761520171,"kind":1,"tags":[["alt","A short note: Closest nostr ever gets to X is reading the commen..."],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","10bc0e9b76a95ff5cecc5b8315d5938730ac1bca1760ee5cad14424838cb70bf","wss://relay.damus.io/","","13cb9f915251404603a2ac5c41805b5a4de57f630205a359ffd95ca11739b133"],["e","f8dd7fafe4d4ea0c8eed302b8a642f0ae86b2cdcd0666cb31ba2ee68759780d8","wss://premium.primal.net/","reply","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","13cb9f915251404603a2ac5c41805b5a4de57f630205a359ffd95ca11739b133","wss://filter.nostr.wine/npub1vnmhd287pvxxk5w9mcycf23av24nscwk0da7rrfaa5wq4l8hsehs90ftlv"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"]],"content":"Closest nostr ever gets to X is reading the comments on an Odell post regarding a BTC node implementation ","sig":"a11c51032c3ef6a37c5b65ee003e4e94b0ed65a9fa4e30a113aa5dc3707633d499e1b1f404f1d434df49f192e8207a883acc99acf873542f5c300e347ca857a1"}] +["EVENT", {"id":"24cbfd5a6ab7f446bdf1a7cd10e21d1733f6c30c90c5b62662f834ae6a6b64c4","pubkey":"b7f85b81e45060309dcf0758e306428988a639e6261eaf88692d6369f3a8d765","created_at":1761520166,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"]],"content":"Spoken like a true British","sig":"ada0448e70b8a9c7783bc29d5aa0917f21cb6451c95fb7eb7f70fb8ba969112d5ec636a7a10e154d21df068533ce9cf71a6de380867a683900a09101f73ec62d"}] +["EVENT", {"id":"34e9de93ab14073c3c7a8208f546067695bb8febd6b290f9bbed96c15d0a768b","pubkey":"d599eecdd22afcb162ef59270dda542759b6ee414252305f16eac21ef1a09b85","created_at":1761520100,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","a7eb078681a447b0546b22b432f20c22a050aec92e5f96c59eafd80d1d200008","wss://relay.primal.net/","reply","165b9d37a28903103af9ad24036bdc54270882c01add515791fc9666167cfac1"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["p","165b9d37a28903103af9ad24036bdc54270882c01add515791fc9666167cfac1","wss://relay.primal.net/"]],"content":"Have link? I'm too lazy to look it up but want to read about this...TIA!","sig":"634a60f63c1fdcb04769ced517b8b343bb2470a3f04f105a76b845a4c7dd0da418211128643dc1b67f6af3eb4a4204c69862b14918ba914fd8d82d511e40cb05"}] +["EVENT", {"id":"5872ac84c570db173419a07d36185dc0e852151379f2cf977fece826cbefa30f","pubkey":"165b9d37a28903103af9ad24036bdc54270882c01add515791fc9666167cfac1","created_at":1761519898,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","f728d9e6e7048358e70930f5ca64b097770d989ccd86854fe618eda9c8a38106","wss://feeds.nostr.band"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","bc367630aae0af9ae390bcbf08c511ce105c186268da21374a2df44031f1da73","wss://feeds.nostr.band","reply","f728d9e6e7048358e70930f5ca64b097770d989ccd86854fe618eda9c8a38106"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"Says the guy who wants to host childporn on chain \n\n","sig":"b77f53196804a492911477aa533b0a59dbc020bdf9c7e9a74af795cfd3853d292d8d0401343edf59ea2fca9da4b1176903a15f4e81b334a5c7169500c644cdb3"}] +["EVENT", {"id":"a7eb078681a447b0546b22b432f20c22a050aec92e5f96c59eafd80d1d200008","pubkey":"165b9d37a28903103af9ad24036bdc54270882c01add515791fc9666167cfac1","created_at":1761519876,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"Blackrock are behind the core 30 release. \nLook it up. \nThey have been throwing shitloads of money at core devs to influence this. \nThe point is to centralise the blockchain, make it unaffordable for regular people to run nodes, so that Blackrock and financial institutions can take control of Bitcoin. \n\nCore Devs are compromised selling their souls for fiat. \n\n","sig":"23b55dd8bd0e9d0a2e264fb12eebc194687ffd00b1a52948f7e139be414233f005c0622e59f8d2ce4333859e51fd2e10384c860cd8f4e36134c7c216e9b295db"}] +["EVENT", {"id":"c90ca1357d43107422c7798bfadd6e2b010cf330d7a9f67992b24dd0bccdfa64","pubkey":"165b9d37a28903103af9ad24036bdc54270882c01add515791fc9666167cfac1","created_at":1761519760,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","ee6ea13ab9fe5c4a68eaf9b1a34fe014a66b40117c50ee2a614f4cda959b6e74","wss://espelho.girino.org/"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","beb732c0f7448c8afe470f1c8626cf02ecc1ad868184b2278a30d54f7251b54a","wss://espelho.girino.org/","reply","ee6ea13ab9fe5c4a68eaf9b1a34fe014a66b40117c50ee2a614f4cda959b6e74"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"Its core Devs pushing this fork \n\n","sig":"41d73da449a4dec5e8530d353ec5f8ef30aa8cecda2514d66ff9f00bf6b82640a9ca72812d4621f295e7edec2eab59d95ebbbea0cc3b61f8eff374b1488684c8"}] +["EVENT", {"id":"38f16341d6b1a1225994b1810f7a3c62153eaacf27bb643dc2d6b870819b5430","pubkey":"61066504617ee79387021e18c89fb79d1ddbc3e7bff19cf2298f40466f8715e9","created_at":1761519420,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"the more time that goes by, the more it seems like something isnt on the up and up.","sig":"3876c65c392a529978c8fb584992b13160649ffd94df9635afe0b4b626985150e4170e1b2e2d7640cdd21deb42d46b06ad4cf31cbf2e1d68dc2b660544ed976d"}] +["EVENT", {"id":"a4557aa997e2952f26b8ef7389c34e5c66f141c75eada8c00c5e6e575ade55f5","pubkey":"77f56243a824d22573fb755dd52c73c14986d15c0c98512d45f4deb08e9f879a","created_at":1761519368,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["imeta","url https://i.postimg.cc/NFzYVx30/soft-fork-2.png","m image/png","x ebc0c19d1f210b2d99a93641bc37e5d0817096f30758cba3710388e0b97bf99d","size 39516","dim 926x233","blurhash 825h[D~qt8RjWBayM{Rjj["],["imeta","url https://i.postimg.cc/pdHvgr67/attorney-quote.png","m image/png","x 209eafbe0296f8e4f39f1f9ac9e33c8cf8715b3d4b81cfd93427df03cbc0ebc7","size 53325","dim 774x303","blurhash 835rZC?cs$jCMaV=MuV?I8"]],"content":"The full text for context.\nhttps://i.postimg.cc/NFzYVx30/soft-fork-2.png\n\nAttorney quotes:\nhttps://i.postimg.cc/pdHvgr67/attorney-quote.png\n\nIn BSV authorities were involved in the csam incident.","sig":"e36650b8a04fdc9a3d0b4bfa5e72b5f9bbeb74f71d3c914f7649c041bf34c519d0c784412c061066a463591ba5a226a7e0e7d6d141552cda15ae380d7796323a"}] +["EVENT", {"id":"431116c2cdb5f6bd0209e9c09623e4a860198721b81c6551a3787140ced506c3","pubkey":"d68cdf0fbb490c42d98f8cf8594f7629790c2e04d5562a11a766b2ed50514026","created_at":1761519250,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"What's the context here @grok \n\n","sig":"5269b72282301c6e7bdf5771c5649513e6718f8a81ab565da719221d7b5ce6a6dd96861700d8be44ead114ac342b21f15c8ada71bcdf8cc295eae017f8e1e819"}] +["EVENT", {"id":"00c8438732520eb44eff6ab8d5e85a271a1f89b24594422499c6a1a2704d53ed","pubkey":"13cb9f915251404603a2ac5c41805b5a4de57f630205a359ffd95ca11739b133","created_at":1761519236,"kind":1,"tags":[["p","13cb9f915251404603a2ac5c41805b5a4de57f630205a359ffd95ca11739b133","","mention"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","f8dd7fafe4d4ea0c8eed302b8a642f0ae86b2cdcd0666cb31ba2ee68759780d8","wss://relay.primal.net","reply","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"Dude you are just influencer and VC.\n\nAnd I am ok with you being Luke 2.0 as long as you have BALLS to call out shitcoin core and core devs. \n\n","sig":"0893da77eefc5a2cda82c9e26933f56e6e91e60acca6b0e5ca6dfde6191a6952ccdc6c0821edb4e290bb9fe5e36082be8b0c6f8552a18f9e1169a0ebabec6266"}] +["EVENT", {"id":"ba9735ccbbd18d0cd3aee053242228704a79f50b4d34d1cb8d4c0ea7aae11e60","pubkey":"29fbc05acee671fb579182ca33b0e41b455bb1f9564b90a3d8f2f39dee3f2779","created_at":1761519066,"kind":1,"tags":[["alt","A short note: Yeah? Nahhhhh. Yeaaah. "],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"]],"content":"Yeah? Nahhhhh. Yeaaah. ","sig":"6eb62ade3af3a4dc0c214a9e677bfad00b72261c0701bb64ce8ac3e814da8eee84609285e72775cfcbd2b92fb77a094bfc8745c712e32ff0bfb56bd074114154"}] +["EVENT", {"id":"f8dd7fafe4d4ea0c8eed302b8a642f0ae86b2cdcd0666cb31ba2ee68759780d8","pubkey":"04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","created_at":1761518984,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","10bc0e9b76a95ff5cecc5b8315d5938730ac1bca1760ee5cad14424838cb70bf","wss://espelho.girino.org/","reply"],["p","13cb9f915251404603a2ac5c41805b5a4de57f630205a359ffd95ca11739b133","","mention"]],"content":"liar","sig":"252bedbfc50147b7a6a16cfe7c5c23ef0b0bd8f2b1bb192add2715c60adfb9994488bb07e48bf82d8f7d3df2157a0835a0a833d52ca2d9d8ea080ef4eaad505d"}] +["EVENT", {"id":"04f4d13a5fa7174c83e161a13e4bc37721670df11312197b5c8075689e622d5d","pubkey":"df5b213d8eeda721796ba42cd7ea6cfccadb99ba2b382e2c089d8ed8b08a9ba1","created_at":1761518775,"kind":1,"tags":[["alt","A short note: The first word of your bio is certainly accurate ?..."],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","10caddb9d95d4f5aa69b026db2b11ca45ac6a7085806296db30cca8c5c4d198e","wss://relay.primal.net/","","493a5a07be1dc06a1dd2d8e943d8924fd469f057857e26f7b99ebdebe00c2557"],["e","3fe6548807dd650a886e91c0512a91aba09226b6f97a95762342ba35e38936e0","wss://relay.primal.net/","reply","aab93e8e3fa6a8974e1c1f3199e5f3d9afb7aaa70b8236e93a5b2fafeafcbd3a"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","493a5a07be1dc06a1dd2d8e943d8924fd469f057857e26f7b99ebdebe00c2557","wss://relay.wavlake.com/"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","aab93e8e3fa6a8974e1c1f3199e5f3d9afb7aaa70b8236e93a5b2fafeafcbd3a","wss://relay.primal.net/"]],"content":"The first word of your bio is certainly accurate 😂","sig":"7ada03c85f4a063b0b9bc43b518263a09165de938cff57dd43d06f6d7f6276687088b5d4602416ba5a696a537c5f4b178b1fee060a2bf1517022944e2b96f6bc"}] +["EVENT", {"id":"f511d424e1e6a5f8c6c5fa426ddea9c6b81ef1f5d196b034dfe02d966ab43109","pubkey":"3dd84213c37adf6b769c8b422ba0439d4cc4d1a835a4c24587337d619c046cc0","created_at":1761518412,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","493a5a07be1dc06a1dd2d8e943d8924fd469f057857e26f7b99ebdebe00c2557"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["p","aab93e8e3fa6a8974e1c1f3199e5f3d9afb7aaa70b8236e93a5b2fafeafcbd3a","wss://relay.primal.net"],["e","3fe6548807dd650a886e91c0512a91aba09226b6f97a95762342ba35e38936e0","wss://relay.primal.net","reply","aab93e8e3fa6a8974e1c1f3199e5f3d9afb7aaa70b8236e93a5b2fafeafcbd3a"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"That you, Vitalik? \n\n","sig":"927478ccaacf9f19bc54ccc9d9f749e0aa6511aeb077878a4e463dde6bf8c3d146498f96409f5cf41a21f1dcec1030723ce73c58358f63ff3678febbadd46637"}] +["EVENT", {"id":"2fe46947f1decc567b23f24a083c2b80ae679a7c1872c5f7339ce765ec646a2e","pubkey":"3dd84213c37adf6b769c8b422ba0439d4cc4d1a835a4c24587337d619c046cc0","created_at":1761518311,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"I want Bitcoin to end central banking. I'm less certain that's going to occur if Bitcoin is a jack of all trades.\n\nPut simply, all this other shit muddies the water \n\n","sig":"28a20beb56da06130ea42054fbcbf745421f7609d87e2c439bcb6335d301e2d0983df609c14fd1f4322935683d2e2b078cb2d2b0dc9ebdc421b3b2cfa67f852c"}] +["EVENT", {"id":"0dcb00f51c3d06fb85518710cc7de64daa8161ee9afd389828ab2660a9ea509a","pubkey":"c3a4a30d7467275053cafcd8ccf2cc700ad561dd61675be4c66621df307162ce","created_at":1761518255,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["nonce","5","4"]],"content":"Its over the top maybe, but could be technically correct. Better to err on the side of caution. ","sig":"58c2020d56f0e14c11be3feb51359225a791ae9aa6efb86e743addf797bc5ce0dd92fb65b928950c715d3f2f80855de8ddd764dda7f48bd7bff47992ebf45117"}] +["EVENT", {"id":"000007b628f5449b6f45d46c6566c08fc1b4a373c0b7fde6acc50535f71b44d0","pubkey":"b9903f77417392e732de6e71a24516cf8efd8f54f7783774b305a3675f330a35","created_at":1761517964,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://nos.lol","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://nos.lol"],["nonce","65129","16"]],"content":"kind of an evergreen statement. rejecting or accepting code being executed on hardware you own and run *may* subject you to legal or moral consequences always and everywhere.","sig":"4b39e8c5ae9462210fecd92fe41474b607efc1147a478202b36c16c45c545840528d01501f2b28e212ed12039851cf0e05148427b6c8b72e906ca4621f30b93c"}] +["EVENT", {"id":"dc733cf4fb77ebd1ea8a8800ec62c1a09b04eb03bd49d01aa273a8dce73737c7","pubkey":"aab93e8e3fa6a8974e1c1f3199e5f3d9afb7aaa70b8236e93a5b2fafeafcbd3a","created_at":1761516823,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","1408bad049bab8a38b976075affe413c3521bbeef62cc4ce3555299f4971f2ca","wss://relay.primal.net"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","a072c07ed3c574859db1fd3d4df1cd630ed1cbc4d2f9a34667a331f83ec38c25","wss://relay.primal.net","reply","1408bad049bab8a38b976075affe413c3521bbeef62cc4ce3555299f4971f2ca"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"Luke is technically a core dev. He even contributed to core 30. \n\n","sig":"042042c19cd75f54cce74297360f20404d0aeb9b542a2dd247e4f72e8f89bc1fc84eed817ed82f277f974edff19a3171c3030769d40e91ad9cc3f62c6b1807eb"}] +["EVENT", {"id":"10bc0e9b76a95ff5cecc5b8315d5938730ac1bca1760ee5cad14424838cb70bf","pubkey":"13cb9f915251404603a2ac5c41805b5a4de57f630205a359ffd95ca11739b133","created_at":1761516782,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"If you weren't CORECUCK then you would have got the context behind this sentence but anyways thanks for showing your true color.\n\nThis is exactly what a lot of bitcoiners were expecting from you. \n\nSince Nostr is still a circlejerk of few Bitcoin simpfluencers, you will unlikely to get deserved heat for this but at some point, plebs will realize who you have been siding with in this war\n\n\n\n\n\n \n\n","sig":"3f1ee94a150f51b8cb2ce129e211f7e155abe215cfec55ca3ca87a71f200fa92cc9ff32c90ea9cf3266580b3a7374c3da296184593acbdfc9b426b3a57a58592"}] +["EVENT", {"id":"3fe6548807dd650a886e91c0512a91aba09226b6f97a95762342ba35e38936e0","pubkey":"aab93e8e3fa6a8974e1c1f3199e5f3d9afb7aaa70b8236e93a5b2fafeafcbd3a","created_at":1761516773,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","493a5a07be1dc06a1dd2d8e943d8924fd469f057857e26f7b99ebdebe00c2557"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","10caddb9d95d4f5aa69b026db2b11ca45ac6a7085806296db30cca8c5c4d198e","","reply","493a5a07be1dc06a1dd2d8e943d8924fd469f057857e26f7b99ebdebe00c2557"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"CSAM is not a serious issue. It's a slippery slope exaggeration born from Luke's delusional imagination. \n\n","sig":"61f46f5f7f758bf39741d36031b31be5a4e84d1c443128cac07ff6e71bebecee9ccb125b74c3d60fc5d151cb1a560cfe5df42fa986d60d118d41b3ddfaa76321"}] +["EVENT", {"id":"3f947ce25f59079f15e1f19f71433753073a0371123d72ffae3d2ebc1142902e","pubkey":"a5796ad50e34c3d5627e7803fd195beb813faa3869f3d1774cd6b9ff19ca9a5f","created_at":1761516674,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"]],"content":"I have a vagina and I’m scared. Sike!","sig":"67360860a11be0bee0df0d8152579422b9bb01bd776a1d1bce04f34e4a2d8d4b5d60e40ac27d2e6c7261aac5abcb5a2d04cc91058a10ff4ec930ca3f78ca3421"}] +["EVENT", {"id":"2eb0db3dd4b2ed493405a551b4b1bf0247318518d88d51c14b8d8c5ee3780bdd","pubkey":"aab93e8e3fa6a8974e1c1f3199e5f3d9afb7aaa70b8236e93a5b2fafeafcbd3a","created_at":1761516661,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","f985d309197c805e1719c73185b574fc3ee407d7c1b6157dee99c6ace2599bbb","wss://nostr-pub.wellorder.net"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","3e27e90ef3d18044b33b6003e5c253c7c77888cb1768f46f7e8974f41cd3f7ba","wss://nostr-pub.wellorder.net","reply","f985d309197c805e1719c73185b574fc3ee407d7c1b6157dee99c6ace2599bbb"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"It's his god complex and delusions around CSAM \n\n","sig":"9e229796adf49cc2608c10b5693e30b680efbbc57959593301288c5e34c45f6dc0541356237b6de76d4960102fdfae5ba16ecc30b8ec33d1d0f9c8c80ed0dc5b"}] +["EVENT", {"id":"0c5b054f56f657d7740f6a11e2d19441b7c5a693433762239fde72a782b16933","pubkey":"f75ac498d3e042b33f097a82bd4fed8e1ffe63be30a14c8b8e6fa8640d90ab2e","created_at":1761516549,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"]],"content":"The concept of this effort is fucked up. If you can change bitcoin and reorganize to remove content you can change bitcoin and make it impossible to use without KYC. This whole thing is an insane suggestion,","sig":"dd29cef42c444a1b831f6fdf5c91a74aa2e46ef83e0a8d6fb5244ae44e3731cc687b0b9f11ea697f355ee7d13cbb4494cf199044df92b645c61f9c180a4b4a06"}] +["EVENT", {"id":"cbef2dbfdefe18cafc91f47f5f1dac32bbfa2d3b10a05c069288bfb4aab7c3fe","pubkey":"3dd84213c37adf6b769c8b422ba0439d4cc4d1a835a4c24587337d619c046cc0","created_at":1761516342,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","f728d9e6e7048358e70930f5ca64b097770d989ccd86854fe618eda9c8a38106","wss://feeds.nostr.band"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","bc367630aae0af9ae390bcbf08c511ce105c186268da21374a2df44031f1da73","wss://feeds.nostr.band","reply","f728d9e6e7048358e70930f5ca64b097770d989ccd86854fe618eda9c8a38106"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"https://blossom.primal.net/c2e797e2c6973620ec3039f809174d7ef7cdb336b37fa146487b8077a6514905.mov \n\n","sig":"51c5305b607c4fdc275b07b0ed81b938ae569df24d6650642ea111eb0b113fe19b2d58175ccde51842a7ef3d0da89800cc85d6b11b4aa5709665846b4ded91b5"}] +["EVENT", {"id":"10caddb9d95d4f5aa69b026db2b11ca45ac6a7085806296db30cca8c5c4d198e","pubkey":"493a5a07be1dc06a1dd2d8e943d8924fd469f057857e26f7b99ebdebe00c2557","created_at":1761516204,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["r","wss://feeds.nostr.band/tony"],["r","wss://nos.lol/"],["r","wss://purplepag.es/"],["r","wss://relay.primal.net/"],["r","wss://relay.wavlake.com/"]],"content":"CSAM is a serious issue and it took down BSV quite effectively. \n\nCore V30 is BSVing Bitcoin. \n\nAdmittedly, the best way to handle this is to target the word \"images\" or \"non financial data\" to include messages.\n\n https://blossom.primal.net/6a88ee408b70f7616d2652f5a8caf3be16d056a816d8eddd1ee594c53148501d.png ","sig":"418556647132f0c0906a7e189702e3113c2ae3acb58936ea6bb44b73cec46f0d2e14d3471d0e3b828237149e1700847f27c4229329b973aff6eb8dd4b4fba935"}] +["EVENT", {"id":"dffab30217bd426550f46eb608995cfc4ac343130a3337d1d64b9543bad87c75","pubkey":"df5b213d8eeda721796ba42cd7ea6cfccadb99ba2b382e2c089d8ed8b08a9ba1","created_at":1761515946,"kind":1,"tags":[["alt","A short note: Lots of disingenuous core proponents phrasing this..."],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"]],"content":"Lots of disingenuous core proponents phrasing this as a sort of threat. \nTheir opinion is that running Core may soon have some pretty important legal implications that some may not be interested in. \nPerfectly rational disclaimer if youre of that opinion. Bad decision though imo","sig":"63d5f63c600b5484e3a7b0ddd911b8ad00c810ff982dc1c67d6234eebbb9791b6e71b92b6b26c2b8639a7558294d978643033c54c0c6dce398dd8f901b69fb68"}] +["EVENT", {"id":"0c07d91939489e596cf69346c92860a77a76938418e219b30caf6b1f40119c13","pubkey":"9a2fe7b81de91641d61d02c8e4df269e93629ecc9894789978e973f01dbe49f2","created_at":1761515762,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","a072c07ed3c574859db1fd3d4df1cd630ed1cbc4d2f9a34667a331f83ec38c25","wss://relay.primal.net","reply"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"],["p","1408bad049bab8a38b976075affe413c3521bbeef62cc4ce3555299f4971f2ca","","mention"]],"content":"🙄","sig":"8b0ba7066edb1d4e8dc279562dfb20426327d89910fbd4f74bca4f6cb27a5633bf0968275ea0ebe569a7f4d440fd34bde41b18f3a682dfd73aac39d967b291f6"}] +["EVENT", {"id":"aa1799ddccbdaad0fcf1dbe4da16b595286c0679312c49586e508e69939dfb2e","pubkey":"41de1771a0318a3d9b9af9ff1a0c3c7b906867ca6d4e5d4335a8bf960b1d3243","created_at":1761515649,"kind":1,"tags":[["alt","A short note: someone clear something up for me... aside from wh..."],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"]],"content":"someone clear something up for me... aside from whatever is inserted, shouldn't we be against bloat? how big is too big?","sig":"446a4f7c3ecfcbde2bc48639d041987f62e71be7edd2f74c7ffb1f98f5d20d8aaae9d09fbfb03963cc69a676a45fff759cfc6faea12de96d194eabd63313b9b2"}] +["EVENT", {"id":"a072c07ed3c574859db1fd3d4df1cd630ed1cbc4d2f9a34667a331f83ec38c25","pubkey":"1408bad049bab8a38b976075affe413c3521bbeef62cc4ce3555299f4971f2ca","created_at":1761515547,"kind":1,"tags":[["alt","A short note: Looks like something core would write lol"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"]],"content":"Looks like something core would write lol","sig":"54b458a0cdd79527d02bb9c346a6f1bf3ddef970d612195443470c0d96d2acc2d2e56e1a9ffc434a6311a828ae17e8f4fbc8a1e21db11deb104e362a0c341bd0"}] +["EVENT", {"id":"32d1bf607e96c9536b35d23419ec770d3c0c83cdc0cc1b2b047021d8aa0729cc","pubkey":"b44af6ad81ce38fa22c80ed8c296b4926989f5a17bb013b5bc8970ae6689c532","created_at":1761515231,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","mention","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","fad6540c8f2fd2a16a25d0d82dd95d3bad7890d435d1690848a0a77d2883a447","wss://nostr-pub.wellorder.net/","mention"]],"content":"nostr:nprofile1qqs044j5pj8jl54pdgjapkpdm9wnhttcjr2rt5tfppy2pfma9zp6g3cpremhxue69uhkummnw3ez6ur4vgh8wetvd3hhyer9wghxuet59uqjvamnwvaz7tmwdaehgu3dwfjkccte9ecxs6tvd9cxxunfwd6xjctwduhxxmmd9ufzexy4 \n\nnostr:nevent1qqsdgjkedjufysyj5a4u9t7aavfwhpfr8sxs8f7e4hzzc25957dyxpgpzemhxue69uhhyetvv9ujuurjd9kkzmpwdejhgq3qqny3tkh0acurzla8x3zy4nhrjz5zd8l9sy9jys09umwng00manysxpqqqqqqzfukknp","sig":"f3b143cc21acbe7f8fead9a3064756ecb061731bf753ddcb22ab9ce77e83bf0771c5bd7cd7ce79b8b5c2f09fca5a1aec3426347e0e215642ad74e02c6c4e109e"}] +["EVENT", {"id":"beb732c0f7448c8afe470f1c8626cf02ecc1ad868184b2278a30d54f7251b54a","pubkey":"ee6ea13ab9fe5c4a68eaf9b1a34fe014a66b40117c50ee2a614f4cda959b6e74","created_at":1761515112,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"Knots crew has lost the plot. They should just fork all the way off.","sig":"309e5a731cccb1ad66255f9905a1279a943d1b5980ea173623894adbc3024536b6f1896c2debe29d102399300431fce5bfa216e0099fd3e46369d116ae7273ea"}] +["EVENT", {"id":"3b7059ee78b7b90670ac02cf67c4ad842b024f46f46549247e3ab1aa58058bee","pubkey":"e96911258fabb7c235ffbb052c96a07bf91f3ef91cfa036e4f442fd23320261f","created_at":1761515058,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"]],"content":"Like nvk says. bitcoin is anarchy some people can’t handle it ","sig":"1e3b82bcc0ea1bdf6584cb3add5251cd9a8ef376715e1de42e4bbfa718360757a2a75d9f0f676b0cd0f2b5f5e0cea494e68559f0fa3752221798d60aeec35a03"}] +["EVENT", {"id":"bb0df397154ce64e10cade9768f1297853473516d25355c04ede611f13f4d4c1","pubkey":"080ba648c4a96fb2d85610c4cd2135a8f8fba73850c5034647b96346eb9aee20","created_at":1761515000,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"Sanatory level shit \n\n","sig":"afc8ce4d50485fb02907c65234e603caadcd1fc9e71c0ae55717893d2a81d81852d2f00988d28a68f69859e9f35fe3b011d8de121e899fef10cecfe2d8b73aeb"}] +["EVENT", {"id":"267a7c8c8fc6d8b7dad972e07d457d60560ac877b014eca0c19bc496b2a9e299","pubkey":"edb470271297ac5a61f277f3cd14de54c67eb5ccd20ef0d9df29be18685bb004","created_at":1761514880,"kind":1,"tags":[["alt","A short note: They have really worked themselves into a fever"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"]],"content":"They have really worked themselves into a fever","sig":"f117e6e995cc5d0e14a931f91ec59ded5b0347d015dcf26037dde3a10bd7303aedd4dce8bc47aaac1ef0138aa2f6088f04a318dec9eb5091a52afc0e598f678b"}] +["EVENT", {"id":"bc367630aae0af9ae390bcbf08c511ce105c186268da21374a2df44031f1da73","pubkey":"f728d9e6e7048358e70930f5ca64b097770d989ccd86854fe618eda9c8a38106","created_at":1761514859,"kind":1,"tags":[["alt","A short note: Not to mention it literally creates an attack vect..."],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"]],"content":"Not to mention it literally creates an attack vector for double spending, making it possible for anyone to trigger an arbitrarily long chain reorganization.","sig":"0f7fe9c426a2328dd80002f8ca35ab58eb017e2c94f06caef467a5f94de7be1a393ea30050e79b21f153088ad09e75863305fa07d48c1060373a6ed4d6f4bef4"}] +["EVENT", {"id":"2c884336cf616b2317798f87127db7b4aead20b77e63ffee5c9e9ed3449c3641","pubkey":"867d7d8f08f454618ff50ec981de1a66bac5149d88b138589a0861789ce5b04f","created_at":1761514852,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["imeta","url https://blossom.primal.net/16daa76d1469a9c828bed7c8f5c36c38654bf7a724094822ced54ea03823ea39.jpg","m image/jpeg","ox 16daa76d1469a9c828bed7c8f5c36c38654bf7a724094822ced54ea03823ea39","dim 736x551"]],"content":" \n\n\n\nhttps://blossom.primal.net/16daa76d1469a9c828bed7c8f5c36c38654bf7a724094822ced54ea03823ea39.jpg","sig":"daac3ae235863238c63a5021f0c78e3ea3bbb602a63a60241588eee7aa0afce4a9d98c0b576f5e31e871fdb13b0fb8f021a82b6293be9ab69813c4ae37aca669"}] +["EVENT", {"id":"3e27e90ef3d18044b33b6003e5c253c7c77888cb1768f46f7e8974f41cd3f7ba","pubkey":"f985d309197c805e1719c73185b574fc3ee407d7c1b6157dee99c6ace2599bbb","created_at":1761514846,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","","root"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","","reply"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"isnt this just Lukes authoritarism?\n\nits not based in reality afaict","sig":"e2b3e6ea99beca48846ce3e4e65cd4396e3203e7ce5422396c9a275e92ef8d84a6a258b8f2b76ec9a133457d9cc5e4dc750311670fb746e7b408e39922dd37d6"}] +["EVENT", {"id":"f4ff1a6aea5a6e4d2b4aed5f73e6ea7dfc5addc68a63046e1a60c4d4ec93a783","pubkey":"460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c","created_at":1761514802,"kind":1,"tags":[["alt","A short note: Gotta love when devs talk legal."],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"]],"content":"Gotta love when devs talk legal.","sig":"2767ede61f8bda2e2b88dd75d9383dcf3e4f90ef0ed826ee481464dae06de9375fddc955b9aa0d8b47bb522ad1b56c54d7953f8406872e232bc0051dafe73dbf"}] +["EVENT", {"id":"002a6cebae66770f4f52ff89d98212852cb72c9ced189107d0c6b4531e21776a","pubkey":"af92154b4fd002924031386f71333b0afd9741a076f5c738bc2603a5b59d671f","created_at":1761514794,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://sendit.nosflare.com/","ODELL"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://sendit.nosflare.com/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["client","Coracle","31990:97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322:1685968093690"],["nonce","506","10"]],"content":"Run Knots or suffer \"moral consequences\"? 😵‍💫","sig":"5892eb19d4e0f5815bf85b2a642c5f137a14c0980eb6281790c0a081136840bc6a48fd69a6da381ded7e36064a6bf3a6b66ef20f775630e46bc17845040c8ac3"}] +["EVENT", {"id":"3d0eb59d46fd3a2007da9136915cb796d6c20d2786edb2b3bb83457f38030309","pubkey":"aab93e8e3fa6a8974e1c1f3199e5f3d9afb7aaa70b8236e93a5b2fafeafcbd3a","created_at":1761514776,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"Yes, 💯 fucked up. Delusional. \n\n","sig":"d0836d60d55a89083da3014fb85290ca365a3e6296b944d38135bbcdb5e312b034cb7f29aef1483f46239fecfbac0239d4a29ce066db6296512a1019c2bb53bc"}] +["EVENT", {"id":"0b8b5f8dfb327f6dfbca88ec5c69d36d8335cd32ef33ea550efe0dbd2d3e3dc9","pubkey":"2556b6fe7991a73d152ff3ad084ee591dcb8d4326622eac9fe1a04ffa7a4ff58","created_at":1761514754,"kind":1,"tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"if logic fails then dragging people into fear and morality is like exactly what politicians do.\nstay away from them as far as possible. \n\n","sig":"0adb078a3280a78166cbf95430bba0c403980853ccb03b38bf6cfb66b72f2c1372d00dd86387ae0bfcda25d793a4037da1679d80d6882cf810c3fa65dc7a4e95"}] +["EVENT", {"id":"0024acc8f5854b3a53dea3233aff6c5af942ea0d0ba47fb6e558c593e9c6bde1","pubkey":"af92154b4fd002924031386f71333b0afd9741a076f5c738bc2603a5b59d671f","created_at":1761514721,"kind":1,"tags":[["q","ad1ad8cceec723f561424d984aeee90c3e95ae5fb3e18b81013405c8405b67c0","","e8d67c435a4a59304e1414280e952efe17be4254fca27916bf63f9f73e54aba4"],["p","958b754a1d3de5b5eca0fe31d2d555f451325f8498a83da1997b7fcd5c39e88c","wss://pyramid.fiatjaf.com/","SerSleepy"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/","ODELL"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","root"],["e","8c88d5d84f60e0eb027abdd89eaa7ffce0b1d7468bae6189cf6aa4946581cb26","wss://directory.yabu.me/","reply","958b754a1d3de5b5eca0fe31d2d555f451325f8498a83da1997b7fcd5c39e88c"],["client","Coracle","31990:97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322:1685968093690"],["nonce","24","10"]],"content":"I think it's part of the long debate in here: \nnostr:nevent1qvzqqqqqqypzp6xk03p45jjexp8pg9pgp62jalshhep9fl9z0ytt7cle7ul9f2ayqqs26xkcenhvwgl4v9pymxz2am5sc0544e0m8cvtsyqngpwggpdk0sq0xwqdc","sig":"d67f0879ab05ed826d39c746fd5d7a2e247aacd918e68736b520664453e20f5851b2afce3372182f2ef2de70b5006633a382f88b96a26d2a67f534714a19f7eb"}] +["EVENT", {"id":"c8bf3aa7b1244cfa730007c7e6162013beed1e5e7a9482be07ff9b802b50f712","pubkey":"4b6025a8fe1145c413e7cee262548cf800eec8d6854a21a1808e0f98e9907649","created_at":1761514533,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"AI got brain 🤣","sig":"2ec50a9d4ac46bc97c55953bcf1b3ec6a8aa6fb60ee233aa1b60041adf759484bebe475c7778d82fa6cab85e97f8c79de0fe161f10cd8b8ce4ee00cd7f57c81a"}] +["EVENT", {"id":"72ae70479ed5fc1d43cbcd228816d199fbb9d51fb0dce52f146387b92210b144","pubkey":"22050dd3659b568c5cb352b0e81958fb986bd941031a90c74ba7f6d2480c11ea","created_at":1761514480,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"pretty coercive...","sig":"3c04f56fbbad9d8d9792b6283bfb62f77620942496a63bdb9d384202aaec0fc9fe94d0d3477cc29c264368a6ec9e34f593db9aaaa4b0d28f7ef824ccca54bc4d"}] +["EVENT", {"id":"a99c22af63828763ff2b67c5f7bd7298f6d0cf2feab42dc4747c44e1ea5c40d0","pubkey":"d5805ae449e108e907091c67cdf49a9835b3cac3dd11489ad215c0ddf7c658fc","created_at":1761514465,"kind":1,"tags":[["alt","A short note: AI prolly"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","8c88d5d84f60e0eb027abdd89eaa7ffce0b1d7468bae6189cf6aa4946581cb26","wss://premium.primal.net/","reply","958b754a1d3de5b5eca0fe31d2d555f451325f8498a83da1997b7fcd5c39e88c"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["p","958b754a1d3de5b5eca0fe31d2d555f451325f8498a83da1997b7fcd5c39e88c","wss://premium.primal.net/"]],"content":"AI prolly","sig":"30285a1bc1051243e6ccc49280eefd16c08003e514b1370127387217f46f9278a71e145c06e3f6f7d3adeb6cfed667b3a076196d3304c80ce3b1d27ab10848b8"}] +["EVENT", {"id":"42af5910c095ececde454e7f53550dc887f7beb026ab83f2e57155334a899ab2","pubkey":"13b42c1bbc81aad1e28b3e87c8e1ac818444902dd5f6ff0582632114cf750fd6","created_at":1761514443,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"]],"content":"1) what","sig":"33ed0dd522cb3dc9410e37378e1d43f8dc63f468ae8fc409454da6ebdd6a888f77bdf095ced829a03b056ece65e9757f0c0fa698b82a92f4a34e9f78a21da051"}] +["EVENT", {"id":"8c88d5d84f60e0eb027abdd89eaa7ffce0b1d7468bae6189cf6aa4946581cb26","pubkey":"958b754a1d3de5b5eca0fe31d2d555f451325f8498a83da1997b7fcd5c39e88c","created_at":1761514440,"kind":1,"tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]],"content":"I wonder who wrote that","sig":"bfafd5a404e54c7e1f543003195635948d0c9c2520a45da3d0a0af627004870f4a78ab1241a0bcb23450c25621a5852ddc7c0716077089cbc1f79db08dc63aeb"}] +["EVENT",{"content":"🤙","created_at":1761601463,"id":"cf23e8398f3db64f7615282fe2f392789d6ecdb21c7fb10df02615ca7a8b5442","kind":7,"pubkey":"16897bfab409ff12a768217e14d472dd5e8a2ae11cc0e3340bf2d6f67f5bac83","sig":"6f1f47f360e54e6c55655d506bcb2f319efbbef471d8a2410fc00a8f31b24423095e464037919c3dbc3287ee2a5984a1c998c1d28c5a2aa884753534f96a7267","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","root"],["e","83df6f171f630d77e3f96c046e810b1546170ddf251ce261690b8525de9ef2a9","","reply"],["p","13cb9f915251404603a2ac5c41805b5a4de57f630205a359ffd95ca11739b133"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","fdd5e8f6ae0db817be0b71da20498c1806968d8a6459559c249f322fa73464a7"],["e","d432efa784aa3740cac93dcba8cd076fc73162a5802e3605ccbf9f21d1b4d47b"],["p","16897bfab409ff12a768217e14d472dd5e8a2ae11cc0e3340bf2d6f67f5bac83"]]}] +["EVENT",{"content":"+","created_at":1761598482,"id":"e1ca1f89c174bad59893bdbd0d11c4bd7898b8a48e9f2ba080a2eb13baef543e","kind":7,"pubkey":"3befbae3754f3da92f9203b0a976a735a2920ea71b0ed85c45a20b0bdeacd66f","sig":"c16140603974da4ce32442436a35c9ca14d0cbdcf4fdec224895891c274f195bafaac0ce18ae629f52835dc0550bd5185235227040fb301fff62fe986d2f2316","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"🤙","created_at":1761598465,"id":"0a490668d04e6769f6f3623790b3b6d10711bd003f7afd8c7c28ad72def47bf0","kind":7,"pubkey":"16897bfab409ff12a768217e14d472dd5e8a2ae11cc0e3340bf2d6f67f5bac83","sig":"2fee405a86fbd19a6f2cc4e6093abcdca931f46372e23e39201d66bbf13ffd5d4429e4225cf9438fd25179300c89355d6be3eacb1c22580404196ec464c144dc","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","10bc0e9b76a95ff5cecc5b8315d5938730ac1bca1760ee5cad14424838cb70bf","wss://nostr.wine/","","13cb9f915251404603a2ac5c41805b5a4de57f630205a359ffd95ca11739b133"],["e","f8dd7fafe4d4ea0c8eed302b8a642f0ae86b2cdcd0666cb31ba2ee68759780d8","wss://premium.primal.net/","reply","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","13cb9f915251404603a2ac5c41805b5a4de57f630205a359ffd95ca11739b133","wss://filter.nostr.wine/npub1vnmhd287pvxxk5w9mcycf23av24nscwk0da7rrfaa5wq4l8hsehs90ftlv"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["e","83df6f171f630d77e3f96c046e810b1546170ddf251ce261690b8525de9ef2a9"],["p","fdd5e8f6ae0db817be0b71da20498c1806968d8a6459559c249f322fa73464a7"]]}] +["EVENT",{"content":"+","created_at":1761594446,"id":"6f915bd690aa6dc94ef0acbba2376b83a118bd7f5f73950053e688f4301aff6b","kind":7,"pubkey":"eb9c0f2cb1d264e8f4023fad907806067ee216eb03624ca0def40096158a5e99","sig":"8026605b604be7530291533bbf781c91b7a10b76af26bcc7a0d1bea88e0685d9edfe517df7b355407fe9ab9935002e151391adbb5fc23097698239593f4ccfbf","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761592649,"id":"cb6e9c840ebcfad4693fe3da9321d6779c40f1e08806b70ccd4111607f12c47d","kind":7,"pubkey":"61116b6a33113eb0e4fea29ea2bc9517a3b7d8211f070ea04e2335c66a61ef2b","sig":"fd5e791a9744204133ee8ba016fa4a2f70f963ab5abe0e785d1a4af754e4f95ad38857c637e7c563abc01f24103274e46702fe56f85e2cea613cc80095d3d095","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761591300,"id":"51f36d83eed01a6c5e99be17797c6700fdf58740f2440b9c29b89d6913aa3bb1","kind":7,"pubkey":"d3af45388825d5eb21bf89b4dcb9262e5f9fd65cba6125e3f2a78ce9e22085d4","sig":"ff85bcdc9350334675fbde768096cd394e145fcb8bcdc7896776a821f1dc70ccb50a6c73710aff5fd2b4b75fb1f8c6b7a2a6a116ea38c87bee2e7481ab2610fb","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761591276,"id":"cd3f6f814bfba94f794d682b39134bae8f586fbe11de4cfbed2cc2019d0c4a9f","kind":7,"pubkey":"b03c82b72e9c973338b532408ead45a4759ca2de81bea4b437e1659c8492a7f2","sig":"f69b51fb02fec6cb24ef307f01cb8aca846c7f83ac48342b875d2949fa03b5719f06c6f32b4497fca08fb1585b91ac26c415aa3d001ad728a8de200956dee1ab","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761591197,"id":"02955bdb367082d4676c8b66ba030075caf79a4459ee1dea7503feac34c99e50","kind":7,"pubkey":"2f5e8886db31b7c648dd2d1ff39765d10187bccba507eaa61da5338e65b71b0c","sig":"062b3d9f698b71155c65ae15d65d02b5f4ca2967778c70bfb2438a51564cd4215527c76c77e0f28a28fe1297c2fd9a47d42a792c15e8ddff88b4cc484bd3b797","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761588136,"id":"7fe890d04e310474bb15a2db7d62b429f776a98660f4cfdd2b6116fd29fc904c","kind":7,"pubkey":"963c328179466ec49d6501fc258615e7984e239d21e8920b632d9bb7d9afb0d3","sig":"acc1fe082ab94b774222efe61598150b97bd9fb9b1a1108930c1c17b43e9d8428ec387ea33a192af73d459ab7c6726989fe3dbe73d606b529fba0d7efdeb6e9b","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761587093,"id":"b744cb5fb6b9bf3c9d8901d71499c3582386a90ce45426465af6723c1d72a591","kind":7,"pubkey":"6c66551547b41817b3f7623179f8c40080ee4f4c3e1dec039e41805d79ec2163","sig":"124336c27e45572162bda13c6bd166aa4610fb07a340515d9b74f94f28d42af7ff0c85b3eb04f2114496f10f1cc0d5fd3732525cf3787184c794aa9d3a2cd1bc","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761576299,"id":"c94dc6972f099963a1a47ed7f2f581c8b73eddeb61c94619471594d8fe509f62","kind":7,"pubkey":"3ec002e564cd283a551ad3dbff5110f46d1fb5563c91e9334781f97491979245","sig":"459f4e313e929b1eb2e362d557a4fc99daca417062da961b03cfa0513607f97216bc6db7029168eaabf76c7d161c58fb1ec080ecf56357650cfcec7aad39c177","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["client","snort","31990:84de35e2584d2b144aae823c9ed0b0f3deda09648530b93d1a2a146d1dea9864:app-profile"]]}] +["EVENT",{"content":"+","created_at":1761575857,"id":"53684e16ae63054ede75994a5858245df9cab93c90a4b1fc110a3a957fe392cd","kind":7,"pubkey":"9437b11227e28ed34a99e7949de9c2d2cd087bebd4b8f0ab0133181f4acf9cec","sig":"8bb9cf3a869aa25689e5bc3d40eb60bbe7a18d248ed6c6749953b412479cd360dc1c91a089bdccb963830f00dfde2b58e978b63d7379e16a24d0fd1470a19435","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761575571,"id":"596d4b0c2c9373f26fefb1cf59fa5c8b12bc881a8fdf874c9dde3047aec47a39","kind":7,"pubkey":"2cdeb9aab4913f1d59c5c882c9847712ce05bc4f7786c09e28b4a562e72b8503","sig":"5ade930b30b8af477449ced83521c2cc948eab6c02b22a47a1afa84c1147b79dffae9bed38873ebc34bd654d7bc100f860c34309a0e93ecef73060ce36e129ff","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"😂","created_at":1761570692,"id":"84691530923d8865e2fe0519b46e326daf1086295e2c8cbb1e74cca89a574660","kind":7,"pubkey":"3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24","sig":"f9b87761eae5aa2ef9fc45cdf374766ccdb5dea1e6ee44ba0f876190805e3f252c6d92d580bfd193a4032b9acb59c0141597da577a28921a84c9585aa7ada7e9","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["k","1"]]}] +["EVENT",{"content":"+","created_at":1761570301,"id":"e4ecabc1b81e23ee64f82359ea5f80e65ea41ad71f38c3618a5addd8c473f063","kind":7,"pubkey":"b9197a4cefd2a11c898406ef2d893f197ea7c11aafb66af073f4c2678b88a3c8","sig":"f2b28cf94d1e831563808b1b54788785eb9b427dd8d66dd8ff9a33b9b4ec62ea93a3f4549bbd1bdeb800f8001d138b1ca0b8e93d2dd8fec689a83db5405d9a26","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"💯","created_at":1761570254,"id":"9ee74e13f3a59cbf41be589f3d77bd6fb2760a31a3e6892e0af0797e89fa8482","kind":7,"pubkey":"3d5e66f4a87dae4e4e1cd56aa9eb7d4316062bc6a19b2891bfa890e7544bd26c","sig":"841ab99e12d3b23423e58f0998ec2f451d4c6c3f7ccc1220ab6576e8912a68e549c01152db92984b373231379d74d7479d65c0dd8c3ea48fe2c1e4d038b6999c","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://premium.primal.net/"],["k","1"]]}] +["EVENT",{"content":"🤙","created_at":1761568490,"id":"bfbda4afecdd1d400a5b373411f015a24e952927020ffb123c782c6675b44b50","kind":7,"pubkey":"16897bfab409ff12a768217e14d472dd5e8a2ae11cc0e3340bf2d6f67f5bac83","sig":"3ba3f0c40a2213e7fd860175bb85aeb23b2946d8378c96fd6f85d0b7ce6245dfe66664b121437529ab5b3eac0809e7c2502adcb173523bdc1f9bd2b404330a51","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","mention","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","74ffc51cc30150cf79b6cb316d3a15cf332ab29a38fec9eb484ab1551d6d1856","wss://nos.lol/","mention"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://premium.primal.net","mention"],["e","caaf49bb907cf7ce7ae42d7621af64ff506d57a2a24dba266231212857927cd7"],["p","13cb9f915251404603a2ac5c41805b5a4de57f630205a359ffd95ca11739b133"]]}] +["EVENT",{"content":"+","created_at":1761567110,"id":"94562f99320e9067a1c231e2612231c3d1e6c7464400a85daf5828001cffd0e6","kind":7,"pubkey":"48e85819c0950e5a834fb23b1d90dbe23e19f0f932f9ffa984f9a29f209d725d","sig":"f68e70aeed559e87ba8e8fb01d3ebe1243c57411bb3331910ce9f74fa5a3e1161a90f716a5c65b562091116ad1fd83810e24c5b6ea2b48f9bd0bd1443d99254c","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761567086,"id":"bc0f25adfd499f20e862052bbc7e2d2d30f7f4186a7bfb40266d0a23186218d9","kind":7,"pubkey":"3319f5048a5d13d019633e555563cefe0e64f68ce0c5f8973ed14b771bcc1834","sig":"f884b8c9685f2f9c2f6f9a0117fa0c1133879e5c324f8863c5b04cee63ff4c0d470448ddbcfd046d791348fa6b77d84e05b3c3fcb446f2c710c1f527935da677","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"👀","created_at":1761566914,"id":"5d1e39f38ef8e11b4fb456f91530d5c1f7f360e079419bdb635eaf2d72f9b1f6","kind":7,"pubkey":"683211bd155c7b764e4b99ba263a151d81209be7a566a2bb1971dc1bbd3b715e","sig":"cae4387bece2d18a79bd74c8aaa0f2be41a9b33c5aa7a32e43cc036f2fa519ba13e1b09cacd4a8419c0e33aebe35f046b2a2f2960c25983dd42e206e29841065","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://nos.lol/","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"🚀","created_at":1761565591,"id":"2a42181e21e9b762c84f701a57a49519fbb2a493be01a9fe626851a8d84a84f9","kind":7,"pubkey":"39f70015a75d9b3edaff4164f84eba7abde8e57ee64b4e50d9e404979d4e378a","sig":"38e783db7d6f3e405c098166bf111b5765fb381731fbc879d1672673213a57ca4d756c63e867fac71488aae028039abcf63fff77d4177eb62860e7aa9521e447","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["k","1"]]}] +["EVENT",{"content":"+","created_at":1761560773,"id":"f6b92be099250cde6609674c51e4c0e9fc4a6390ec2fef0e1a4aa9f6eca6b449","kind":7,"pubkey":"c1e2b5ea5dd26300977bac516103189a4e9fceb42d5fcea59a1f0133dce587c0","sig":"3d1dfb9d6b873b2a04373c2050fafd80ff59de0053efb5ee5b93e1a75ccebe8bbe7ef57f4f2184ccad8c1077593252ea4003fdeb14f6307e8b3757862870db3f","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","","","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["k","1"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["client","Phoenix","31990:84de35e2584d2b144aae823c9ed0b0f3deda09648530b93d1a2a146d1dea9864:app-profile"]]}] +["EVENT",{"content":"+","created_at":1761555924,"id":"6af8d3b6abe227224c83783debea4f63b22a027e9c0583b377aaba5e198524ee","kind":7,"pubkey":"dd6ada2f5ad07f146772939f658aedf0fa02023946f8b9e02b16ef80bcf43630","sig":"66726d6989f664b2767b56b2f774314c6a58c0e9c370a493c132ec92fcdb8bec8b52c4bae7b8c18ad0f30e70143e4ae9a54c180eb0b09636b17d272bfda4e411","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"🤙","created_at":1761551820,"id":"48c466527ee42b45ff13806e39344f4883a02c8e96beddae4bc4251d2a6294bf","kind":7,"pubkey":"0c24e3235228f3ee5369d4a8a46ea92f28f1a83936de61ac6b4003d40344d422","sig":"9352d14626d042ded58bffdbcab00cfcc756b9ba200d5550377fe8dafb7d3d1cbc2acccc7858730c87bc19e4ef58e56426aa87804561fb68b2a73a8716a52423","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["k","1"]]}] +["EVENT",{"content":"+","created_at":1761551701,"id":"55757cbce9fb39fdf72b5c4eb1b84acc7cf3428dd021a3cbc92f75732b38937f","kind":7,"pubkey":"e4a886f848a3d0141e5a4dc993145d39082be5c81ecb40d2e664e74a5993a678","sig":"8028c7bfb3502aa9fbc98f7e9a21488d8c325c43576c3aa29bda399547cfa196deafd560f055990a1a2fc2ae036eb4d3054237bfd18cf2e6fbf43c449149b236","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"🤙","created_at":1761551307,"id":"5027f0b57f870548aac78f17e13ecdef9b11fdb9e0677fd1cd45da3a2345a208","kind":7,"pubkey":"32d1ee76f586c12912d56c49db3f9ffc0221f647ab511759eaa08eb47587d8da","sig":"4c15a76ed12febba332ce674491a2b3f5fb8b54558701a69dcae87ba8ab47627714f4543c6bebdd89cfae9a0b2a26dfa93facead45b6ae7bc6452b80213da57c","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761549223,"id":"e247a8abe282ec03868890adf20a151095934eee04bc9bab56a98b9b6dbe0106","kind":7,"pubkey":"318c6318d90d80fc77b4c8dca160e17b94a7ab30aa7b01afbe76bb65de4b28e0","sig":"21c42c6d43760cf96346b6d4dc04a0fff8c2e1a84969128af250ae8664652e9493ce413858632b7ca84af5d174955a601915680b4eb9001e098931872a5b3769","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.damus.io/"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["k","1"]]}] +["EVENT",{"content":"+","created_at":1761549008,"id":"26abb92e63fb90aeea81c06c3014bc058347893d2e16b7dfe341384fd988d3ef","kind":7,"pubkey":"d54c85fa6483639e306db3efa0f8c6bf3f79ca61df1fc58bbc3e9c9d1e8504f5","sig":"5a76fad4e4616583660a8bc6b4d1912f24eb37609705799ab623cabe155c7651f0f296e0ea2137575b7363b47077c427528aa13bc179e1f0575ef0eb2ef37764","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"🔥","created_at":1761547432,"id":"a1805ec42c58fc4f12f77ed04bc0e37458df9a2f86621bbc67aaed8673f97a8e","kind":7,"pubkey":"8476d0dcdb53f1cc67efc8d33f40104394da2d33e61369a8a8ade288036977c6","sig":"88e2ff0e320625154695c325459675992ad3b0fa1947a81a3c9695dd6ddcd3503ddc3ebc21695effc8c488b1c41e40f6e0ba54a60683ec3ef277db21305c0e02","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.snort.social/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.snort.social/","reply","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","b89cae5b6410dcef5581590fa5463aebd86fdf750e4c021176ec8e7f4c37d37e"],["p","6e75f7972397ca3295e0f4ca0fbc6eb9cc79be85bafdd56bd378220ca8eee74e"]]}] +["EVENT",{"content":"🔥","created_at":1761547388,"id":"7cd32aa4d61bc5e1a080fa6ee50c2c1d5ebe693144b05f38a989de6aed79c01f","kind":7,"pubkey":"8476d0dcdb53f1cc67efc8d33f40104394da2d33e61369a8a8ade288036977c6","sig":"4891ef28776dcfeb88a805fa2ac40235697128c7763a787ee259e055f064e453ec572cb7cd2b34c727ffb362378dc9891f55c1ee179cc9ee84e5601b5f8badff","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["e","353650488951095ef21800bb2cbc3ce63736fe3260859585c662140a99108ceb"],["p","87e98bb670527d44379bd73a6c920afeb115297c0c0650cc182138388d6616f4"]]}] +["EVENT",{"content":"💯","created_at":1761547336,"id":"b23b752f9bc8ba1458b9e17988a0c2eaa34398d49d2fbf44daf1d43064bda051","kind":7,"pubkey":"8476d0dcdb53f1cc67efc8d33f40104394da2d33e61369a8a8ade288036977c6","sig":"c45912488f6dec0cf3d733e1d8288a99a3b8ca1866c43c2d9c5749a02af68b82059b790624b6ac8b158f01cce60830266a5954370ae1698a2a25a0e96fe00fec","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","10bc0e9b76a95ff5cecc5b8315d5938730ac1bca1760ee5cad14424838cb70bf","wss://relay.damus.io/","","13cb9f915251404603a2ac5c41805b5a4de57f630205a359ffd95ca11739b133"],["e","f8dd7fafe4d4ea0c8eed302b8a642f0ae86b2cdcd0666cb31ba2ee68759780d8","wss://premium.primal.net/","reply","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","13cb9f915251404603a2ac5c41805b5a4de57f630205a359ffd95ca11739b133","wss://filter.nostr.wine/npub1vnmhd287pvxxk5w9mcycf23av24nscwk0da7rrfaa5wq4l8hsehs90ftlv"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["e","554f937cf7515ace5c7bcba56aa69a0acb52936f91876b472ccede6bef4e418d"],["p","bb1c5151439e9abb680c15b191c1606b03333db3acc35a713091d9913751c3b3"]]}] +["EVENT",{"content":"🔥","created_at":1761547313,"id":"ec49dc401288b6e152d778f4b2ddfde38e4182dc783a46be747774d276758e9b","kind":7,"pubkey":"8476d0dcdb53f1cc67efc8d33f40104394da2d33e61369a8a8ade288036977c6","sig":"bee5a464a96641f62db5849b57fbe30244aecde50ba5b33fca4da3d4b2066a1f845fbc67e3e49ff2831cbc371985d366887e19ef7053ca2b946ba19229d8c0eb","tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","a7eb078681a447b0546b22b432f20c22a050aec92e5f96c59eafd80d1d200008"],["p","165b9d37a28903103af9ad24036bdc54270882c01add515791fc9666167cfac1"]]}] +["EVENT",{"content":"🔥","created_at":1761547264,"id":"612d05d705a58c1f9d206a850e3c3ba9fc2f621e1abf1e338319fc6f7f19f229","kind":7,"pubkey":"8476d0dcdb53f1cc67efc8d33f40104394da2d33e61369a8a8ade288036977c6","sig":"ff447de597911b8993904d6644ebd11970f3c9e0df9698f863e89f1adf5d81f539046c04374b90382da73bb3c2cbd2df65779ca93e2a76e3a21083c392ff6032","tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","10bc0e9b76a95ff5cecc5b8315d5938730ac1bca1760ee5cad14424838cb70bf"],["p","13cb9f915251404603a2ac5c41805b5a4de57f630205a359ffd95ca11739b133"]]}] +["EVENT",{"content":"🔥","created_at":1761547237,"id":"d50d8966cbcb285baa5a342d15d8cb3069d04c6c1bac040e9958cfd514be1a81","kind":7,"pubkey":"8476d0dcdb53f1cc67efc8d33f40104394da2d33e61369a8a8ade288036977c6","sig":"62e1a4d033184307a61ca8766ef60386fd44461619177111957297fcd44ec92bb42ee306479b7a9262f6405b75330e5057b228dbd7522a3c144bb2f5b8127685","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","10caddb9d95d4f5aa69b026db2b11ca45ac6a7085806296db30cca8c5c4d198e"],["p","493a5a07be1dc06a1dd2d8e943d8924fd469f057857e26f7b99ebdebe00c2557"]]}] +["EVENT",{"content":"+","created_at":1761546919,"id":"9eddc902bcbe5a6fa1a0884cc773cf22832c842405e0c2e1cdabd7a5f2be8062","kind":7,"pubkey":"b299876ba85e33da57269247f7f91aee025f5bd2bc229aa85c7908f37c10c838","sig":"05f16e9c26d69a4e6589a6ef9b9f4658248a5ea4d949a52f40d4cf614d5e01f7866050cd897322f5c4227507a1c986028a9022cad024e85de971d2ee43bc3843","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761543930,"id":"422cdce893362ee5e6f96168f22cae543d257b1a252355ebdf78bc6257fa73f3","kind":7,"pubkey":"66affba7ffb396a1d82fa096ed50722d4ae8ff5f769edac434bb5c1b2d69c4e1","sig":"cff8561e9efff659c0ebd834fb985d920f56351c3f09802c805beddac873b581360a6dde688e9f2ff0af84f4b95d4fc7fb16e19a00158244a532f49cbd63b169","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761539064,"id":"af352f529b6587b9df574d41e0fffcf263a47db0e16bdc928b45f597d4024229","kind":7,"pubkey":"13a1e3759d866920abfb7b55dc51690916cd333c9a86372c4c080db03005c388","sig":"daefe683f25551eddae7acace28ca677ed2ad1a2b32b9abb0c26e371f9efd201f596d559ef3cc548e62aa7d225b14cb3d155a63ea8e756ae8dbf1f7ed3770e8c","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"😢","created_at":1761536974,"id":"098ec33b6c312b05b53c5055f0057f108a6b000462e0e2199a8083036e07d0c3","kind":7,"pubkey":"c3caba20313ab8a447d9aae50f45ef505a8454c5748f60e0ce013541f80e20f2","sig":"802206137f066d8df7c80366682b5e38604abef37ab9cfba693c49a4c08593298717178805a48b5f0553502a40d0b318230f4e9b64b756c01b99766b78cc51d5","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://premium.primal.net/"],["k","1"]]}] +["EVENT",{"content":"+","created_at":1761536272,"id":"58cf039fbcc60b01ae613d86f1a6414e6d4554b368f089f3aa179a2eb0563567","kind":7,"pubkey":"05e90eff47c1e0ecaf5c5bdd4cb25b96993728a68d8baf5fdd5ceb4e4c522648","sig":"93c0e45d0b5f2a9996fa238a61f46c8c748ddc5643423712d8223b3f8b7188c424fe3d3045ac2b9c6a1c6142af0c28bf750731c8a613889c0efe9823749d374b","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"🤙","created_at":1761536095,"id":"61ece3eea35215412ec15da26c04262517bcf7581031af2ff33cdd82b5d2c4d3","kind":7,"pubkey":"3bf1dc06b122f34bb0a81b311856efee93107bde2d7d74cadc0dcba7ab4b1686","sig":"0f94a87e2832333aec5fc1f234fa9703571f035eaaf8d0f3d3e45ed7863476d1ffb3a79a7ff028e79a152822cfeac1c73baffbdebdb25a61321657e3486b77f4","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["e","aa1799ddccbdaad0fcf1dbe4da16b595286c0679312c49586e508e69939dfb2e"],["p","41de1771a0318a3d9b9af9ff1a0c3c7b906867ca6d4e5d4335a8bf960b1d3243"]]}] +["EVENT",{"content":"+","created_at":1761535200,"id":"e8383b667dab58c396c7c8c62d1f4c91c7609176c4a3e16117ebcdd1b8976600","kind":7,"pubkey":"c673ff0b5f228feb0abb1001882178d4c588bc4e50f857173544b5543b454f81","sig":"ff0b16ad9fbaefccbc6ee276ffb65cc0f9312d16caca8af1cf851b7f2acb4adf846133b1c6f29de0c2db26b23ba3daa01288c9bb7cff882087ead85a928098ae","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"👀","created_at":1761534220,"id":"217366ab3e78961570c600b1a963bdbe598f44abe0031aa5e72a357d8da0c897","kind":7,"pubkey":"ff2e412319f01788687a63de3248c2e9fac3f2ad15e3d621e2b42275cacb9ecd","sig":"e692d10a049daa2e529f3fe9dada7a60ebb5508fc2e9c3559a068972b37b5432d13d99bbcaa17932cd049b222024e8a3c336a33cd5b4e35da73420535912ef79","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["k","1"]]}] +["EVENT",{"content":"+","created_at":1761532024,"id":"550167b1445257de1f3d2177322c095ed5f1245ae7e4e6a00c44d8383f1c7598","kind":7,"pubkey":"063335de4bd6d0134b097e312a9af808ddf8832672575973201151389bf96ffa","sig":"8e9d4a1032af5336ece99d8a7fda7a2f67a4570af416fc76252b44b1e0e7a3ba456663e1f44ce9bce96c424e60c877cb81f0cfd1611713d4db147af16a5ee5f8","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761531170,"id":"c006021a55c969b020b6456b4080e63a4d1a466af3efdb989a16040b525ca5df","kind":7,"pubkey":"01ed03d58c90ca82021adba63d40e0ae61d4a639d3e64b085bd1976dca70f352","sig":"de4a7df15302d9dda83d6d1cb55cb34b2c19121644f45bcfdf9687fe7a967246a68f8e8552230e5c79c8658ee86ac41a5738339c17551a4e610a8410a60f7288","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["k","1"]]}] +["EVENT",{"content":"+","created_at":1761527760,"id":"1b357fa6362f8e87d0b66cb5d6b1b095d2e424e650af4d47a097db3d772d80b0","kind":7,"pubkey":"50f52c61cedd18ab097274e20c8ed06dced75822b8ece1f7a8bdc4647fd21126","sig":"c18fae801887ae6602c31a5cc3e501aa226110be76fd45de7a5a6e1195493067d21a60d5d954fc985ff71e40a35138b185ecd69377423b6c2b9078188f845921","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"💀","created_at":1761527605,"id":"b5ff73c4679f491052d2ca68fc7fd259f970a1de16a2e30b540899d20f7fb364","kind":7,"pubkey":"26e9ab7f2c8d2ac37903af90be2a1aef6f2acbd699f4f259caac7ad33d2000c1","sig":"efa0ae80c490e7c5108888ea6fbcb2a73740ed187eec53b4996d1edc3faf7bb5d21aca6bb1ff2ba91180105fd06d35fc7471ecc0f6e8a23e6372eb43a6502758","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"😂","created_at":1761527394,"id":"2bb6a160171202dc62cc96b96685587bd552785c0338940540e441ec8f42973e","kind":7,"pubkey":"357efc15a0a2a39919300301d29b1db43e537bf19b7846ab16466f2089c5446a","sig":"852261b9c2957ed7ab0bca66c1160d66eb0fcd71a7586ed51830ce6df901a71a8e43bdcf7c943ef29548939e552be2fe2a8be5b31f856871224f8ed229ac0f77","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["k","1"]]}] +["EVENT",{"content":"+","created_at":1761527256,"id":"b120d8a4cdd91a6f47924c015ef4b3352e0d23877617c73e542464fbd73409ee","kind":7,"pubkey":"82a1e67bfa9dbd6e3b117a1b18743d86aff8450e93d034d4757ca49656b64d29","sig":"18c6deda5306206cdbeecfca842b9db3c642239c911fc13a2aacbfb66bba3e32802a32c70773b3bd5a1601a88b4f634f661072d1e476cb4b7acb668dff45e901","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"🤙","created_at":1761527097,"id":"cb81abf5194dc1cd09d52c529bf091414bd031cacc58211be85b0332acd9c715","kind":7,"pubkey":"adeddcaed09462b291f1dd54dbb5806198710eb0b520fc0ed7b0629147cee262","sig":"2d7eff47ae3dce208ce090b625341f60272e14bf8a8126aa51320aa652ce11bc32a767c993b1e14a88fa055a1af07a698ad47f36abef42117c8b855bc86122fb","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761526918,"id":"fcee7c419a4a714965eb2340f2e44aa210a25e3c32d0ad184e93f3230947e953","kind":7,"pubkey":"008e4bedfeb7b69a01518c4d84affc2b6d714255cd75ae88eb94d9dd51b56aa6","sig":"764e3e0ce3ef1002882d326fddfb5b48a5f0d4eff26eabaeebb53e0646dadd0d2f1f951b045b1ffa45608bfdb14298d66055900f9ed52a4377b2d1ee8f442a4b","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"🤙","created_at":1761524563,"id":"90ddf085031c0efca4e1b5e5706a7c08e805503432a63d010e6c8321fbce021a","kind":7,"pubkey":"e1fc88f8e378784587c2337f4eb90a46c0c0622c42e77d75ec4a37521f30f622","sig":"4eb5e345457fb299aaae537f78c969c2905d62c2b3e0e9f00f462ab70783686e911dbf4ecef859b9958c72e4ef90fb821fe913fe5ed7931798286159927c3e44","tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","a4557aa997e2952f26b8ef7389c34e5c66f141c75eada8c00c5e6e575ade55f5"],["p","77f56243a824d22573fb755dd52c73c14986d15c0c98512d45f4deb08e9f879a"]]}] +["EVENT",{"content":"🤙","created_at":1761524381,"id":"f3c42ee75edeb7494d001f8281c2fa0ce5c6a7d35d249569114c57be8f72323c","kind":7,"pubkey":"e1fc88f8e378784587c2337f4eb90a46c0c0622c42e77d75ec4a37521f30f622","sig":"2913b76a2c13710dc0aff0a4c4555bcc83d524f0019e1610d7cf1f73099f5ade78dba36c3f0384524ed68ca6b9ba0da215320b5fb657eb17442dc4ccdf9f43a4","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net","root"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["e","a61b6b67bbea65632992da1ba780ce677dc66a9bfc6c5e69d67ccb8b6929fbea"],["p","d986b8a48cef4950fc62f7dc2e0d277ca505757b5aa73f0959b20659e71f7cac"]]}] +["EVENT",{"content":"+","created_at":1761522970,"id":"250d2c756a1a3cd32ed69599fd019b04e3694958c5c396040fdb9c4e33a121dc","kind":7,"pubkey":"697dfdaaf9c78dad67e05b52134211402e11804ddac72a0582e939b538075b69","sig":"40cd695dc8c405ceb256992f9844e9ba9a84fb47251256ad2976564242ed75649c21735e1055776830c06b662502205f4d4b5a14de629845b082d46482f4ac76","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"✌️","created_at":1761522425,"id":"fd50aa213711d078a8e78e0385d6f584ff931d70553f11ec7304d26075f66c70","kind":7,"pubkey":"ee6ea13ab9fe5c4a68eaf9b1a34fe014a66b40117c50ee2a614f4cda959b6e74","sig":"5a34b69aac3f9f245c7c9a520e30dd5af2f856460015682ee5c8f7e4107c34349dadc987af203461e76b7983679d7b97e83fa5fb80a10e1b8ef9399ddf485629","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","","root"],["e","beb732c0f7448c8afe470f1c8626cf02ecc1ad868184b2278a30d54f7251b54a","wss://espelho.girino.org/","reply"],["p","ee6ea13ab9fe5c4a68eaf9b1a34fe014a66b40117c50ee2a614f4cda959b6e74","","mention"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","","mention"],["p","846307688a09ec112967eb0544e78a9e88c48f50712c39ed491ff2b6fa019ae8","","mention"],["e","580c0d214b68ae4cfa8b53e1d5af5f6a8a81975c4f73dc93c54306be59a85234"],["p","c13d020bbca9a27d651099809af9d4b61ffd46e844f20e477c17cd2a0304d8a3"]]}] +["EVENT",{"content":"+","created_at":1761521898,"id":"25118f5e875d4ec345f46cd24991c45f921e40786d635ed5760c67aeb80ef4c9","kind":7,"pubkey":"24cf5e0ea5ffe5e084bdf7195f793e60c3278e5e6a6819a67865b90e9904d481","sig":"fe7188673a53f747496a97d860829d753946a8ad7324d90195aba7bcb03f13cd3a7063560a76cd9307e4cd4438d807cf2be52d021547a9932f68dfbfd6bdfe99","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761521501,"id":"6fc22b141d03be4645b52962951cbcf4d8d489eba02d4c227dc4766710c88b50","kind":7,"pubkey":"78a4da45553634ce0fc32b3b29b2ab20a813febdfe6ff6f66cd453c9db390eef","sig":"df41c4e8008f5ba9f940b62ce0d5840f94f451b514c548bdd067d763c8b6f547a39f953b78dd1564ad65e26e4d34f26c7df6a2c03723cd44d4163b0740fa5582","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761520820,"id":"c98492786332d03321b97e816d6d4fd905845142c3c5b8f983dce59b375ea0d6","kind":7,"pubkey":"5366d04e40666e8d660bc3d85047f3e1b91123a68a4451f632defd8c7ba6a068","sig":"444e96801ecc09a6a6fee37c015ca7f500082525fe8056a829880361bde6221119861b851e4a2985dc4ab04b677a30266ed44f46480d0b83271d5c5dcbe73ca0","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"🤙","created_at":1761520084,"id":"2043ca160ede370211aab94b2f0d439dd729ad6b4a6182abf1dd136a0dd30836","kind":7,"pubkey":"362774024e40e255b0b68cf8da11a54a33944c2a6ef017aa0f115825a1346404","sig":"0d1389b31bb3887afe9be5e8d1f18d862770ca43d2e6302bd4689938377b093db60e93c62c2c9b86835f60b748c1c44c88b3edd681284f494e7cdb49c3429bdf","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"❤️","created_at":1761519902,"id":"c425d1add1be29b89946f8b29cbb2bbe27c1cdb8f664dd049ba5eebb4ebe56f4","kind":7,"pubkey":"cfb7616a350b4ab9718ceaa9234eef0d0ca8fd3eefc82296a308e562554c9dbc","sig":"1264536537bfddcfa972f4df779b25610d47cdaf680be9b34367aa3ec3d425d40ba720755c8bf3fec90bc37fca3ebe4d6866c36f852dfd0ba35735b98165754e","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["k","1"]]}] +["EVENT",{"content":"+","created_at":1761519571,"id":"239bec81bccb16945246437e06f7d939f7206d17bbdb4db15e32aebb309737fd","kind":7,"pubkey":"8a4428bd77dd4ef2de85e95e049a5088350609ad74ccec0e036bb9ed50f0cb9d","sig":"4023cf37bba917182fbec35f9799b4115a0aaeadda100366dde80c6bf1559992e59b3cfb6761494d70e3c52fab106c0e542a4ed568526c8ec03edcbd1d72ec06","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761519487,"id":"53a6c98a1abad94eef92e300c1da472b395124731ab77a24eb8ad3362b38286d","kind":7,"pubkey":"89bce26e32a0e94292a73bee06fc0264a07db70a7204a6fcb0d41c67ba8dec85","sig":"a6be1bda7a533805befd9342b9e787095a6a5c208e42b870c917434c42e9679119bc944e0a54e53386638c634152f1833f986488658665d0030c65471fbee195","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761518536,"id":"018a62d508ccd4f28a581602bf98b792d5f766c77740e3e2e6aa39021dccc68b","kind":7,"pubkey":"2753ab1fa157aa13ee9067cd63f4613e0724a4e094a59f586e822829cd5857ec","sig":"65b29d974b28946738e06a574af81e57c24f9337e66c7f2b6ae6711768338505859a8505d77d2c48a7e3199b058f0c86a2288ad6e71ec279ad97145692538f84","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761518490,"id":"672ccd384c649c01d7a55598e6d3ec0ae46f6b98bc3322a01cb170bd4957b825","kind":7,"pubkey":"3c906042e889f081619588980bcf1ebca6a5443022ad6dd8205aba269577212b","sig":"95aa5e21195f36cd23dedd0616e0be90f6bd4797ef4d718de8c835aba9c0d839abe1262fa7cf3bd03ea39f9b95f9bc69446f7a8c78d3551529c72a6175f6507a","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"🤙","created_at":1761517341,"id":"a40241b464fca749bc9a6243647ae815a666b5489f1c0e79bba87245da867ff6","kind":7,"pubkey":"ebb20bb10f5500361bb94233dc1a07e561e643eacd6c12e8aee56aa484243a2c","sig":"9f7fc3eb472030f04d8f0fe76b78ae064b7e634cdc2bfe1d026aa8203474c034e67ac381710e4c79b1638322a3a48b20891a3723524006718e8d5dd293580578","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761516748,"id":"f134d0cdd56b8c2604b2153ffa997ab8e0ab233eaa0ee6577be468af5358205b","kind":7,"pubkey":"5aee9b5cd9a376bbf0ec6d5e8349e095a28e73512d29887125c0156804c61215","sig":"58267f0d8cc4ed3d56efcab40b0a552915a9d63114ba304d5a0ffe4f5a72038ad423a81daa303f4f3c03b5436349de5ccbd38d440019edf8f88175230dc0eb9e","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761516355,"id":"14f972dc70358a19c46640819f4ca3cc3699a0e6a70ba4f4dfbdab29562164ac","kind":7,"pubkey":"a5796ad50e34c3d5627e7803fd195beb813faa3869f3d1774cd6b9ff19ca9a5f","sig":"0a6fa1cc28394203289f4f64c92be32d7a091702ea40d17ebf149e7785ea491f8fa2f38d4bb916b4ac09ba2f9940b804339144404942c39b145b7150d7e5e8be","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"🤙🏻","created_at":1761516196,"id":"fe626a9656b13e8384083d058abc44c6a6c2685a563442bc5f41c7ef937fb3b5","kind":7,"pubkey":"d3f3f3c36f85b8609a5bf74e167d45861d6e8becaf4591acc4de751f8ec9aa27","sig":"1390365d4f0eab5bd58e7708375c555392c6c2ed3fccd1aeeb8f9f69ebbe741815f1cac045fe1f3905d1e8b4cca95a96f527d3497ed029f2b6c566253bcd3857","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://premium.primal.net/"],["k","1"]]}] +["EVENT",{"content":"+","created_at":1761516142,"id":"059f5719be5f78458a3caa7acf436b63d8bef888c615d99614818b3547bc55c5","kind":7,"pubkey":"544d1305cbe53c03ba78d5d2aede607b7a288435e794bbebc8e211f25da12a3b","sig":"430b55c9663f64b693d322cef045eca6f12c297a053ffa923cc049dae1a369e35f5db725f52f8d818b0d31c8480e47e4280c9eec4a9883b1ebb942b2c1332446","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"👀","created_at":1761516122,"id":"b8feb148a8353ed8d7c62147ca187fd91f0d6bd7024b3027dde1fefde7577ce7","kind":7,"pubkey":"d28413712171c33e117d4bd0930ac05b2c51b30eb3021ef8d4f1233f02c90a2b","sig":"9060af458a9178b3d91f8eba046df2952e6e5a29f59dcd99e93bc28b4791392d5b9feafbe402303a2d442529b1b643743ade75207a8954387c5c1a4ba9eeca34","tags":[["p","958b754a1d3de5b5eca0fe31d2d555f451325f8498a83da1997b7fcd5c39e88c","wss://pyramid.fiatjaf.com/","SerSleepy"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/","ODELL"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","root"],["e","8c88d5d84f60e0eb027abdd89eaa7ffce0b1d7468bae6189cf6aa4946581cb26","wss://directory.yabu.me/","reply","958b754a1d3de5b5eca0fe31d2d555f451325f8498a83da1997b7fcd5c39e88c"],["e","0024acc8f5854b3a53dea3233aff6c5af942ea0d0ba47fb6e558c593e9c6bde1"],["p","af92154b4fd002924031386f71333b0afd9741a076f5c738bc2603a5b59d671f"]]}] +["EVENT",{"content":"+","created_at":1761516008,"id":"8ac33b2fcc5898c9bdcc4f7b53fd628a98b3f3bdac619d24050a36bf99af80e9","kind":7,"pubkey":"a42048d7ea26e9c36a67b5ff266c508882a89dbe36ef603cc9c6726326886c32","sig":"5f1ffe501c9809f1900b368f731d7ab76d3b236d0a17eb7274ecdf19be9ffa1a2cb942e2d97cfaf740a80fdbd6c23bf06579934d759ad2ad92a3f4c1ce0ac50f","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761515823,"id":"9c7c1466f1918b42b353b4d595d871e21954101e6daf0e838c67c9e436d6dc88","kind":7,"pubkey":"99dd1997593d92a5dcabd1b04fe039834d488f1edbb9bf94eb3d506921aef7e6","sig":"6a5db5a82df6ceba0abe509eefa36d2e0a43bd29c04ac12fe209b830d03e31bc1fa0c38d25193cfb366158199e2da79b0111010aabb98a78019cc4e1e8eca33c","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761515599,"id":"42e9947a63d1ba675fb7c03515e3207960aba9c0cf45eb0c925445cb484b202c","kind":7,"pubkey":"877308276be50ce9bafa7e5e374e4fcbf5e9859a21918f34baefd000746b7d35","sig":"30aafa93a2df93eb03877f82a5f073e2ba9f5f3379cbb0ac249dc205334c52e45adf278aad894a2bb375d459de4a60edb1ec996c80d7e2a907ae326411fa25e3","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://nostr.wine/"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["k","1"]]}] +["EVENT",{"content":"❤️","created_at":1761515550,"id":"b88c3f1fbbdabe25db50bfce24d5a64d07bc590027c30747d9e12d71a0ec2409","kind":7,"pubkey":"1408bad049bab8a38b976075affe413c3521bbeef62cc4ce3555299f4971f2ca","sig":"92f5293a1e9b4181b15dd1ea56feff21f67202e74b49124e578a49c28a51058f1edbafdc74fc92241d11e7574149c4b5c4a7e3c854cbf7c240ae28f8ed00b331","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://premium.primal.net/"],["k","1"]]}] +["EVENT",{"content":"💯","created_at":1761515539,"id":"d4325fe312b84c23f94c7cce1c5fafc53162f6c2984f65581081e58dfbe435da","kind":7,"pubkey":"c4f5e7a75a8ce3683d529cff06368439c529e5243c6b125ba68789198856cac7","sig":"b78c20c080880e31beeb252aff2850c4d772770289158506509e6e2f267287cfb56a33e824498b6e4b158341132fc0987a8d9a1d145105c020ae9c7ccc2efa44","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://nostr.wine/","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://nostr.wine/"],["k","1"]]}] +["EVENT",{"content":"+","created_at":1761515537,"id":"f60ed452293dfa0712e90f98a44d709b1ef527a47bd0ebdf6916fe29f3916a56","kind":7,"pubkey":"ae5d1aef5eccf70b37313802f648b4fcf71eeb75d97e3f8eceed28da668fc2ea","sig":"299babb347c9157b41d0e5b13b1f22577d5e4eddfb67bf3070b14b460dc9eca6af0dc76c5d8d5d9cf0c23418596385333afefb231bf636094a5c53671538cc87","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761515454,"id":"2fb4f9aeccc1ab8114df7ed1f1ba436e3e5e9546e266910272bb4469da57781b","kind":7,"pubkey":"12a83f97f7aeef4f9271ce426070b6d34d0d02a89a9c92caf1f686ed89c7ab24","sig":"12838567e6e3c79be99ee8a55a689f7f6acfd122c407a3f84799f242f562546280280e0c5238d1f55d171bba3bd5f7b7aeec077fe010838b69f6a1d64b83fb03","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761515348,"id":"042139b6fd2cbfcb8309a53752a60c380ab878c87e0503c2270903b2bd224bab","kind":7,"pubkey":"73dfb2714d170075be9683ee9156486eb5de8d34dd39a879cadae54518656455","sig":"ea78f83d5b238dde325e02998d25e742fb746845d395844cf94269e990240e2280e282365c4d3909c538359300e960e4887367f387856bfd6d31d0f3c9fb512b","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761515306,"id":"90821bdec3cc8773d5003fdad41eca99a239ae31b70baeb29de9438c436cc10d","kind":7,"pubkey":"f9a2580977f9f25b8eb590ee0b5419e783a2af9d7669906db3755da9b769f4b9","sig":"7d8fb4b898e65c386a526228b5cf80cd46c695282bb2bbd3faad26c4e873b9c6aa31e4d8d00e326d6c99e12f29c3023fd03ef31a3eb1658c05928fc0625cba48","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761515060,"id":"16c765c1c30fabaec4e15fd2172f480e9850c86191429f3965ba04b96b8d3f1a","kind":7,"pubkey":"e96911258fabb7c235ffbb052c96a07bf91f3ef91cfa036e4f442fd23320261f","sig":"2197a21b9ddb00b802e077ada9bbf87c19693ce77eb1c52fabef30367b8d444d024ebedc32521450dfd970d2624b79c2fbd53d12367cb74add0706c7a98a545f","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"😬","created_at":1761515039,"id":"0475677d23eeb207fc8832116cb5d520aebdef7a231dd89fb23c133f9c57aa7e","kind":7,"pubkey":"ee6ea13ab9fe5c4a68eaf9b1a34fe014a66b40117c50ee2a614f4cda959b6e74","sig":"02aff982ff743de48307e4853bdcaab3e617d2fe2c1285fa43d29e490acda4a981a252664da6db91158cbc6f974de04ebd66802eb56b99087296312fa20c64f5","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"😬","created_at":1761515034,"id":"879bd2021ebe8974e67b8fc7e51e87ad4dffdd40b283f38042aad133926964cd","kind":7,"pubkey":"ee6ea13ab9fe5c4a68eaf9b1a34fe014a66b40117c50ee2a614f4cda959b6e74","sig":"6e95badb8e52d46a7673aaf9f0d63bdf2a831c4813d1f9a28bbd24fdf927c7f4d9070ec5c8f8f62a52416c5630640ca5de8c02ec6c3fe77cfbfd5f34db6185dc","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","root","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["e","bc367630aae0af9ae390bcbf08c511ce105c186268da21374a2df44031f1da73"],["p","f728d9e6e7048358e70930f5ca64b097770d989ccd86854fe618eda9c8a38106"]]}] +["EVENT",{"content":"+","created_at":1761514945,"id":"e57b18bf89302b4ff43ad7b273e349f892e11a3d307477b434210c5d0c940a69","kind":7,"pubkey":"080ba648c4a96fb2d85610c4cd2135a8f8fba73850c5034647b96346eb9aee20","sig":"1f3a5428ce42eae7f6c41e6fc7f27ac94ac30230cc293af17739b17c647cae3e1762a5da9e835522af6d639f1343390dcf1fa231bbf6dc724061f620a59563b8","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"😂","created_at":1761514882,"id":"074deaea39f4bd9b450d0a229e7e475e3f8ff3aca15cc27d9036e9389202c9ff","kind":7,"pubkey":"7a96dd29b1eef4e7077c0b031ef7f93ddd8274fa3bfd52129e75f3b4372a1009","sig":"87a19562f4b622827ac4f851718b0aa4e8ca302bd5bf8ea4084276490c4d677d380cacdd98df8f157106ac7c825fecf90be48439f76229dfb0d26ebd76b9a6a6","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["k","1"]]}] +["EVENT",{"content":"🤙","created_at":1761514833,"id":"b62717a944e3b2586bcd77a1e88cfda7f2b5fe4873adc07c1288383865a6bfc5","kind":7,"pubkey":"623bb03b63b9a48c12e89bc3cb4c70ff10d1ad23737c332fb59c00c1a691bb0b","sig":"2e2dbbeb1463efc4eb5d93d35e221f935db7c2f7c4bf8fd61f7d02b1b7d0381df273884d6076b8b4d29b8df8f4baf1c68a530e53621b58f2b105d07855ede1e8","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761514770,"id":"a5ce07ca5c3f2650000fba57ca0c5724c2e0828dfb45ee49bbd670bccc2b4d80","kind":7,"pubkey":"bea135f01ee6182b8618acaaa5eefd5271f22d5c8b2827413fe894a720051c36","sig":"d64e6b39a4c9499c941c286679987c9550b9aabc86409cb531ca62907e3e28ba2df2302442893cac2d24e7acb1275c3548fb78d171390d8d300ed970b887c64a","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761514761,"id":"dc3ab86a1ae11a73e7bdff2aeb075f62efc3a1bbf67a74ed768b0ddd232e2bcb","kind":7,"pubkey":"9103b1d5ec938461bde245859ccd9ad0e3ac40af9aa2f85c62d84a2a0b7d75f2","sig":"3ae16f2dfc6707bc7fea6e683b1d697d8b5000aa962a0f2c8897cebbe2145688ccb0c0a6f7dc63bfda2642146d3850244e173b30a11c8441a756789c5b24701d","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761514690,"id":"b2ce736474e439b3d5d86119fe8540220580241d5e67f72acf075563f74ea156","kind":7,"pubkey":"9f557e7c1eafe4dd88d6f050f7a1c8a4cc1408fbcb8370236a2e358f3a3da3e5","sig":"2e414e1fc9db94e3bf22ca59f5df2b5f778fa868c2a146d81846b3e1c0c33b5a52e11986c3b83ccdab0e766802b5ddcfd807337ea09fe97250d50e5c1520c0fa","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"+","created_at":1761514635,"id":"da7170f8e6d1009b66f6e9474cdf2f0cfc5ff3dea1447c2b8028a0dea6f78b6d","kind":7,"pubkey":"23d1d973554196e571622b78d2cb859fd1eb579e20299358c3c38f4d903f137f","sig":"ffad326e8ff06ba6e00bd7aee97957680d45bf13ce146c5a89960c9c48aa6349172bbd06119bd48d309b62ad30b417c74455d467645926544ead4bf31ce6684f","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"🫡","created_at":1761514634,"id":"8308020cd19434d17fb207de13693a9581d106a7f8dd5808e227a978715d9930","kind":7,"pubkey":"78283e0d50c65682fd63d401f6f187f25dc56372bdf5dd02871f13e8bfb541cb","sig":"0da1e6d502fe08a19411bedbba5125308663f249da2488191cb50780dec5f1f629a880c846b3b77f08d3ea5fba591719d97dd467f9c5bb4d909e2dc9dbccea92","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"💯","created_at":1761514625,"id":"c5cd4bc4434496e8167a3cd199b2fc9ab4d817018539ed6a70aae424cd44d706","kind":7,"pubkey":"2eb03a1f316c3cf9c900e7f536ee28e5486349067be018a965a7c7ca5b4f7f3c","sig":"2a81ddf17f2bdd53da0002c2b12992f26a6e10c3e069ff841adc96764fcd434d7c20c38d43bf4c626ea794dd52f6a15ea717440810e9d291e6ccd76cc4c91c86","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://premium.primal.net/"],["k","1"]]}] +["EVENT",{"content":"+","created_at":1761514577,"id":"f302e3b18bdb3adc4ef5e2becf2f1bdc4283983c1e371070bf5f8899d337df26","kind":7,"pubkey":"e0d87aa1949999b43ef0a0812aa94072628d5b0ba62ccac7833d4824fd4de225","sig":"85c8234ff43b145bd929f65a397321a0fe85b71aa60a2c18261c908acec5bd9c8a46f8a22080f1ec1c456606693cb99bce1a951dab98dee3c853ff37a45ac1d8","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"😂","created_at":1761514463,"id":"99e2d5f6fd78ca8b024452733c08fa639da2e8550a12c3a5680d6355dfdf30c1","kind":7,"pubkey":"4b6025a8fe1145c413e7cee262548cf800eec8d6854a21a1808e0f98e9907649","sig":"1857629603486e460f07ec0d38750444e52ae15f0f6f01e982b04b55449bd394a4ae7599964b673f29e199ab8bf2113efb2467e2c15459c6f493b02fcea14ea9","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"]]}] +["EVENT",{"content":"👀","created_at":1761514455,"id":"66af95a6bdfec756344f48241562b684082ff9c76ea940c11c4fd85e91e1219c","kind":7,"pubkey":"d5805ae449e108e907091c67cdf49a9835b3cac3dd11489ad215c0ddf7c658fc","sig":"69f4a3fe7c1cc6aa9c9cc4a2e90e4b71c3b9afaad262e68b92336e0493ff1a748b5dcc20ab6e86d4551dc5ea680ddfa1c08d47f9e4845927e143e8ef2183479b","tags":[["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://relay.primal.net/","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"],["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://relay.primal.net/"],["k","1"]]}] +["EVENT",{"content":"+","created_at":1761514412,"id":"7124bca1479edeb1476d94ed6620ee1210194590b08cf1df385d053679d73fe7","kind":7,"pubkey":"af92154b4fd002924031386f71333b0afd9741a076f5c738bc2603a5b59d671f","sig":"311e7b92ae479262c8ad91ee745eca9c78d469459577d7fb598bff1e6c580f289b3c1d82cd769d0891da9248250d6877357ddaf293f33f496af9e6c8894bc485","tags":[["p","04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9","wss://premium.primal.net/","ODELL"],["k","1"],["e","d44ad96cb8924092a76bc2afddeb12eb85233c0d03a7d9adc42c2a85a79a4305","wss://premium.primal.net/"],["client","Coracle","31990:97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322:1685968093690"]]}] From 4c179987772f6d4d47b972b48391f1a99e1c9b5e Mon Sep 17 00:00:00 2001 From: William Casarin Date: Wed, 29 Oct 2025 16:21:44 -0700 Subject: [PATCH 10/12] migration: implement ndb_count_reactions also verify it matches the online counting --- src/nostrdb.c | 113 ++++++++++++++++++++++++++++++++++++-------------- test.c | 12 ++++++ 2 files changed, 95 insertions(+), 30 deletions(-) diff --git a/src/nostrdb.c b/src/nostrdb.c index bbe8880f..b24e5e50 100644 --- a/src/nostrdb.c +++ b/src/nostrdb.c @@ -2002,14 +2002,94 @@ static int ndb_rebuild_note_indices(struct ndb_txn *txn, enum ndb_dbs *indices, int ndb_cursor_start(MDB_cursor *cur, MDB_val *k, MDB_val *v); +// find the last id tag in a note (e, p, etc) +static unsigned char *ndb_note_last_id_tag(struct ndb_note *note, char type) +{ + unsigned char *last = NULL; + struct ndb_iterator iter; + struct ndb_str str; + + // get the liked event id (last id) + ndb_tags_iterate_start(note, &iter); + + while (ndb_tags_iterate_next(&iter)) { + if (iter.tag->count < 2) + continue; + + str = ndb_tag_str(note, iter.tag, 0); + + // assign liked to the last e tag + if (str.flag == NDB_PACKED_STR && str.str[0] == type) { + str = ndb_tag_str(note, iter.tag, 1); + if (str.flag == NDB_PACKED_ID) + last = str.id; + } + } + + return last; +} + + static int ndb_count_replies(struct ndb_txn *txn, const unsigned char *note_id, uint16_t *direct_replies, uint32_t *thread_replies) { return 1; } /* count all of the reactions for a note */ -static int ndb_count_reactions(struct ndb_txn *txn, const unsigned char *note_id, uint32_t *count) +int ndb_count_reactions(struct ndb_txn *txn, const unsigned char *note_id, uint32_t *count) { + MDB_val k, v; + MDB_cursor *cur; + MDB_dbi db; + + int rc; + uint64_t note_key; + size_t size; + struct ndb_note *note; + unsigned char *keybuf, *last_id; + char buffer[41]; /* 1 + 32 + 8 */ + *count = 0; + + db = txn->lmdb->dbs[NDB_DB_NOTE_TAGS]; + if ((rc = mdb_cursor_open(txn->mdb_txn, db, &cur))) { + fprintf(stderr, "ndb_count_reactions: mdb_cursor_open failed, error %d\n", rc); + return 0; + } + + buffer[0] = 'e'; + memcpy(&buffer[1], note_id, 32); + memset(&buffer[33], 0x00, 8); + + k.mv_data = buffer; + k.mv_size = sizeof(buffer); + v.mv_data = NULL; + v.mv_size = 0; + + if (mdb_cursor_get(cur, &k, &v, MDB_SET_RANGE)) + goto cleanup; + + do { + keybuf = (unsigned char *)k.mv_data; + note_key = *((uint64_t*)v.mv_data); + if (k.mv_size < sizeof(buffer)) + break; + if (keybuf[0] != 'e') + break; + if (memcmp(&keybuf[1], note_id, 32)) + break; + if (!(note = ndb_get_note_by_key(txn, note_key, &size))) + continue; + if (ndb_note_kind(note) != 7) + continue; + if (!(last_id = ndb_note_last_id_tag(note, 'e'))) + continue; + if (memcmp(last_id, note_id, 32)) + continue; + (*count)++; + } while (mdb_cursor_get(cur, &k, &v, MDB_NEXT) == 0); + +cleanup: + mdb_cursor_close(cur); return 1; } @@ -2108,7 +2188,7 @@ static void ndb_note_meta_builder_count_reactions(struct ndb_txn *txn, struct nd // /* switch from flatbuffer stats to custom v2 */ -static int ndb_migrate_reaction_stats(struct ndb_txn *txn) +static int ndb_migrate_metadata(struct ndb_txn *txn) { MDB_val k, k2, v, v2; MDB_cursor *cur; @@ -2491,7 +2571,7 @@ static struct ndb_migration MIGRATIONS[] = { { .fn = ndb_migrate_lower_user_search_indices }, { .fn = ndb_migrate_utf8_profile_names }, { .fn = ndb_migrate_profile_indices }, - //{ .fn = ndb_migrate_reaction_stats }, + //{ .fn = ndb_migrate_metadata }, }; @@ -3477,33 +3557,6 @@ static int ndb_write_profile(struct ndb_txn *txn, return 1; } -// find the last id tag in a note (e, p, etc) -static unsigned char *ndb_note_last_id_tag(struct ndb_note *note, char type) -{ - unsigned char *last = NULL; - struct ndb_iterator iter; - struct ndb_str str; - - // get the liked event id (last id) - ndb_tags_iterate_start(note, &iter); - - while (ndb_tags_iterate_next(&iter)) { - if (iter.tag->count < 2) - continue; - - str = ndb_tag_str(note, iter.tag, 0); - - // assign liked to the last e tag - if (str.flag == NDB_PACKED_STR && str.str[0] == type) { - str = ndb_tag_str(note, iter.tag, 1); - if (str.flag == NDB_PACKED_ID) - last = str.id; - } - } - - return last; -} - int ndb_set_note_meta(struct ndb *ndb, const unsigned char *id, struct ndb_note_meta *meta) { struct ndb_writer_msg msg; diff --git a/test.c b/test.c index 7a52b2d1..1a58c9d1 100644 --- a/test.c +++ b/test.c @@ -30,6 +30,8 @@ static void delete_test_db() { unlink(TEST_DIR "/data.lock"); } +int ndb_count_reactions(struct ndb_txn *txn, const unsigned char *note_id, uint32_t *count); + static void db_load_events(struct ndb *ndb, const char *filename) { size_t filesize; @@ -132,7 +134,17 @@ static void test_count_metadata() assert(reactions > 0); assert(total_reactions == reactions); + + ndb_end_query(&txn); + + ndb_begin_query(ndb, &txn); + /* this is used in the migration code, + * let's make sure it matches the online logic */ + ndb_count_reactions(&txn, id, &reactions); + printf("\t# after-counted reactions %d\n", reactions); + assert(reactions == total_reactions); ndb_end_query(&txn); + ndb_destroy(ndb); delete_test_db(); From b81490cda1c6c18459e515c130e88b331dc9fea9 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Thu, 30 Oct 2025 13:31:59 -0700 Subject: [PATCH 11/12] migration: reply counter + test Signed-off-by: William Casarin --- src/nostrdb.c | 219 ++++++++++++++++++++++++++++++++------------------ test.c | 23 ++++-- 2 files changed, 158 insertions(+), 84 deletions(-) diff --git a/src/nostrdb.c b/src/nostrdb.c index b24e5e50..ff369690 100644 --- a/src/nostrdb.c +++ b/src/nostrdb.c @@ -2029,9 +2029,151 @@ static unsigned char *ndb_note_last_id_tag(struct ndb_note *note, char type) return last; } +/* get reply information from a note */ +static void ndb_parse_reply(struct ndb_note *note, struct ndb_note_reply *note_reply) +{ + unsigned char *root, *reply, *mention, *id; + const char *marker; + struct ndb_iterator iter; + struct ndb_str str; + uint16_t count; + int any_marker, first; + + any_marker = 0; + first = 1; + root = NULL; + reply = NULL; + mention = NULL; + + // get the liked event id (last id) + ndb_tags_iterate_start(note, &iter); + while (ndb_tags_iterate_next(&iter)) { + if (root && reply && mention) + break; + + marker = NULL; + count = ndb_tag_count(iter.tag); + + if (count < 2) + continue; + + str = ndb_tag_str(note, iter.tag, 0); + if (!(str.flag == NDB_PACKED_STR && str.str[0] == 'e')) + continue; + + str = ndb_tag_str(note, iter.tag, 1); + if (str.flag != NDB_PACKED_ID) + continue; + id = str.id; + + /* if we have the marker, assign it */ + if (count >= 4) { + str = ndb_tag_str(note, iter.tag, 3); + if (str.flag == NDB_PACKED_STR) + marker = str.str; + } -static int ndb_count_replies(struct ndb_txn *txn, const unsigned char *note_id, uint16_t *direct_replies, uint32_t *thread_replies) + if (marker) { + any_marker = true; + if (!strcmp(marker, "root")) + root = id; + else if (!strcmp(marker, "reply")) + reply = id; + else if (!strcmp(marker, "mention")) + mention = id; + } else if (!any_marker && first) { + root = id; + first = 0; + } else if (!any_marker && !reply) { + reply = id; + } + } + + note_reply->reply = reply; + note_reply->root = root; + note_reply->mention = mention; +} + +static int ndb_is_reply_to_root(struct ndb_note_reply *reply) { + if (reply->root && !reply->reply) + return 0; + else if (reply->root && reply->reply) + return !memcmp(reply->root, reply->reply, 32); + else + return 0; +} + + +int ndb_count_replies(struct ndb_txn *txn, const unsigned char *note_id, uint16_t *direct_replies, uint32_t *thread_replies) +{ + MDB_val k, v; + MDB_cursor *cur; + MDB_dbi db; + + int rc; + uint64_t note_key; + size_t size; + struct ndb_note *note; + unsigned char *keybuf, *reply_id; + struct ndb_note_reply reply; + char buffer[41]; /* 1 + 32 + 8 */ + + *direct_replies = 0; + *thread_replies = 0; + + db = txn->lmdb->dbs[NDB_DB_NOTE_TAGS]; + if ((rc = mdb_cursor_open(txn->mdb_txn, db, &cur))) { + fprintf(stderr, "ndb_count_reactions: mdb_cursor_open failed, error %d\n", rc); + return 0; + } + + buffer[0] = 'e'; + memcpy(&buffer[1], note_id, 32); + memset(&buffer[33], 0x00, 8); + + k.mv_data = buffer; + k.mv_size = sizeof(buffer); + v.mv_data = NULL; + v.mv_size = 0; + + if (mdb_cursor_get(cur, &k, &v, MDB_SET_RANGE)) + goto cleanup; + + do { + keybuf = (unsigned char *)k.mv_data; + note_key = *((uint64_t*)v.mv_data); + if (k.mv_size < sizeof(buffer)) + break; + if (keybuf[0] != 'e') + break; + if (memcmp(&keybuf[1], note_id, 32)) + break; + if (!(note = ndb_get_note_by_key(txn, note_key, &size))) + continue; + if (ndb_note_kind(note) != 1) + continue; + + ndb_parse_reply(note, &reply); + + if (ndb_is_reply_to_root(&reply)) { + reply_id = reply.root; + } else { + reply_id = reply.reply; + } + + if (reply_id && !memcmp(reply_id, note_id, 32)) { + (*direct_replies)++; + } + + if (reply.root && !memcmp(reply.root, note_id, 32)) { + (*thread_replies)++; + } + + } while (mdb_cursor_get(cur, &k, &v, MDB_NEXT) == 0); + +cleanup: + mdb_cursor_close(cur); return 1; } @@ -5632,81 +5774,6 @@ static unsigned char *ndb_note_first_tag_id(struct ndb_note *note, char tag) return NULL; } -/* get reply information from a note */ -static void ndb_parse_reply(struct ndb_note *note, struct ndb_note_reply *note_reply) -{ - unsigned char *root, *reply, *mention, *id; - const char *marker; - struct ndb_iterator iter; - struct ndb_str str; - uint16_t count; - int any_marker, first; - - any_marker = 0; - first = 1; - root = NULL; - reply = NULL; - mention = NULL; - - // get the liked event id (last id) - ndb_tags_iterate_start(note, &iter); - while (ndb_tags_iterate_next(&iter)) { - if (root && reply && mention) - break; - - marker = NULL; - count = ndb_tag_count(iter.tag); - - if (count < 2) - continue; - - str = ndb_tag_str(note, iter.tag, 0); - if (!(str.flag == NDB_PACKED_STR && str.str[0] == 'e')) - continue; - - str = ndb_tag_str(note, iter.tag, 1); - if (str.flag != NDB_PACKED_ID) - continue; - id = str.id; - - /* if we have the marker, assign it */ - if (count >= 4) { - str = ndb_tag_str(note, iter.tag, 3); - if (str.flag == NDB_PACKED_STR) - marker = str.str; - } - - if (marker) { - any_marker = true; - if (!strcmp(marker, "root")) - root = id; - else if (!strcmp(marker, "reply")) - reply = id; - else if (!strcmp(marker, "mention")) - mention = id; - } else if (!any_marker && first) { - root = id; - first = 0; - } else if (!any_marker && !reply) { - reply = id; - } - } - - note_reply->reply = reply; - note_reply->root = root; - note_reply->mention = mention; -} - -static int ndb_is_reply_to_root(struct ndb_note_reply *reply) -{ - if (reply->root && !reply->reply) - return 0; - else if (reply->root && reply->reply) - return !memcmp(reply->root, reply->reply, 32); - else - return 0; -} - static int ndb_increment_quote_metadata( struct ndb_txn *txn, unsigned char *quoted_note_id, diff --git a/test.c b/test.c index 1a58c9d1..1ea760e3 100644 --- a/test.c +++ b/test.c @@ -31,6 +31,7 @@ static void delete_test_db() { } int ndb_count_reactions(struct ndb_txn *txn, const unsigned char *note_id, uint32_t *count); +int ndb_count_replies(struct ndb_txn *txn, const unsigned char *note_id, uint16_t *direct_replies, uint32_t *thread_replies); static void db_load_events(struct ndb *ndb, const char *filename) { @@ -78,8 +79,8 @@ static void test_count_metadata() struct ndb_note_meta *meta; //struct ndb_note_meta_entry *counts; struct ndb_note_meta_entry *entry; - uint16_t count; - uint32_t total_reactions, reactions, replies; + uint16_t count, direct_replies[2]; + uint32_t total_reactions, reactions, thread_replies[2]; int i; reactions = 0; @@ -118,13 +119,13 @@ static void test_count_metadata() assert(entry); assert(*ndb_note_meta_counts_quotes(entry) == 2); - replies = *ndb_note_meta_counts_thread_replies(entry); - printf("\t# thread replies %d\n", replies); - assert(replies == 93); + thread_replies[0] = *ndb_note_meta_counts_thread_replies(entry); + printf("\t# thread replies %d\n", thread_replies[0]); + assert(thread_replies[0] == 93); - replies = *ndb_note_meta_counts_direct_replies(entry); - printf("\t# direct replies %d\n", replies); - assert(replies == 14); + direct_replies[0] = *ndb_note_meta_counts_direct_replies(entry); + printf("\t# direct replies %d\n", direct_replies[0]); + assert(direct_replies[0] == 14); total_reactions = *ndb_note_meta_counts_total_reactions(entry); printf("\t# total reactions %d\n", reactions); @@ -143,6 +144,12 @@ static void test_count_metadata() ndb_count_reactions(&txn, id, &reactions); printf("\t# after-counted reactions %d\n", reactions); assert(reactions == total_reactions); + + ndb_count_replies(&txn, id, &direct_replies[1], &thread_replies[1]); + printf("\t# after-counted replies direct:%d thread:%d\n", direct_replies[1], thread_replies[1]); + assert(direct_replies[0] == direct_replies[1]); + assert(thread_replies[0] == thread_replies[1]); + ndb_end_query(&txn); ndb_destroy(ndb); From f0edc1eb11e58a87e6338cb92973b523c19d3af4 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Thu, 30 Oct 2025 14:39:03 -0700 Subject: [PATCH 12/12] migration: migrate full reaction stats for all kind1 notes Signed-off-by: William Casarin --- TODO | 3 -- ndb.c | 6 +++ src/metadata.c | 52 +++++++++++++++++++++- src/nostrdb.c | 116 ++++++++++++++++++++++++++++++++++++------------- src/nostrdb.h | 6 ++- test.c | 4 +- 6 files changed, 150 insertions(+), 37 deletions(-) diff --git a/TODO b/TODO index 7fe226e2..cf10d3cc 100644 --- a/TODO +++ b/TODO @@ -1,4 +1 @@ test write +metadata -(B) migrate reply stats +metadata -(B) migrate quote stats +metadata -(B) migrate reaction stats +metadata diff --git a/ndb.c b/ndb.c index f58d30b0..558f0769 100644 --- a/ndb.c +++ b/ndb.c @@ -28,6 +28,7 @@ static int usage() printf(" print-tag-keys\n"); printf(" print-relay-kind-index-keys\n"); printf(" print-author-kind-index-keys\n"); + printf(" print-note-metadata\n"); printf(" import \n\n"); printf("settings\n\n"); @@ -108,6 +109,7 @@ int ndb_print_kind_keys(struct ndb_txn *txn); int ndb_print_tag_index(struct ndb_txn *txn); int ndb_print_relay_kind_index(struct ndb_txn *txn); int ndb_print_author_kind_index(struct ndb_txn *txn); +int ndb_print_note_metadata(struct ndb_txn *txn); static void print_note(struct ndb_note *note) { @@ -443,6 +445,10 @@ int main(int argc, char *argv[]) ndb_begin_query(ndb, &txn); ndb_print_author_kind_index(&txn); ndb_end_query(&txn); + } else if (argc == 2 && !strcmp(argv[1], "print-note-metadata")) { + ndb_begin_query(ndb, &txn); + ndb_print_note_metadata(&txn); + ndb_end_query(&txn); } else if (argc == 3 && !strcmp(argv[1], "note-relays")) { struct ndb_note_relay_iterator iter; const char *relay; diff --git a/src/metadata.c b/src/metadata.c index 6794e166..7fd5b574 100644 --- a/src/metadata.c +++ b/src/metadata.c @@ -221,7 +221,7 @@ uint16_t *ndb_note_meta_entry_type(struct ndb_note_meta_entry *entry) } /* find a metadata entry, optionally matching a payload */ -struct ndb_note_meta_entry *ndb_note_meta_find_entry(struct ndb_note_meta *meta, uint16_t type, uint64_t *payload) +static struct ndb_note_meta_entry *ndb_note_meta_find_entry_impl(struct ndb_note_meta *meta, uint16_t type, uint64_t *payload, int sorted) { struct ndb_note_meta_entry *entries, *entry; int i; @@ -232,6 +232,8 @@ struct ndb_note_meta_entry *ndb_note_meta_find_entry(struct ndb_note_meta *meta, entries = ndb_note_meta_entries(meta); assert(((intptr_t)entries - (intptr_t)meta) == 16); + /* TODO(jb55): do bsearch for large sorted entries */ + for (i = 0; i < meta->count; i++) { entry = &entries[i]; assert(((uintptr_t)entry % 8) == 0); @@ -250,6 +252,22 @@ struct ndb_note_meta_entry *ndb_note_meta_find_entry(struct ndb_note_meta *meta, return NULL; } +struct ndb_note_meta_entry *ndb_note_meta_find_entry(struct ndb_note_meta *meta, uint16_t type, uint64_t *payload) +{ + int sorted = 1; + return ndb_note_meta_find_entry_impl(meta, type, payload, sorted); +} + +struct ndb_note_meta_entry *ndb_note_meta_builder_find_entry( + struct ndb_note_meta_builder *builder, + uint16_t type, + uint64_t *payload) +{ + /* meta building in progress is not necessarily sorted */ + int sorted = 0; + return ndb_note_meta_find_entry_impl((struct ndb_note_meta *)builder->cursor.start, type, payload, sorted); +} + void ndb_note_meta_reaction_set(struct ndb_note_meta_entry *entry, uint32_t count, union ndb_reaction_str str) { entry->type = NDB_NOTE_META_REACTION; @@ -389,3 +407,35 @@ union ndb_reaction_str ndb_note_meta_reaction_str(struct ndb_note_meta_entry *en { return entry->payload.reaction_str; } + +void print_note_meta(struct ndb_note_meta *meta) +{ + int count, i; + struct ndb_note_meta_entry *entries, *entry; + union ndb_reaction_str reaction; + char strbuf[128]; + + count = ndb_note_meta_entries_count(meta); + entries = ndb_note_meta_entries(meta); + + for (i = 0; i < count; i++) { + entry = &entries[i]; + switch (entry->type) { + case NDB_NOTE_META_REACTION: + reaction = ndb_note_meta_reaction_str(entry); + + ndb_reaction_to_str(&reaction, strbuf); + printf("%s%d ", strbuf, *ndb_note_meta_reaction_count(entry)); + break; + case NDB_NOTE_META_COUNTS: + printf("quotes %d\treplies %d\tall_replies %d\treactions %d\t", + *ndb_note_meta_counts_quotes(entry), + *ndb_note_meta_counts_direct_replies(entry), + *ndb_note_meta_counts_thread_replies(entry), + *ndb_note_meta_counts_total_reactions(entry)); + break; + } + } + + printf("\n"); +} diff --git a/src/nostrdb.c b/src/nostrdb.c index ff369690..9f69fdb4 100644 --- a/src/nostrdb.c +++ b/src/nostrdb.c @@ -2178,7 +2178,7 @@ int ndb_count_replies(struct ndb_txn *txn, const unsigned char *note_id, uint16_ } /* count all of the reactions for a note */ -int ndb_count_reactions(struct ndb_txn *txn, const unsigned char *note_id, uint32_t *count) +int ndb_rebuild_reaction_metadata(struct ndb_txn *txn, const unsigned char *note_id, struct ndb_note_meta_builder *builder, uint32_t *count) { MDB_val k, v; MDB_cursor *cur; @@ -2189,6 +2189,8 @@ int ndb_count_reactions(struct ndb_txn *txn, const unsigned char *note_id, uint3 size_t size; struct ndb_note *note; unsigned char *keybuf, *last_id; + struct ndb_note_meta_entry *entry; + union ndb_reaction_str reaction_str; char buffer[41]; /* 1 + 32 + 8 */ *count = 0; @@ -2227,6 +2229,21 @@ int ndb_count_reactions(struct ndb_txn *txn, const unsigned char *note_id, uint3 continue; if (memcmp(last_id, note_id, 32)) continue; + + if (builder) { + if (!ndb_reaction_set(&reaction_str, ndb_note_content(note))) + ndb_reaction_set(&reaction_str, "+"); + + if ((entry = ndb_note_meta_builder_find_entry(builder, NDB_NOTE_META_REACTION, &reaction_str.binmoji))) { + (*ndb_note_meta_reaction_count(entry))++; + } else if ((entry = ndb_note_meta_add_entry(builder))) { + ndb_note_meta_reaction_set(entry, 1, reaction_str); + } else { + /* couldn't add reaction entry ? */ + ndb_debug("ndb_rebuild_note_indices: couldn't add reaction count entry to metadata builder\n"); + } + } + (*count)++; } while (mdb_cursor_get(cur, &k, &v, MDB_NEXT) == 0); @@ -2301,19 +2318,22 @@ static int ndb_note_meta_builder_counts(struct ndb_txn *txn, thread_replies = 0; total_reactions = 0; - if (!(entry = ndb_note_meta_add_entry(builder))) - return 0; - - rcs[0] = ndb_count_reactions(txn, note_id, &total_reactions); + rcs[0] = ndb_rebuild_reaction_metadata(txn, note_id, builder, &total_reactions); rcs[1] = ndb_count_quotes(txn, note_id, "es); rcs[2] = ndb_count_replies(txn, note_id, &direct_replies, &thread_replies); - if (!rcs[0] && !rcs[1] && !rcs[2]) + if (!rcs[0] && !rcs[1] && !rcs[2]) { return 0; + } /* no entry needed */ - if (quotes == 0 && direct_replies == 0 && thread_replies == 0 && quotes == 0) - return 1; + if (quotes == 0 && direct_replies == 0 && thread_replies == 0 && quotes == 0) { + return 0; + } + + if (!(entry = ndb_note_meta_add_entry(builder))) { + return 0; + } ndb_note_meta_counts_set(entry, total_reactions, quotes, direct_replies, thread_replies); @@ -2334,49 +2354,64 @@ static int ndb_migrate_metadata(struct ndb_txn *txn) { MDB_val k, k2, v, v2; MDB_cursor *cur; - MDB_dbi db; + MDB_dbi note_db, meta_db; unsigned char *id; - unsigned char buffer[4096]; - int rc; + size_t scratch_size = 1024 * 1024; + unsigned char *buffer = malloc(scratch_size); + int rc, count; struct ndb_note_meta_builder builder; + struct ndb_note *note; struct ndb_note_meta *meta; - db = txn->lmdb->dbs[NDB_DB_META]; + meta_db = txn->lmdb->dbs[NDB_DB_META]; + note_db = txn->lmdb->dbs[NDB_DB_NOTE]; - if ((rc = mdb_cursor_open(txn->mdb_txn, db, &cur))) { - fprintf(stderr, "ndb_migrate_reaction_stats: mdb_cursor_open failed, error %d\n", rc); + /* drop metadata table to avoid issues */ + if (mdb_drop(txn->mdb_txn, meta_db, 0)) { + fprintf(stderr, "ndb_migrate_metadata: mdb_drop failed\n"); return -1; } + if ((rc = mdb_cursor_open(txn->mdb_txn, note_db, &cur))) { + fprintf(stderr, "ndb_migrate_metadata: mdb_cursor_open failed, error %d\n", rc); + return -1; + } + + count = 0; + /* loop through every metadata entry */ while (mdb_cursor_get(cur, &k, &v, MDB_NEXT) == 0) { - ndb_note_meta_builder_init(&builder, buffer, sizeof(buffer)); + ndb_note_meta_builder_init(&builder, buffer, scratch_size); - id = (unsigned char *)k.mv_data; - ndb_note_meta_builder_count_reactions(txn, &builder); - ndb_note_meta_builder_counts(txn, id, &builder); - ndb_note_meta_build(&builder, &meta); + note = (struct ndb_note *)v.mv_data; + id = ndb_note_id(note); + k2.mv_data = (unsigned char *)id; + k2.mv_size = 32; - /* no counts found, just delete this entry */ - if (ndb_note_meta_entries_count(meta) == 0) { - if ((rc = mdb_del(txn->mdb_txn, db, &k, &v))) { - ndb_debug("delete old metadata entry failed: %s\n", mdb_strerror(rc)); - return -1; - } + rc = ndb_note_meta_builder_counts(txn, id, &builder); + if (!rc) { + mdb_del(txn->mdb_txn, meta_db, &k2, NULL); continue; } - k2.mv_data = (unsigned char *)id; - k2.mv_size = 32; + ndb_note_meta_build(&builder, &meta); + assert(ndb_note_meta_entries(meta)->type != 0); + v2.mv_data = meta; v2.mv_size = ndb_note_meta_total_size(meta); /* set entry */ - if ((rc = mdb_put(txn->mdb_txn, db, &k2, &v2, 0))) { + if ((rc = mdb_put(txn->mdb_txn, meta_db, &k2, &v2, 0))) { ndb_debug("migrate metadata entry failed on write: %s\n", mdb_strerror(rc)); } + + count++; } + fprintf(stderr, "nostrdb: migrated %d metadata entries\n", count); + + free(buffer); + mdb_cursor_close(cur); return 1; } @@ -2713,7 +2748,7 @@ static struct ndb_migration MIGRATIONS[] = { { .fn = ndb_migrate_lower_user_search_indices }, { .fn = ndb_migrate_utf8_profile_names }, { .fn = ndb_migrate_profile_indices }, - //{ .fn = ndb_migrate_metadata }, + { .fn = ndb_migrate_metadata }, }; @@ -8515,6 +8550,29 @@ void ndb_config_set_ingest_filter(struct ndb_config *config, config->filter_context = filter_ctx; } +int ndb_print_note_metadata(struct ndb_txn *txn) +{ + MDB_cursor *cur; + MDB_val k, v; + int i; + + if (mdb_cursor_open(txn->mdb_txn, txn->lmdb->dbs[NDB_DB_META], &cur)) + return 0; + + i = 1; + while (mdb_cursor_get(cur, &k, &v, MDB_NEXT) == 0) { + print_hex(k.mv_data, 32); + printf("\t"); + print_note_meta((struct ndb_note_meta*)v.mv_data); + i++; + } + + mdb_cursor_close(cur); + + return i; +} + + int ndb_print_author_kind_index(struct ndb_txn *txn) { MDB_cursor *cur; diff --git a/src/nostrdb.h b/src/nostrdb.h index 86a7df28..34d809af 100644 --- a/src/nostrdb.h +++ b/src/nostrdb.h @@ -49,8 +49,8 @@ struct bolt11; */ enum ndb_metadata_type { NDB_NOTE_META_RESERVED = 0, /* not used */ - NDB_NOTE_META_COUNTS = 2, /* replies, quotes, etc */ - NDB_NOTE_META_REACTION = 4, /* count of all the reactions on a post, grouped by different reaction strings */ + NDB_NOTE_META_COUNTS = 100, /* replies, quotes, etc */ + NDB_NOTE_META_REACTION = 200, /* count of all the reactions on a post, grouped by different reaction strings */ }; // some bindings like swift needs help with forward declared pointers @@ -658,6 +658,7 @@ struct ndb_note_meta *ndb_get_note_meta(struct ndb_txn *txn, const unsigned char int ndb_set_note_meta(struct ndb *ndb, const unsigned char *id, struct ndb_note_meta *meta); size_t ndb_note_meta_total_size(struct ndb_note_meta *header); int ndb_note_meta_builder_init(struct ndb_note_meta_builder *builder, unsigned char *, size_t); +struct ndb_note_meta_entry *ndb_note_meta_builder_find_entry(struct ndb_note_meta_builder *builder, uint16_t type, uint64_t *payload); void ndb_note_meta_build(struct ndb_note_meta_builder *builder, struct ndb_note_meta **meta); uint16_t ndb_note_meta_entries_count(struct ndb_note_meta *meta); struct ndb_note_meta_entry *ndb_note_meta_entries(struct ndb_note_meta *meta); @@ -673,6 +674,7 @@ uint16_t *ndb_note_meta_counts_direct_replies(struct ndb_note_meta_entry *entry) uint32_t *ndb_note_meta_counts_thread_replies(struct ndb_note_meta_entry *entry); uint16_t *ndb_note_meta_entry_type(struct ndb_note_meta_entry *entry); struct ndb_note_meta_entry *ndb_note_meta_entry_at(struct ndb_note_meta *meta, int ind); +void print_note_meta(struct ndb_note_meta *meta); // META STRINGS int ndb_reaction_set(union ndb_reaction_str *reaction, const char *str); diff --git a/test.c b/test.c index 1ea760e3..e90793e6 100644 --- a/test.c +++ b/test.c @@ -30,7 +30,7 @@ static void delete_test_db() { unlink(TEST_DIR "/data.lock"); } -int ndb_count_reactions(struct ndb_txn *txn, const unsigned char *note_id, uint32_t *count); +int ndb_rebuild_reaction_metadata(struct ndb_txn *txn, const unsigned char *note_id, struct ndb_note_meta_builder *builder, uint32_t *count); int ndb_count_replies(struct ndb_txn *txn, const unsigned char *note_id, uint16_t *direct_replies, uint32_t *thread_replies); static void db_load_events(struct ndb *ndb, const char *filename) @@ -141,7 +141,7 @@ static void test_count_metadata() ndb_begin_query(ndb, &txn); /* this is used in the migration code, * let's make sure it matches the online logic */ - ndb_count_reactions(&txn, id, &reactions); + ndb_rebuild_reaction_metadata(&txn, id, NULL, &reactions); printf("\t# after-counted reactions %d\n", reactions); assert(reactions == total_reactions);