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:
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);