damus

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

commit b4b84e689584506db4e4bf582cf15c300774bd37
parent 7831ede057fdee81fedbd634b2ed4d186ba76273
Author: William Casarin <jb55@jb55.com>
Date:   Thu, 13 Feb 2025 14:35:22 -0800

nostrdb: resync with repo

Signed-off-by: William Casarin <jb55@jb55.com>

Diffstat:
Mnostrdb/src/bolt11/bolt11.c | 606++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mnostrdb/src/bolt11/bolt11.h | 66+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Anostrdb/src/bolt11/str.h | 228+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anostrdb/src/bolt11/str_debug.h | 30++++++++++++++++++++++++++++++
Mnostrdb/src/content_parser.c | 10+++++-----
Mnostrdb/src/nostrdb.c | 70++++++++++++++++++++++++++++++++--------------------------------------
Mnostrdb/src/nostrdb.h | 8+++-----
7 files changed, 699 insertions(+), 319 deletions(-)

diff --git a/nostrdb/src/bolt11/bolt11.c b/nostrdb/src/bolt11/bolt11.c @@ -55,11 +55,10 @@ static struct multiplier multipliers[] = { }; /* If pad is false, we discard any bits which don't fit in the last byte. - * Otherwise we add an extra byte. Returns error string or NULL on success. */ -static const char *pull_bits(struct hash_u5 *hu5, - const u5 **data, size_t *data_len, - void *dst, size_t nbits, - bool pad) + * Otherwise we add an extra byte */ +static bool pull_bits(struct hash_u5 *hu5, + u5 **data, size_t *data_len, void *dst, size_t nbits, + bool pad) { size_t n5 = nbits / 5; size_t len = 0; @@ -68,67 +67,53 @@ static const char *pull_bits(struct hash_u5 *hu5, n5++; if (*data_len < n5) - return "truncated"; + return false; if (!bech32_convert_bits(dst, &len, 8, *data, n5, 5, pad)) - return "non-zero trailing bits"; + return false; if (hu5) hash_u5(hu5, *data, n5); *data += n5; *data_len -= n5; - return NULL; + return true; } +/* For pulling fields where we should have checked it will succeed already. */ +#ifndef NDEBUG +#define pull_bits_certain(hu5, data, data_len, dst, nbits, pad) \ + assert(pull_bits((hu5), (data), (data_len), (dst), (nbits), (pad))) +#else +#define pull_bits_certain pull_bits +#endif + /* Helper for pulling a variable-length big-endian int. */ -static const char *pull_uint(struct hash_u5 *hu5, - const u5 **data, size_t *data_len, - u64 *val, size_t databits) +static bool pull_uint(struct hash_u5 *hu5, + u5 **data, size_t *data_len, + u64 *val, size_t databits) { be64 be_val; - const char *err; /* Too big. */ if (databits > sizeof(be_val) * CHAR_BIT) - return "integer too large"; - err = pull_bits(hu5, data, data_len, &be_val, databits, true); - if (err) - return err; - if (databits == 0) - *val = 0; - else - *val = be64_to_cpu(be_val) >> - (sizeof(be_val) * CHAR_BIT - databits); - return NULL; + return false; + if (!pull_bits(hu5, data, data_len, &be_val, databits, true)) + return false; + *val = be64_to_cpu(be_val) >> (sizeof(be_val) * CHAR_BIT - databits); + return true; } -static void *pull_all(const tal_t *ctx, - struct hash_u5 *hu5, - const u5 **data, size_t *data_len, - bool pad, - const char **err) +static size_t num_u8(size_t num_u5) { - void *ret; - size_t retlen; - - if (pad) - retlen = (*data_len * 5 + 7) / 8; - else - retlen = (*data_len * 5) / 8; - - ret = tal_arr(ctx, u8, retlen); - *err = pull_bits(hu5, data, data_len, ret, *data_len * 5, pad); - if (*err) - return tal_free(ret); - return ret; + return (num_u5 * 5 + 4) / 8; } /* Frees bolt11, returns NULL. */ static struct bolt11 *decode_fail(struct bolt11 *b11, char **fail, - const char *fmt, ...) + const char *fmt, ...) PRINTF_FMT(3,4); static struct bolt11 *decode_fail(struct bolt11 *b11, char **fail, - const char *fmt, ...) + const char *fmt, ...) { va_list ap; @@ -142,33 +127,20 @@ static struct bolt11 *decode_fail(struct bolt11 *b11, char **fail, * These handle specific fields in the payment request; returning the problem * if any, or NULL. */ -static const char *unknown_field(struct bolt11 *b11, - struct hash_u5 *hu5, - const u5 **data, size_t *field_len, - u5 type) +static char *unknown_field(struct bolt11 *b11, + struct hash_u5 *hu5, + u5 **data, size_t *data_len, + u5 type, size_t length) { - const char *err; + struct bolt11_field *extra = tal(b11, struct bolt11_field); + u8 u8data[num_u8(length)]; - tal_free(pull_all(NULL, hu5, data, field_len, true, &err)); - return err; -} - -/* If field isn't expected length (in *bech32*!), call unknown_field. - * Otherwise copy into dst without padding, set have_flag if non-NULL. */ -static const char *pull_expected_length(struct bolt11 *b11, - struct hash_u5 *hu5, - const u5 **data, size_t *field_len, - size_t expected_length, - u5 type, - bool *have_flag, - void *dst) -{ - if (*field_len != expected_length) - return unknown_field(b11, hu5, data, field_len, type); + extra->tag = type; + extra->data = tal_dup_arr(extra, u5, *data, length, 0); + list_add_tail(&b11->extra_fields, &extra->list); - if (have_flag) - *have_flag = true; - return pull_bits(hu5, data, field_len, dst, *field_len * 5, false); + pull_bits_certain(hu5, data, data_len, u8data, length * 5, true); + return NULL; } /* BOLT #11: @@ -176,28 +148,34 @@ static const char *pull_expected_length(struct bolt11 *b11, * `p` (1): `data_length` 52. 256-bit SHA256 payment_hash. Preimage of this * provides proof of payment */ -static const char *decode_p(struct bolt11 *b11, - const struct feature_set *our_features, - struct hash_u5 *hu5, - const u5 **data, size_t *field_len, - bool *have_p) +static void decode_p(struct bolt11 *b11, + struct hash_u5 *hu5, + u5 **data, size_t *data_len, + size_t data_length, bool *have_p) { - struct sha256 payment_hash; /* BOLT #11: * * A payer... SHOULD use the first `p` field that it did NOT * skip as the payment hash. */ - assert(!*have_p); + if (*have_p) { + unknown_field(b11, hu5, data, data_len, 'p', data_length); + return; + } /* BOLT #11: * * A reader... MUST skip over unknown fields, OR an `f` field * with unknown `version`, OR `p`, `h`, `s` or `n` fields that do * NOT have `data_length`s of 52, 52, 52 or 53, respectively. - */ - return pull_expected_length(b11, hu5, data, field_len, 52, 'p', - have_p, &payment_hash); + */ + if (data_length != 52) { + unknown_field(b11, hu5, data, data_len, 'p', data_length); + return; + } + + pull_bits_certain(hu5, data, data_len, &b11->payment_hash, 256, false); + *have_p = true; } /* Check for valid UTF-8 */ @@ -235,24 +213,23 @@ static char *utf8_str(const tal_t *ctx, const u8 *buf TAKES, size_t buflen) return ret; } + /* BOLT #11: * * `d` (13): `data_length` variable. Short description of purpose of payment * (UTF-8), e.g. '1 cup of coffee' or 'ナンセンス 1杯' */ -static const char *decode_d(struct bolt11 *b11, - const struct feature_set *our_features, - struct hash_u5 *hu5, - const u5 **data, size_t *field_len, - bool *have_d) +static char *decode_d(struct bolt11 *b11, + struct hash_u5 *hu5, + u5 **data, size_t *data_len, + size_t data_length, bool *have_d) { u8 *desc; - const char *err; + if (*have_d) + return unknown_field(b11, hu5, data, data_len, 'd', data_length); - assert(!*have_d); - desc = pull_all(NULL, hu5, data, field_len, false, &err); - if (!desc) - return err; + desc = tal_arr(NULL, u8, data_length * 5 / 8); + pull_bits_certain(hu5, data, data_len, desc, data_length*5, false); *have_d = true; b11->description = utf8_str(b11, take(desc), tal_bytelen(desc)); @@ -269,28 +246,30 @@ static const char *decode_d(struct bolt11 *b11, * 639 bytes, but the transport mechanism for the description in that case is * transport specific and not defined here. */ -static const char *decode_h(struct bolt11 *b11, - const struct feature_set *our_features, - struct hash_u5 *hu5, - const u5 **data, size_t *field_len, - bool *have_h) +static void decode_h(struct bolt11 *b11, + struct hash_u5 *hu5, + u5 **data, size_t *data_len, + size_t data_length, bool *have_h) { - const char *err; - struct sha256 hash; + if (*have_h) { + unknown_field(b11, hu5, data, data_len, 'h', data_length); + return; + } - assert(!*have_h); /* BOLT #11: * * A reader... MUST skip over unknown fields, OR an `f` field * with unknown `version`, OR `p`, `h`, `s` or `n` fields that do * NOT have `data_length`s of 52, 52, 52 or 53, respectively. */ - err = pull_expected_length(b11, hu5, data, field_len, 52, 'h', - have_h, &hash); + if (data_length != 52) { + unknown_field(b11, hu5, data, data_len, 'h', data_length); + return; + } - /* If that gave us the hash, store it */ - if (*have_h) - b11->description_hash = tal_dup(b11, struct sha256, &hash); - return err; + b11->description_hash = tal(b11, struct sha256); + pull_bits_certain(hu5, data, data_len, b11->description_hash, 256, + false); + *have_h = true; } /* BOLT #11: @@ -299,149 +278,163 @@ static const char *decode_h(struct bolt11 *b11, * (big-endian). Default is 3600 (1 hour) if not specified. */ #define DEFAULT_X 3600 -static const char *decode_x(struct bolt11 *b11, - const struct feature_set *our_features, - struct hash_u5 *hu5, - const u5 **data, size_t *field_len, - bool *have_x) +static char *decode_x(struct bolt11 *b11, + struct hash_u5 *hu5, + u5 **data, size_t *data_len, + size_t data_length, bool *have_x) { - const char *err; - - assert(!*have_x); + if (*have_x) + return unknown_field(b11, hu5, data, data_len, 'x', + data_length); /* FIXME: Put upper limit in bolt 11 */ - err = pull_uint(hu5, data, field_len, &b11->expiry, *field_len * 5); - if (err) - return tal_fmt(b11, "x: %s", err); + if (!pull_uint(hu5, data, data_len, &b11->expiry, data_length * 5)) + return tal_fmt(b11, "x: length %zu chars is excessive", + *data_len); *have_x = true; return NULL; } -static struct bolt11 *new_bolt11(const tal_t *ctx, - const struct amount_msat *msat TAKES) +/* BOLT #11: + * + * `c` (24): `data_length` variable. `min_final_cltv_expiry` to use for the + * last HTLC in the route. Default is 18 if not specified. + */ +static char *decode_c(struct bolt11 *b11, + struct hash_u5 *hu5, + u5 **data, size_t *data_len, + size_t data_length, bool *have_c) { - struct bolt11 *b11 = tal(ctx, struct bolt11); + u64 c; + if (*have_c) + return unknown_field(b11, hu5, data, data_len, 'c', + data_length); - b11->description = NULL; - b11->description_hash = NULL; - b11->msat = NULL; - b11->expiry = DEFAULT_X; - - if (msat) - b11->msat = tal_dup(b11, struct amount_msat, msat); - return b11; + /* FIXME: Put upper limit in bolt 11 */ + if (!pull_uint(hu5, data, data_len, &c, data_length * 5)) + return tal_fmt(b11, "c: length %zu chars is excessive", + *data_len); + b11->min_final_cltv_expiry = (u32)c; + /* Can overflow, since c is 64 bits but value must be < 32 bits */ + if (b11->min_final_cltv_expiry != c) + return tal_fmt(b11, "c: %"PRIu64" is too large", c); + + *have_c = true; + return NULL; } -struct decoder { - /* What BOLT11 letter this is */ - const char letter; - /* If false, then any dups get treated as "unknown" fields */ - bool allow_duplicates; - /* Routine to decode: returns NULL if it decodes ok, and - * sets *have_field = true if it is not an unknown form. - * Otherwise returns error string (literal or tal off b11). */ - const char *(*decode)(struct bolt11 *b11, - const struct feature_set *our_features, - struct hash_u5 *hu5, - const u5 **data, size_t *field_len, - bool *have_field); -}; +static char *decode_n(struct bolt11 *b11, + struct hash_u5 *hu5, + u5 **data, size_t *data_len, + size_t data_length, bool *have_n) +{ + if (*have_n) + return unknown_field(b11, hu5, data, data_len, 'n', + data_length); -static const struct decoder decoders[] = { /* BOLT #11: * - * A payer... SHOULD use the first `p` field that it did NOT - * skip as the payment hash. + * A reader... MUST skip over unknown fields, OR an `f` field + * with unknown `version`, OR `p`, `h`, `s` or `n` fields that do + * NOT have `data_length`s of 52, 52, 52 or 53, respectively. */ + if (data_length != 53) + return unknown_field(b11, hu5, data, data_len, 'n', + data_length); + + pull_bits_certain(hu5, data, data_len, &b11->receiver_id.k, + data_length * 5, false); + /* + if (!node_id_valid(&b11->receiver_id)) + return tal_fmt(b11, "n: invalid pubkey %s", + node_id_to_hexstr(b11, &b11->receiver_id)); */ - { 'p', false, decode_p }, - { 'd', false, decode_d }, - { 'h', false, decode_h }, - { 'x', false, decode_x }, -}; -static const struct decoder *find_decoder(char c) -{ - for (size_t i = 0; i < ARRAY_SIZE(decoders); i++) { - if (decoders[i].letter == c) - return decoders + i; - } + *have_n = true; return NULL; } -static bool bech32_decode_alloc(const tal_t *ctx, - const char **hrp_ret, - const u5 **data_ret, - size_t *data_len, - const char *str) +/* BOLT #11: + * + * `m` (27): `data_length` variable. Additional metadata to attach to + * the payment. Note that the size of this field is limited by the + * maximum hop payload size. Long metadata fields reduce the maximum + * route length. + */ +static char *decode_m(struct bolt11 *b11, + struct hash_u5 *hu5, + u5 **data, size_t *data_len, + size_t data_length, + bool *have_m) { - char *hrp = tal_arr(ctx, char, strlen(str) - 6); - u5 *data = tal_arr(ctx, u5, strlen(str) - 8); + size_t mlen = (data_length * 5) / 8; - if (bech32_decode(hrp, data, data_len, str, (size_t)-1) - != BECH32_ENCODING_BECH32) { - tal_free(hrp); - tal_free(data); - return false; - } + if (*have_m) + return unknown_field(b11, hu5, data, data_len, 'm', + data_length); - /* We needed temporaries because these are const */ - *hrp_ret = hrp; - *data_ret = data; - return true; -} + b11->metadata = tal_arr(b11, u8, mlen); + pull_bits_certain(hu5, data, data_len, b11->metadata, + data_length * 5, false); -static bool has_lightning_prefix(const char *invstring) -{ - /* BOLT #11: - * - * If a URI scheme is desired, the current recommendation is to either - * use 'lightning:' as a prefix before the BOLT-11 encoding */ - return (strstarts(invstring, "lightning:") || - strstarts(invstring, "LIGHTNING:")); + *have_m = true; + return NULL; } -static char *str_lowering(const void *ctx, const char *string TAKES) +struct bolt11 *new_bolt11(const tal_t *ctx) { - char *ret; + struct bolt11 *b11 = tal(ctx, struct bolt11); - ret = tal_strdup(ctx, string); - for (char *p = ret; *p; p++) *p = tolower(*p); - return ret; -} + list_head_init(&b11->extra_fields); + b11->description = NULL; + b11->description_hash = NULL; + b11->fallbacks = NULL; + b11->msat = NULL; + b11->expiry = DEFAULT_X; + b11->features = tal_arr(b11, u8, 0); + /* BOLT #11: + * - if the `c` field (`min_final_cltv_expiry`) is not provided: + * - MUST use an expiry delta of at least 18 when making the payment + */ + b11->min_final_cltv_expiry = 18; + //b11->payment_secret = NULL; + b11->metadata = NULL; -static const char *to_canonical_invstr(const tal_t *ctx, - const char *invstring) -{ - if (has_lightning_prefix(invstring)) - invstring += strlen("lightning:"); - return str_lowering(ctx, invstring); + //if (msat) + //b11->msat = tal_dup(b11, struct amount_msat, msat); + return b11; } +/* Define sha256_eq. */ +//STRUCTEQ_DEF(sha256, 0, u); + /* Extracts signature but does not check it. */ -static struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, - const struct feature_set *our_features, - struct sha256 *hash, - const u5 **sig, - bool *have_n, - char **fail) +struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, u5 **sig, char **fail) { - const char *hrp, *prefix; - char *amountstr; - const u5 *data; + char *hrp, *amountstr, *prefix; + u5 *data; size_t data_len; - struct bolt11 *b11 = new_bolt11(ctx, NULL); + struct bolt11 *b11 = new_bolt11(ctx); struct hash_u5 hu5; - const char *err; - /* We don't need all of these, but in theory we could have 32 types */ - bool have_field[32]; + bool have_p = false, have_d = false, have_h = false, have_n = false, + have_x = false, have_c = false, have_m = false; - memset(have_field, 0, sizeof(have_field)); + /* BOLT #11: + * + * If a URI scheme is desired, the current recommendation is to either + * use 'lightning:' as a prefix before the BOLT-11 encoding + */ + if (strstarts(str, "lightning:") || strstarts(str, "LIGHTNING:")) + str += strlen("lightning:"); if (strlen(str) < 8) return decode_fail(b11, fail, "Bad bech32 string"); - if (!bech32_decode_alloc(b11, &hrp, &data, &data_len, str)) + hrp = tal_arr(b11, char, strlen(str) - 6); + data = tal_arr(b11, u5, strlen(str) - 8); + + if (bech32_decode(hrp, data, &data_len, str, (size_t)-1) + != BECH32_ENCODING_BECH32) return decode_fail(b11, fail, "Bad bech32 string"); /* For signature checking at the end. */ @@ -454,7 +447,7 @@ static struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, * `lntb` for Bitcoin testnet, `lntbs` for Bitcoin signet, and `lnbcrt` for Bitcoin regtest) * 1. `amount`: optional number in that currency, followed by an optional * `multiplier` letter. The unit encoded here is the 'social' convention of a payment unit -- in the case of Bitcoin the unit is 'bitcoin' NOT satoshis. - */ + */ prefix = tal_strndup(b11, hrp, strcspn(hrp, "0123456789")); /* BOLT #11: @@ -463,7 +456,7 @@ static struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, */ if (!strstarts(prefix, "ln")) return decode_fail(b11, fail, - "Prefix '%s' does not start with ln", prefix); + "Prefix '%s' does not start with ln", prefix); /* BOLT #11: * @@ -500,17 +493,17 @@ static struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, amount = strtoull(amountstr, &end, 10); if (amount == ULLONG_MAX && errno == ERANGE) return decode_fail(b11, fail, - "Invalid amount '%s'", amountstr); + "Invalid amount '%s'", amountstr); if (!*amountstr || *end) return decode_fail(b11, fail, - "Invalid amount postfix '%s'", end); + "Invalid amount postfix '%s'", end); /* BOLT #11: * * if the `multiplier` is present... MUST multiply * `amount` by the `multiplier` value to derive the * amount required for payment. - */ + */ b11->msat = tal(b11, struct amount_msat); /* BOLT #11: * @@ -520,8 +513,8 @@ static struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, */ if (amount * m10 % 10 != 0) return decode_fail(b11, fail, - "Invalid sub-millisatoshi amount" - " '%sp'", amountstr); + "Invalid sub-millisatoshi amount" + " '%sp'", amountstr); *b11->msat = amount_msat(amount * m10 / 10); } @@ -534,16 +527,12 @@ static struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, * 1. zero or more tagged parts * 1. `signature`: Bitcoin-style signature of above (520 bits) */ - err = pull_uint(&hu5, &data, &data_len, &b11->timestamp, 35); - if (err) - return decode_fail(b11, fail, - "Can't get 35-bit timestamp: %s", err); + if (!pull_uint(&hu5, &data, &data_len, &b11->timestamp, 35)) + return decode_fail(b11, fail, "Can't get 35-bit timestamp"); while (data_len > 520 / 5) { const char *problem = NULL; - u64 type, field_len64; - size_t field_len; - const struct decoder *decoder; + u64 type, data_length; /* BOLT #11: * @@ -553,72 +542,153 @@ static struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, * 1. `data_length` (10 bits, big-endian) * 1. `data` (`data_length` x 5 bits) */ - err = pull_uint(&hu5, &data, &data_len, &type, 5); - if (err) - return decode_fail(b11, fail, - "Can't get tag: %s", err); - err = pull_uint(&hu5, &data, &data_len, &field_len64, 10); - if (err) + if (!pull_uint(&hu5, &data, &data_len, &type, 5) + || !pull_uint(&hu5, &data, &data_len, &data_length, 10)) return decode_fail(b11, fail, - "Can't get length: %s", err); + "Can't get tag and length"); /* Can't exceed total data remaining. */ - if (field_len64 > data_len) + if (data_length > data_len) return decode_fail(b11, fail, "%c: truncated", - bech32_charset[type]); - - /* These are different types on 32 bit! But since data_len is - * also size_t, above check ensures this will fit. */ - field_len = field_len64; - assert(field_len == field_len64); - - /* Do this now: the decode function fixes up the data ptr */ - data_len -= field_len; - - decoder = find_decoder(bech32_charset[type]); - if (!decoder || (have_field[type] && !decoder->allow_duplicates)) { - problem = unknown_field(b11, &hu5, &data, &field_len, - bech32_charset[type]); - } else { - problem = decoder->decode(b11, our_features, &hu5, - &data, &field_len, &have_field[type]); + bech32_charset[type]); + + switch (bech32_charset[type]) { + case 'p': + decode_p(b11, &hu5, &data, &data_len, data_length, + &have_p); + break; + + case 'd': + problem = decode_d(b11, &hu5, &data, &data_len, + data_length, &have_d); + break; + + case 'h': + decode_h(b11, &hu5, &data, &data_len, data_length, + &have_h); + break; + + case 'n': + problem = decode_n(b11, &hu5, &data, + &data_len, data_length, + &have_n); + break; + + case 'x': + problem = decode_x(b11, &hu5, &data, + &data_len, data_length, + &have_x); + break; + + case 'c': + problem = decode_c(b11, &hu5, &data, + &data_len, data_length, + &have_c); + break; + + /* + case 'f': + problem = decode_f(b11, &hu5, &data, + &data_len, data_length); + break; + case 'r': + problem = decode_r(b11, &hu5, &data, &data_len, + data_length); + break; + case '9': + problem = decode_9(b11, our_features, &hu5, + &data, &data_len, + data_length); + break; + case 's': + problem = decode_s(b11, &hu5, &data, &data_len, + data_length, &have_s); + break; + */ + case 'm': + problem = decode_m(b11, &hu5, &data, &data_len, + data_length, &have_m); + break; + default: + unknown_field(b11, &hu5, &data, &data_len, + bech32_charset[type], data_length); } if (problem) return decode_fail(b11, fail, "%s", problem); - if (field_len) - return decode_fail(b11, fail, "%c: extra %zu bytes", - bech32_charset[type], field_len); } - if (!have_field[bech32_charset_rev['p']]) + if (!have_p) return decode_fail(b11, fail, "No valid 'p' field found"); - /* BOLT #11: - * A writer: - *... - * - MUST include either exactly one `d` or exactly one `h` field. - */ - /* FIXME: It doesn't actually say the reader must check though! */ - if (!have_field[bech32_charset_rev['d']] - && !have_field[bech32_charset_rev['h']]) - return decode_fail(b11, fail, - "must have either 'd' or 'h' field"); - - hash_u5_done(&hu5, hash); *sig = tal_dup_arr(ctx, u5, data, data_len, 0); - - *have_n = have_field[bech32_charset_rev['n']]; return b11; } -struct bolt11 *bolt11_decode_minimal(const tal_t *ctx, const char *str, - char **fail) +/* Decodes and checks signature; returns NULL on error. */ +struct bolt11 *bolt11_decode(const tal_t *ctx, const char *str, char **fail) { - const u5 *sigdata; - struct sha256 hash; - bool have_n; + u5 *sigdata; + size_t data_len; + u8 sig_and_recid[65]; + //secp256k1_ecdsa_recoverable_signature sig; + struct bolt11 *b11; + + b11 = bolt11_decode_nosig(ctx, str, &sigdata, fail); + if (!b11) + return NULL; + + /* BOLT #11: + * + * A writer...MUST set `signature` to a valid 512-bit + * secp256k1 signature of the SHA2 256-bit hash of the + * human-readable part, represented as UTF-8 bytes, + * concatenated with the data part (excluding the signature) + * with 0 bits appended to pad the data to the next byte + * boundary, with a trailing byte containing the recovery ID + * (0, 1, 2, or 3). + */ + data_len = tal_count(sigdata); + if (!pull_bits(NULL, &sigdata, &data_len, sig_and_recid, 520, false)) + return decode_fail(b11, fail, "signature truncated"); + + assert(data_len == 0); - str = to_canonical_invstr(ctx, str); - return bolt11_decode_nosig(ctx, str, NULL, &hash, &sigdata, &have_n, - fail); + /* + if (!secp256k1_ecdsa_recoverable_signature_parse_compact + (secp256k1_ctx, &sig, sig_and_recid, sig_and_recid[64])) + return decode_fail(b11, fail, "signature invalid"); + + secp256k1_ecdsa_recoverable_signature_convert(secp256k1_ctx, + &b11->sig, &sig); + */ + + /* BOLT #11: + * + * A reader... MUST check that the `signature` is valid (see + * the `n` tagged field specified below). ... A reader... + * MUST use the `n` field to validate the signature instead of + * performing signature recovery. + */ + /* + if (!have_n) { + struct pubkey k; + if (!secp256k1_ecdsa_recover(secp256k1_ctx, + &k.pubkey, + &sig, + (const u8 *)&hash)) + return decode_fail(b11, fail, + "signature recovery failed"); + node_id_from_pubkey(&b11->receiver_id, &k); + } else { + struct pubkey k; + if (!pubkey_from_node_id(&k, &b11->receiver_id)) + abort(); + if (!secp256k1_ecdsa_verify(secp256k1_ctx, &b11->sig, + (const u8 *)&hash, + &k.pubkey)) + return decode_fail(b11, fail, "invalid signature"); + } + */ + + return b11; } diff --git a/nostrdb/src/bolt11/bolt11.h b/nostrdb/src/bolt11/bolt11.h @@ -1,6 +1,5 @@ #ifndef LIGHTNING_COMMON_BOLT11_H #define LIGHTNING_COMMON_BOLT11_H -/* Borrowed from CLN's common/bolt11.[ch] implementation as of v24.08rc1 */ #include "ccan/short_types/short_types.h" #include "hash_u5.h" @@ -8,6 +7,7 @@ #include "ccan/list/list.h" #include "amount.h" #include "node_id.h" +//#include <secp256k1_recovery.h> /* We only have 10 bits for the field length, meaning < 640 bytes */ #define BOLT11_FIELD_BYTE_LIMIT ((1 << 10) * 5 / 8) @@ -28,19 +28,79 @@ struct bolt11_field { u5 *data; }; +/* BOLT #11: + * * `pubkey` (264 bits) + * * `short_channel_id` (64 bits) + * * `fee_base_msat` (32 bits, big-endian) + * * `fee_proportional_millionths` (32 bits, big-endian) + * * `cltv_expiry_delta` (16 bits, big-endian) + */ + +/* +struct route_info { + struct node_id pubkey; + u16 cltv_expiry_delta; + struct short_channel_id short_channel_id; + u32 fee_base_msat, fee_proportional_millionths; +}; + */ + struct bolt11 { + const struct chainparams *chain; u64 timestamp; struct amount_msat *msat; /* NULL if not specified. */ + struct sha256 payment_hash; + struct node_id receiver_id; + /* description_hash valid if and only if description is NULL. */ const char *description; struct sha256 *description_hash; /* How many seconds to pay from @timestamp above. */ u64 expiry; + + /* How many blocks final hop requires. */ + u32 min_final_cltv_expiry; + + /* If non-NULL, indicates fallback addresses to pay to. */ + const u8 **fallbacks; + + /* If non-NULL: array of route arrays */ + //struct route_info **routes; + + /* signature of sha256 of entire thing. */ + //secp256k1_ecdsa_signature sig; + + /* payment secret, if any. */ + //struct secret *payment_secret; + + /* Features bitmap, if any. */ + u8 *features; + + /* Optional metadata to send with payment. */ + u8 *metadata; + + struct list_head extra_fields; }; -/* Does not check signature, nor extract node. */ -struct bolt11 *bolt11_decode_minimal(const tal_t *ctx, const char *str, char **fail); +/* Decodes and checks signature; returns NULL on error; description is + * (optional) out-of-band description of payment, for `h` field. + * fset is NULL to accept any features (usually not desirable!). + * + * if @must_be_chain is not NULL, fails unless it's this chain. + */ +struct bolt11 *bolt11_decode(const tal_t *ctx, const char *str, char **fail); + +/* Extracts signature but does not check it. */ +struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, u5 **sigdata, char **fail); + +/* Initialize an empty bolt11 struct with optional amount */ +struct bolt11 *new_bolt11(const tal_t *ctx); + +#if DEVELOPER +/* Flag for tests to suppress `min_final_cltv_expiry` field generation, to match test vectors */ +extern bool dev_bolt11_no_c_generation; +#endif #endif /* LIGHTNING_COMMON_BOLT11_H */ diff --git a/nostrdb/src/bolt11/str.h b/nostrdb/src/bolt11/str.h @@ -0,0 +1,228 @@ +/* CC0 (Public domain) - see LICENSE file for details */ +#ifndef CCAN_STR_H +#define CCAN_STR_H +#include "../config.h" +#include <string.h> +#include <stdbool.h> +#include <limits.h> +#include <ctype.h> + +/** + * streq - Are two strings equal? + * @a: first string + * @b: first string + * + * This macro is arguably more readable than "!strcmp(a, b)". + * + * Example: + * if (streq(somestring, "")) + * printf("String is empty!\n"); + */ +#define streq(a,b) (strcmp((a),(b)) == 0) + +/** + * strstarts - Does this string start with this prefix? + * @str: string to test + * @prefix: prefix to look for at start of str + * + * Example: + * if (strstarts(somestring, "foo")) + * printf("String %s begins with 'foo'!\n", somestring); + */ +#define strstarts(str,prefix) (strncmp((str),(prefix),strlen(prefix)) == 0) + +/** + * strends - Does this string end with this postfix? + * @str: string to test + * @postfix: postfix to look for at end of str + * + * Example: + * if (strends(somestring, "foo")) + * printf("String %s end with 'foo'!\n", somestring); + */ +static inline bool strends(const char *str, const char *postfix) +{ + if (strlen(str) < strlen(postfix)) + return false; + + return streq(str + strlen(str) - strlen(postfix), postfix); +} + +/** + * stringify - Turn expression into a string literal + * @expr: any C expression + * + * Example: + * #define PRINT_COND_IF_FALSE(cond) \ + * ((cond) || printf("%s is false!", stringify(cond))) + */ +#define stringify(expr) stringify_1(expr) +/* Double-indirection required to stringify expansions */ +#define stringify_1(expr) #expr + +/** + * strcount - Count number of (non-overlapping) occurrences of a substring. + * @haystack: a C string + * @needle: a substring + * + * Example: + * assert(strcount("aaa aaa", "a") == 6); + * assert(strcount("aaa aaa", "ab") == 0); + * assert(strcount("aaa aaa", "aa") == 2); + */ +size_t strcount(const char *haystack, const char *needle); + +/** + * STR_MAX_CHARS - Maximum possible size of numeric string for this type. + * @type_or_expr: a pointer or integer type or expression. + * + * This provides enough space for a nul-terminated string which represents the + * largest possible value for the type or expression. + * + * Note: The implementation adds extra space so hex values or negative + * values will fit (eg. sprintf(... "%p"). ) + * + * Example: + * char str[STR_MAX_CHARS(int)]; + * + * sprintf(str, "%i", 7); + */ +#define STR_MAX_CHARS(type_or_expr) \ + ((sizeof(type_or_expr) * CHAR_BIT + 8) / 9 * 3 + 2 \ + + STR_MAX_CHARS_TCHECK_(type_or_expr)) + +#if HAVE_TYPEOF +/* Only a simple type can have 0 assigned, so test that. */ +#define STR_MAX_CHARS_TCHECK_(type_or_expr) \ + (sizeof(({ typeof(type_or_expr) x = 0; x; }))*0) +#else +#define STR_MAX_CHARS_TCHECK_(type_or_expr) 0 +#endif + +/** + * cisalnum - isalnum() which takes a char (and doesn't accept EOF) + * @c: a character + * + * Surprisingly, the standard ctype.h isalnum() takes an int, which + * must have the value of EOF (-1) or an unsigned char. This variant + * takes a real char, and doesn't accept EOF. + */ +static inline bool cisalnum(char c) +{ + return isalnum((unsigned char)c); +} +static inline bool cisalpha(char c) +{ + return isalpha((unsigned char)c); +} +static inline bool cisascii(char c) +{ + return isascii((unsigned char)c); +} +#if HAVE_ISBLANK +static inline bool cisblank(char c) +{ + return isblank((unsigned char)c); +} +#endif +static inline bool ciscntrl(char c) +{ + return iscntrl((unsigned char)c); +} +static inline bool cisdigit(char c) +{ + return isdigit((unsigned char)c); +} +static inline bool cisgraph(char c) +{ + return isgraph((unsigned char)c); +} +static inline bool cislower(char c) +{ + return islower((unsigned char)c); +} +static inline bool cisprint(char c) +{ + return isprint((unsigned char)c); +} +static inline bool cispunct(char c) +{ + return ispunct((unsigned char)c); +} +static inline bool cisspace(char c) +{ + return isspace((unsigned char)c); +} +static inline bool cisupper(char c) +{ + return isupper((unsigned char)c); +} +static inline bool cisxdigit(char c) +{ + return isxdigit((unsigned char)c); +} + +#include "str_debug.h" + +/* These checks force things out of line, hence they are under DEBUG. */ +#ifdef CCAN_STR_DEBUG +#include <ccan/build_assert/build_assert.h> + +/* These are commonly misused: they take -1 or an *unsigned* char value. */ +#undef isalnum +#undef isalpha +#undef isascii +#undef isblank +#undef iscntrl +#undef isdigit +#undef isgraph +#undef islower +#undef isprint +#undef ispunct +#undef isspace +#undef isupper +#undef isxdigit + +/* You can use a char if char is unsigned. */ +#if HAVE_BUILTIN_TYPES_COMPATIBLE_P && HAVE_TYPEOF +#define str_check_arg_(i) \ + ((i) + BUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(typeof(i), \ + char) \ + || (char)255 > 0)) +#else +#define str_check_arg_(i) (i) +#endif + +#define isalnum(i) str_isalnum(str_check_arg_(i)) +#define isalpha(i) str_isalpha(str_check_arg_(i)) +#define isascii(i) str_isascii(str_check_arg_(i)) +#if HAVE_ISBLANK +#define isblank(i) str_isblank(str_check_arg_(i)) +#endif +#define iscntrl(i) str_iscntrl(str_check_arg_(i)) +#define isdigit(i) str_isdigit(str_check_arg_(i)) +#define isgraph(i) str_isgraph(str_check_arg_(i)) +#define islower(i) str_islower(str_check_arg_(i)) +#define isprint(i) str_isprint(str_check_arg_(i)) +#define ispunct(i) str_ispunct(str_check_arg_(i)) +#define isspace(i) str_isspace(str_check_arg_(i)) +#define isupper(i) str_isupper(str_check_arg_(i)) +#define isxdigit(i) str_isxdigit(str_check_arg_(i)) + +#if HAVE_TYPEOF +/* With GNU magic, we can make const-respecting standard string functions. */ +#undef strstr +#undef strchr +#undef strrchr + +/* + 0 is needed to decay array into pointer. */ +#define strstr(haystack, needle) \ + ((typeof((haystack) + 0))str_strstr((haystack), (needle))) +#define strchr(haystack, c) \ + ((typeof((haystack) + 0))str_strchr((haystack), (c))) +#define strrchr(haystack, c) \ + ((typeof((haystack) + 0))str_strrchr((haystack), (c))) +#endif +#endif /* CCAN_STR_DEBUG */ + +#endif /* CCAN_STR_H */ diff --git a/nostrdb/src/bolt11/str_debug.h b/nostrdb/src/bolt11/str_debug.h @@ -0,0 +1,30 @@ +/* CC0 (Public domain) - see LICENSE file for details */ +#ifndef CCAN_STR_DEBUG_H +#define CCAN_STR_DEBUG_H + +/* #define CCAN_STR_DEBUG 1 */ + +#ifdef CCAN_STR_DEBUG +/* Because we mug the real ones with macros, we need our own wrappers. */ +int str_isalnum(int i); +int str_isalpha(int i); +int str_isascii(int i); +#if HAVE_ISBLANK +int str_isblank(int i); +#endif +int str_iscntrl(int i); +int str_isdigit(int i); +int str_isgraph(int i); +int str_islower(int i); +int str_isprint(int i); +int str_ispunct(int i); +int str_isspace(int i); +int str_isupper(int i); +int str_isxdigit(int i); + +char *str_strstr(const char *haystack, const char *needle); +char *str_strchr(const char *s, int c); +char *str_strrchr(const char *s, int c); +#endif /* CCAN_STR_DEBUG */ + +#endif /* CCAN_STR_DEBUG_H */ diff --git a/nostrdb/src/content_parser.c b/nostrdb/src/content_parser.c @@ -91,8 +91,8 @@ static int parse_hashtag(struct cursor *cur, struct ndb_block *block) { // // bech32 blocks are stored as: // -// bech32_buffer_size : u16 // nostr_bech32_type : varint +// bech32_buffer_size : u16 // bech32_data : [u8] // // The TLV form is compact already, so we just use it directly @@ -168,7 +168,7 @@ static int push_invoice_str(struct ndb_content_parser *p, struct ndb_str_block * struct bolt11 *bolt11; char *fail; - if (!(bolt11 = bolt11_decode_minimal(NULL, str->str, &fail))) + if (!(bolt11 = bolt11_decode(NULL, str->str, &fail))) return 0; start = p->buffer.p; @@ -522,7 +522,7 @@ int ndb_parse_content(unsigned char *buf, int buf_size, struct ndb_content_parser parser; struct ndb_block block; - unsigned char *start, *pre_mention; + unsigned char *start, *pre_mention, *blocks_start; make_cursor(buf, buf + buf_size, &parser.buffer); @@ -539,7 +539,7 @@ int ndb_parse_content(unsigned char *buf, int buf_size, parser.blocks->flags = 0; parser.blocks->version = 1; - start = parser.content.p; + blocks_start = start = parser.content.p; while (parser.content.p < parser.content.end) { cp = peek_char(&parser.content, -1); c = peek_char(&parser.content, 0); @@ -577,7 +577,7 @@ int ndb_parse_content(unsigned char *buf, int buf_size, return 0; } - parser.blocks->blocks_size = parser.buffer.p - parser.buffer.start; + parser.blocks->blocks_size = parser.buffer.p - blocks_start; // // pad to 8-byte alignment diff --git a/nostrdb/src/nostrdb.c b/nostrdb/src/nostrdb.c @@ -257,7 +257,7 @@ struct ndb_search_words // str: cstr // timestamp: varint // word_index: varint -// +// static int ndb_make_text_search_key(unsigned char *buf, int bufsize, int word_index, int word_len, const char *str, uint64_t timestamp, uint64_t note_id, @@ -453,7 +453,7 @@ static inline int ndb_unpack_text_search_key_string(struct cursor *cur, if (!cursor_skip(cur, *str_len)) return 0; - + return 1; } @@ -527,7 +527,7 @@ int ndb_filter_clone(struct ndb_filter *dst, struct ndb_filter *src) data_size = src->data_buf.end - src->data_buf.start; src_size = data_size + elem_size; - // let's only allow finalized filters to be cloned + // let's only allow finalized filters to be cloned if (!src || !src->finalized) return 0; @@ -584,7 +584,7 @@ int ndb_filter_end(struct ndb_filter *filter) filter->finalized = 1; ndb_debug("ndb_filter_end: %ld -> %ld\n", orig_size, elem_len + data_len); - + return 1; } @@ -649,12 +649,6 @@ ndb_filter_get_string_element(const struct ndb_filter *filter, const struct ndb_ return (const char *)ndb_filter_elements_data(filter, els->elements[index]); } -uint64_t * -ndb_filter_get_int_element_ptr(struct ndb_filter_elements *els, int index) -{ - return &els->elements[index]; -} - uint64_t ndb_filter_get_int_element(const struct ndb_filter_elements *els, int index) { @@ -1238,7 +1232,7 @@ static int ndb_write_profile_search_index(struct ndb_txn *txn, { int rc; MDB_val key, val; - + key.mv_data = index_key; key.mv_size = sizeof(*index_key); val.mv_data = &profile_key; @@ -1499,7 +1493,7 @@ static inline void ndb_tsid_high(struct ndb_tsid *key, const unsigned char *id) } enum ndb_ingester_msgtype { - NDB_INGEST_EVENT, // write json to the ingester queue for processing + NDB_INGEST_EVENT, // write json to the ingester queue for processing NDB_INGEST_QUIT, // kill ingester thread immediately }; @@ -1645,7 +1639,7 @@ static struct ndb_migration MIGRATIONS[] = { int ndb_end_query(struct ndb_txn *txn) { - // this works on read or write queries. + // this works on read or write queries. return mdb_txn_commit(txn->mdb_txn) == 0; } @@ -1691,7 +1685,7 @@ static void ndb_writer_last_profile_fetch(struct ndb_txn *txn, { int rc; MDB_val key, val; - + key.mv_data = (unsigned char*)pubkey; key.mv_size = 32; val.mv_data = &fetched_at; @@ -1967,7 +1961,7 @@ static int ndbprofile_parse_json(flatcc_builder_t *B, NdbProfile_parse_json_table(ctx, buf, buf + bufsiz, profile); if (ctx->error) return 0; - + if (!flatcc_builder_end_buffer(B, *profile)) return 0; @@ -2069,7 +2063,7 @@ static int ndb_ingester_process_note(secp256k1_context *ctx, assert(((uint64_t)note % 4) == 0); if (note->kind == 0) { - struct ndb_profile_record_builder *b = + struct ndb_profile_record_builder *b = &out->profile.record; ndb_process_profile_note(note, b); @@ -2122,7 +2116,7 @@ static int ndb_ingester_process_event(secp256k1_context *ctx, } note_size = - ev->client ? + ev->client ? ndb_client_event_from_json(ev->json, ev->len, &fce, buf, bufsize, &cb) : ndb_ws_event_from_json(ev->json, ev->len, &tce, buf, bufsize, &cb); @@ -2317,7 +2311,7 @@ static int ndb_search_key_cmp(const MDB_val *a, const MDB_val *b) } static int ndb_write_profile_pk_index(struct ndb_txn *txn, struct ndb_note *note, uint64_t profile_key) - + { MDB_val key, val; int rc; @@ -2355,7 +2349,7 @@ static int ndb_write_profile(struct ndb_txn *txn, MDB_val key, val; MDB_dbi profile_db; - + note = profile->note.note; // add note_key to profile record @@ -2481,7 +2475,7 @@ static int ndb_write_reaction_stats(struct ndb_txn *txn, struct ndb_note *note) } else { // clone existing and add to it meta = NdbEventMeta_as_root(root); - + reactions = NdbEventMeta_reactions_get(meta); NdbEventMeta_clone(&builder, meta); NdbEventMeta_reactions_add(&builder, reactions + 1); @@ -2500,7 +2494,7 @@ static int ndb_write_reaction_stats(struct ndb_txn *txn, struct ndb_note *note) // if we have the note yet or not key.mv_data = liked; key.mv_size = 32; - + val.mv_data = root; val.mv_size = len; @@ -2522,7 +2516,7 @@ static int ndb_write_reaction_stats(struct ndb_txn *txn, struct ndb_note *note) static int ndb_write_note_id_index(struct ndb_txn *txn, struct ndb_note *note, uint64_t note_key) - + { struct ndb_tsid tsid; int rc; @@ -3341,7 +3335,7 @@ static int ndb_prefix_matches(struct ndb_text_search_result *result, // matches are nice but range searches allow us to match prefixes as // well. A double-char prefix is suffient, but maybe we could up this // in the future. - // + // // TODO: How are we handling utf-8 prefix matches like // japanese? // @@ -3357,7 +3351,7 @@ static int ndb_prefix_matches(struct ndb_text_search_result *result, search_word->word, search_word->word_len); - if (result->prefix_chars <= (int)((double)search_word->word_len / 1.5)) + if (result->prefix_chars <= (int)((double)search_word->word_len / 1.5)) return 0; return 1; @@ -3530,7 +3524,7 @@ int ndb_text_search(struct ndb_txn *txn, const char *query, limit = min(limit, config->limit); } // end search config - + text_db = txn->lmdb->dbs[NDB_DB_NOTE_TEXT]; make_cursor((unsigned char *)query, (unsigned char *)query + strlen(query), &cur); @@ -3706,7 +3700,7 @@ static uint64_t ndb_write_note(struct ndb_txn *txn, // let's quickly sanity check if we already have this note if (ndb_get_notekey_by_id(txn, note->note->id)) return 0; - + // get dbs note_db = txn->lmdb->dbs[NDB_DB_NOTE]; @@ -3753,7 +3747,7 @@ static void ndb_write_version(struct ndb_txn *txn, uint64_t version) uint64_t version_key; version_key = NDB_META_KEY_VERSION; - + key.mv_data = &version_key; key.mv_size = sizeof(version_key); val.mv_data = &version; @@ -3871,7 +3865,7 @@ static void *ndb_writer_thread(void *data) done = 1; continue; case NDB_WRITER_PROFILE: - note_nkey = + note_nkey = ndb_write_note(&txn, &msg->note, scratch, scratch_size); if (note_nkey > 0) { @@ -4010,7 +4004,7 @@ static void *ndb_ingester_thread(void *data) if (to_write > 0) { ndb_debug("pushing %d events to write queue\n", to_write); if (!ndb_writer_queue_msgs(ingester->writer, outs, to_write)) { - ndb_debug("failed pushing %d events to write queue\n", to_write); + ndb_debug("failed pushing %d events to write queue\n", to_write); } } } @@ -4043,7 +4037,7 @@ static int ndb_writer_init(struct ndb_writer *writer, struct ndb_lmdb *lmdb, fprintf(stderr, "ndb writer thread failed to create\n"); return 0; } - + return 1; } @@ -4247,7 +4241,7 @@ static int ndb_queue_write_version(struct ndb *ndb, uint64_t version) static int ndb_run_migrations(struct ndb *ndb) { int64_t version, latest_version, i; - + latest_version = sizeof(MIGRATIONS) / sizeof(MIGRATIONS[0]); if ((version = ndb_db_version(ndb)) == -1) { @@ -4399,7 +4393,7 @@ int ndb_process_client_event(struct ndb *ndb, const char *json, int len) // Process anostr event from a relay, // // ie: ["EVENT", "subid", {"content":"..."}...] -// +// // This function returns as soon as possible, first copying the passed // json and then queueing it up for processing. Worker threads then take // the json and process it. @@ -5071,7 +5065,7 @@ static inline int ndb_builder_find_str(struct ndb_builder *builder, uint32_t index = ((uint32_t*)builder->str_indices.start)[i]; const char *some_str = (const char*)builder->strings.start + index; - if (!memcmp(some_str, str, len) && some_str[len] == '\0') { + if (!memcmp(some_str, str, len)) { // found an existing matching str, use that index *pstr = ndb_offset_str(index); return 1; @@ -5222,7 +5216,7 @@ static int ndb_builder_make_json_str(struct ndb_builder *builder, int *written, int pack_ids) { // let's not care about de-duping these. we should just unescape - // in-place directly into the strings table. + // in-place directly into the strings table. if (written) *written = len; @@ -5422,7 +5416,7 @@ int ndb_ws_event_from_json(const char *json, int len, struct ndb_tce *tce, tce->subid_len = toksize(tok); return ndb_parse_json_note(&parser, &ev->note); - } else if (tok_len == 4 && !memcmp("EOSE", json + tok->start, 4)) { + } else if (tok_len == 4 && !memcmp("EOSE", json + tok->start, 4)) { tce->evtype = NDB_TCE_EOSE; tok = &parser.toks[parser.i++]; @@ -5984,7 +5978,7 @@ int ndb_stat(struct ndb *ndb, struct ndb_stat *stat) } /// Push an element to the current tag -/// +/// /// Basic idea is to call ndb_builder_new_tag inline int ndb_builder_push_tag_str(struct ndb_builder *builder, const char *str, int len) @@ -5998,7 +5992,7 @@ inline int ndb_builder_push_tag_str(struct ndb_builder *builder, // // CONFIG -// +// void ndb_default_config(struct ndb_config *config) { int cores = get_cpu_cores(); @@ -6478,7 +6472,7 @@ uint64_t ndb_subscribe(struct ndb *ndb, struct ndb_filter *filters, int num_filt ndb_filter_group_init(&sub->group); if (!ndb_filter_group_add_filters(&sub->group, filters, num_filters)) return 0; - + // 500k ought to be enough for anyone buflen = sizeof(uint64_t) * 65536; buf = malloc(buflen); diff --git a/nostrdb/src/nostrdb.h b/nostrdb/src/nostrdb.h @@ -245,6 +245,7 @@ struct ndb_filter_elements { int count; // this needs to be pointer size for reasons + // FIXME: what about on 32bit systems?? uint64_t elements[0]; }; @@ -353,8 +354,6 @@ struct bech32_nevent { struct ndb_relays relays; const unsigned char *event_id; const unsigned char *pubkey; // optional - uint32_t kind; - int has_kind; }; struct bech32_nprofile { @@ -374,7 +373,7 @@ struct bech32_nrelay { struct ndb_str_block relay; }; -typedef struct nostr_bech32 { +struct nostr_bech32 { enum nostr_bech32_type type; union { @@ -386,7 +385,7 @@ typedef struct nostr_bech32 { struct bech32_naddr naddr; struct bech32_nrelay nrelay; }; -} nostr_bech32_t; +}; struct ndb_mention_bech32_block { @@ -503,7 +502,6 @@ int ndb_filter_from_json(const char *, int len, struct ndb_filter *filter, unsig unsigned char *ndb_filter_get_id_element(const struct ndb_filter *, const struct ndb_filter_elements *, int index); const char *ndb_filter_get_string_element(const struct ndb_filter *, const struct ndb_filter_elements *, int index); uint64_t ndb_filter_get_int_element(const struct ndb_filter_elements *, int index); -uint64_t *ndb_filter_get_int_element_ptr(struct ndb_filter_elements *, int index); struct ndb_filter_elements *ndb_filter_current_element(const struct ndb_filter *); struct ndb_filter_elements *ndb_filter_get_elements(const struct ndb_filter *, int);