From fa4b61d13d19daba938cecae4d1d37de5958b3b3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 12 Jan 2023 11:51:13 +1030 Subject: [PATCH] common/bolt11: convert to table-driven. Decode functions are now almost entirely uniform, so just use a table. Signed-off-by: Rusty Russell --- common/bolt11.c | 171 ++++++++++++++++++++++++++---------------------- 1 file changed, 92 insertions(+), 79 deletions(-) diff --git a/common/bolt11.c b/common/bolt11.c index 88270ae8a..4b00e8535 100644 --- a/common/bolt11.c +++ b/common/bolt11.c @@ -177,6 +177,7 @@ static const char *pull_expected_length(struct bolt11 *b11, * 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) @@ -186,8 +187,7 @@ static const char *decode_p(struct bolt11 *b11, * A payer... SHOULD use the first `p` field that it did NOT * skip as the payment hash. */ - if (*have_p) - return unknown_field(b11, hu5, data, field_len, 'p'); + assert(!*have_p); /* BOLT #11: * @@ -205,6 +205,7 @@ static const char *decode_p(struct bolt11 *b11, * (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) @@ -212,9 +213,7 @@ static const char *decode_d(struct bolt11 *b11, u8 *desc; const char *err; - if (*have_d) - return unknown_field(b11, hu5, data, field_len, 'd'); - + assert(!*have_d); desc = pull_all(NULL, hu5, data, field_len, false, &err); if (!desc) return err; @@ -235,6 +234,7 @@ static const char *decode_d(struct bolt11 *b11, * 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) @@ -242,9 +242,7 @@ static const char *decode_h(struct bolt11 *b11, const char *err; struct sha256 hash; - if (*have_h) - return unknown_field(b11, hu5, data, field_len, 'h'); - + assert(!*have_h); /* BOLT #11: * * A reader... MUST skip over unknown fields, OR an `f` field @@ -266,14 +264,14 @@ static const char *decode_h(struct bolt11 *b11, */ #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) { const char *err; - if (*have_x) - return unknown_field(b11, hu5, data, field_len, 'x'); + assert(!*have_x); /* FIXME: Put upper limit in bolt 11 */ err = pull_uint(hu5, data, field_len, &b11->expiry, *field_len * 5); @@ -290,6 +288,7 @@ static const char *decode_x(struct bolt11 *b11, * last HTLC in the route. Default is 18 if not specified. */ static const char *decode_c(struct bolt11 *b11, + const struct feature_set *our_features, struct hash_u5 *hu5, const u5 **data, size_t *field_len, bool *have_c) @@ -297,8 +296,7 @@ static const char *decode_c(struct bolt11 *b11, u64 c; const char *err; - if (*have_c) - return unknown_field(b11, hu5, data, field_len, 'c'); + assert(!*have_c); /* FIXME: Put upper limit in bolt 11 */ err = pull_uint(hu5, data, field_len, &c, *field_len * 5); @@ -314,13 +312,12 @@ static const char *decode_c(struct bolt11 *b11, } static const char *decode_n(struct bolt11 *b11, + const struct feature_set *our_features, struct hash_u5 *hu5, const u5 **data, size_t *field_len, bool *have_n) { - if (*have_n) - return unknown_field(b11, hu5, data, field_len, 'n'); - + assert(!*have_n); /* BOLT #11: * * A reader... MUST skip over unknown fields, OR an `f` field @@ -336,6 +333,7 @@ static const char *decode_n(struct bolt11 *b11, * forwarding nodes from probing the payment recipient. */ static const char *decode_s(struct bolt11 *b11, + const struct feature_set *our_features, struct hash_u5 *hu5, const u5 **data, size_t *field_len, bool *have_s) @@ -343,8 +341,7 @@ static const char *decode_s(struct bolt11 *b11, const char *err; struct secret secret; - if (*have_s) - return unknown_field(b11, hu5, data, field_len, 's'); + assert(!*have_s); /* BOLT #11: * @@ -365,8 +362,10 @@ static const char *decode_s(struct bolt11 *b11, * and contains a witness program or P2PKH or P2SH address. */ static const char *decode_f(struct bolt11 *b11, + const struct feature_set *our_features, struct hash_u5 *hu5, - const u5 **data, size_t *field_len) + const u5 **data, size_t *field_len, + bool *have_f) { u64 version; u8 *fallback; @@ -428,6 +427,7 @@ static const char *decode_f(struct bolt11 *b11, b11->fallbacks[tal_count(b11->fallbacks)-1] = tal_steal(b11->fallbacks, fallback); + *have_f = true; return NULL; } @@ -464,8 +464,10 @@ static void towire_route_info(u8 **pptr, const struct route_info *route_info) * * `cltv_expiry_delta` (16 bits, big-endian) */ static const char *decode_r(struct bolt11 *b11, + const struct feature_set *our_features, struct hash_u5 *hu5, - const u5 **data, size_t *field_len) + const u5 **data, size_t *field_len, + bool *have_r) { const u8 *r8; size_t n = 0; @@ -489,6 +491,7 @@ static const char *decode_r(struct bolt11 *b11, /* Append route */ tal_arr_expand(&b11->routes, r); + *have_r = true; return NULL; } @@ -515,13 +518,16 @@ static void shift_bitmap_down(u8 *bitmap, size_t bits) static const char *decode_9(struct bolt11 *b11, const struct feature_set *our_features, struct hash_u5 *hu5, - const u5 **data, size_t *field_len) + const u5 **data, size_t *field_len, + bool *have_9) { size_t flen = (*field_len * 5 + 7) / 8; int badf; size_t databits = *field_len * 5; const char *err; + assert(!*have_9); + b11->features = pull_all(b11, hu5, data, field_len, true, &err); if (!b11->features) return err; @@ -545,6 +551,7 @@ static const char *decode_9(struct bolt11 *b11, return tal_fmt(b11, "9: unknown feature bit %i", badf); } + *have_9 = true; return NULL; } @@ -556,14 +563,14 @@ static const char *decode_9(struct bolt11 *b11, * route length. */ static const char *decode_m(struct bolt11 *b11, + const struct feature_set *our_features, struct hash_u5 *hu5, const u5 **data, size_t *field_len, bool *have_m) { const char *err; - if (*have_m) - return unknown_field(b11, hu5, data, field_len, 'm'); + assert(!*have_m); b11->metadata = pull_all(b11, hu5, data, field_len, false, &err); if (!b11->metadata) @@ -599,6 +606,56 @@ struct bolt11 *new_bolt11(const tal_t *ctx, return b11; } +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 const struct decoder decoders[] = { + /* BOLT #11: + * + * A payer... SHOULD use the first `p` field that it did NOT + * skip as the payment hash. + */ + { 'p', false, decode_p }, + { 'd', false, decode_d }, + { 'h', false, decode_h }, + { 'x', false, decode_x }, + { 'c', false, decode_c }, + { 'n', false, decode_n }, + { 's', false, decode_s }, + /* BOLT #11: + * - MAY include one or more `f` fields. + */ + { 'f', true, decode_f }, + /* BOLT #11: + * + * there may be more than one `r` field + */ + { 'r', true, decode_r }, + { '9', false, decode_9 }, + { 'm', false, decode_m }, +}; + +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; + } + return NULL; +} + static bool bech32_decode_alloc(const tal_t *ctx, const char **hrp_ret, const u5 **data_ret, @@ -638,10 +695,10 @@ struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, struct bolt11 *b11 = new_bolt11(ctx, NULL); struct hash_u5 hu5; const char *err; - bool have_p = false, have_d = false, have_h = false, - have_x = false, have_c = false, have_s = false, have_m = false; + /* We don't need all of these, but in theory we could have 32 types */ + bool have_field[32]; - *have_n = false; + memset(have_field, 0, sizeof(have_field)); b11->routes = tal_arr(b11, struct route_info *, 0); /* BOLT #11: @@ -770,6 +827,7 @@ struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, while (data_len > 520 / 5) { const char *problem = NULL; u64 type, field_len; + const struct decoder *decoder; /* BOLT #11: * @@ -795,60 +853,13 @@ struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, /* Do this now: the decode function fixes up the data ptr */ data_len -= field_len; - /* FIXME: We should make decoding table driven, since they - * follow common patterns! */ - switch (bech32_charset[type]) { - case 'p': - problem = decode_p(b11, &hu5, &data, &field_len, - &have_p); - break; - - case 'd': - problem = decode_d(b11, &hu5, &data, &field_len, - &have_d); - break; - - case 'h': - problem = decode_h(b11, &hu5, &data, &field_len, - &have_h); - break; - - case 'n': - problem = decode_n(b11, &hu5, &data, &field_len, - have_n); - break; - - case 'x': - problem = decode_x(b11, &hu5, &data, &field_len, - &have_x); - break; - - case 'c': - problem = decode_c(b11, &hu5, &data, &field_len, - &have_c); - break; - - case 'f': - problem = decode_f(b11, &hu5, &data, &field_len); - break; - case 'r': - problem = decode_r(b11, &hu5, &data, &field_len); - break; - case '9': - problem = decode_9(b11, our_features, &hu5, - &data, &field_len); - break; - case 's': - problem = decode_s(b11, &hu5, &data, &field_len, - &have_s); - break; - case 'm': - problem = decode_m(b11, &hu5, &data, &field_len, - &have_m); - break; - default: + 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]); } if (problem) return decode_fail(b11, fail, "%s", problem); @@ -857,10 +868,10 @@ struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, bech32_charset[type], field_len); } - if (!have_p) + if (!have_field[bech32_charset_rev['p']]) return decode_fail(b11, fail, "No valid 'p' field found"); - if (have_h && description) { + if (have_field[bech32_charset_rev['h']] && description) { struct sha256 sha; /* BOLT #11: @@ -877,6 +888,8 @@ struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, 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; }