From cf43e4437893a36bb9920e12a81a08741edcc20a Mon Sep 17 00:00:00 2001 From: Rusty Russell <rusty@rustcorp.com.au> Date: Thu, 2 Apr 2020 14:34:47 +1030 Subject: [PATCH] common/features: don't use internal global. Turns out that unnecessary: all callers can access the feature_set, so make it much more like a normal primitive. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> --- channeld/channeld.c | 13 +- common/bolt11.c | 17 +-- common/bolt11.h | 7 +- common/features.c | 134 +++++++------------- common/features.h | 37 +++--- common/test/run-bolt11.c | 37 ++++-- common/test/run-features.c | 91 +++++++++---- connectd/connectd.c | 18 +-- connectd/peer_exchange_initmsg.c | 5 +- connectd/peer_exchange_initmsg.h | 1 + connectd/test/run-initiator-success.c | 3 - devtools/bolt11-cli.c | 2 +- gossipd/gossip_generation.c | 3 +- gossipd/gossipd.c | 4 +- gossipd/gossipd.h | 3 + gossipd/test/run-extended-info.c | 3 - gossipd/test/run-next_block_range.c | 3 - lightningd/invoice.c | 6 +- lightningd/lightningd.c | 36 ++++++ lightningd/lightningd.h | 2 +- lightningd/opening_control.c | 6 +- lightningd/options.c | 25 +--- lightningd/pay.c | 2 +- lightningd/plugin.c | 2 +- lightningd/test/run-find_my_abspath.c | 7 + lightningd/test/run-invoice-select-inchan.c | 4 +- openingd/openingd.c | 12 +- plugins/pay.c | 3 +- 28 files changed, 253 insertions(+), 233 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index a6643e9c2706..dc04e03f9956 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -76,6 +76,9 @@ struct peer { /* Features peer supports. */ u8 *features; + /* Features we support. */ + struct feature_set *fset; + /* Tolerable amounts for feerate (only relevant for fundee). */ u32 feerate_min, feerate_max; @@ -415,7 +418,7 @@ static void send_announcement_signatures(struct peer *peer) static u8 *create_channel_announcement(const tal_t *ctx, struct peer *peer) { int first, second; - u8 *cannounce, *features = get_agreed_channelfeatures(tmpctx, peer->features); + u8 *cannounce, *features = get_agreed_channelfeatures(tmpctx, peer->fset, peer->features); if (peer->channel_direction == 0) { first = LOCAL; @@ -2325,7 +2328,7 @@ static void peer_reconnect(struct peer *peer, bool dataloss_protect, check_extra_fields; const u8 **premature_msgs = tal_arr(peer, const u8 *, 0); - dataloss_protect = feature_negotiated(peer->features, + dataloss_protect = feature_negotiated(peer->fset, peer->features, OPT_DATA_LOSS_PROTECT); /* Both these options give us extra fields to check. */ @@ -3045,7 +3048,6 @@ static void init_channel(struct peer *peer) secp256k1_ecdsa_signature *remote_ann_node_sig; secp256k1_ecdsa_signature *remote_ann_bitcoin_sig; bool option_static_remotekey; - struct feature_set *feature_set; #if !DEVELOPER bool dev_fail_process_onionpacket; /* Ignored */ #endif @@ -3057,7 +3059,7 @@ static void init_channel(struct peer *peer) msg = wire_sync_read(tmpctx, MASTER_FD); if (!fromwire_channel_init(peer, msg, &chainparams, - &feature_set, + &peer->fset, &funding_txid, &funding_txout, &funding, &minimum_depth, @@ -3113,9 +3115,6 @@ static void init_channel(struct peer *peer) master_badmsg(WIRE_CHANNEL_INIT, msg); } - /* Now we know what features to advertize. */ - features_init(take(feature_set)); - /* stdin == requests, 3 == peer, 4 = gossip, 5 = gossip_store, 6 = HSM */ per_peer_state_set_fds(peer->pps, 3, 4, 5); diff --git a/common/bolt11.c b/common/bolt11.c index c1020c6cf513..08173ef72bd2 100644 --- a/common/bolt11.c +++ b/common/bolt11.c @@ -489,6 +489,7 @@ static void shift_bitmap_down(u8 *bitmap, size_t bits) * See [Feature Bits](#feature-bits). */ static char *decode_9(struct bolt11 *b11, + const struct feature_set *fset, struct hash_u5 *hu5, u5 **data, size_t *data_len, size_t data_length) @@ -511,13 +512,12 @@ static char *decode_9(struct bolt11 *b11, * - if the `9` field contains unknown _even_ bits that are non-zero: * - MUST fail the payment. */ - /* BOLT #11: - * The field is big-endian. The least-significant bit is numbered 0, - * which is _even_, and the next most significant bit is numbered 1, - * which is _odd_. */ - badf = features_unsupported(b11->features); - if (badf != -1) - return tal_fmt(b11, "9: unknown feature bit %i", badf); + /* We skip this check for the cli tool, which sets fset to NULL */ + if (fset) { + badf = features_unsupported(fset, b11->features, BOLT11_FEATURE); + if (badf != -1) + return tal_fmt(b11, "9: unknown feature bit %i", badf); + } return NULL; } @@ -545,6 +545,7 @@ struct bolt11 *new_bolt11(const tal_t *ctx, /* Decodes and checks signature; returns NULL on error. */ struct bolt11 *bolt11_decode(const tal_t *ctx, const char *str, + const struct feature_set *fset, const char *description, char **fail) { char *hrp, *amountstr, *prefix; @@ -739,7 +740,7 @@ struct bolt11 *bolt11_decode(const tal_t *ctx, const char *str, data_length); break; case '9': - problem = decode_9(b11, &hu5, &data, &data_len, + problem = decode_9(b11, fset, &hu5, &data, &data_len, data_length); break; case 's': diff --git a/common/bolt11.h b/common/bolt11.h index 2b337ff28c83..dae6da562039 100644 --- a/common/bolt11.h +++ b/common/bolt11.h @@ -14,6 +14,8 @@ /* We only have 10 bits for the field length, meaning < 640 bytes */ #define BOLT11_FIELD_BYTE_LIMIT ((1 << 10) * 5 / 8) +struct feature_set; + struct bolt11_field { struct list_node list; @@ -74,8 +76,11 @@ struct bolt11 { }; /* Decodes and checks signature; returns NULL on error; description is - * (optional) out-of-band description of payment, for `h` field. */ + * (optional) out-of-band description of payment, for `h` field. + * fset is NULL to accept any features (usually not desirable!). + */ struct bolt11 *bolt11_decode(const tal_t *ctx, const char *str, + const struct feature_set *fset, const char *description, char **fail); /* Initialize an empty bolt11 struct with optional amount */ diff --git a/common/features.c b/common/features.c index 3f0a792b88f2..8a24fb6d4277 100644 --- a/common/features.c +++ b/common/features.c @@ -5,10 +5,6 @@ #include <common/utils.h> #include <wire/peer_wire.h> -/* We keep a map of our features for each context, with the assumption that - * the init features is a superset of the others. */ -static struct feature_set *our_features; - enum feature_copy_style { /* Feature is not exposed (importantly, being 0, this is the default!). */ FEATURE_DONT_REPRESENT, @@ -67,84 +63,53 @@ static enum feature_copy_style feature_copy_style(u32 f, enum feature_place p) abort(); } -static u8 *mkfeatures(const tal_t *ctx, enum feature_place place) +struct feature_set *feature_set_for_feature(const tal_t *ctx, int feature) { - u8 *f = tal_arr(ctx, u8, 0); - const u8 *base = our_features->bits[INIT_FEATURE]; - - assert(place != INIT_FEATURE); - for (size_t i = 0; i < tal_bytelen(base)*8; i++) { - if (!feature_is_set(base, i)) - continue; + struct feature_set *fs = tal(ctx, struct feature_set); - switch (feature_copy_style(i, place)) { + for (size_t i = 0; i < ARRAY_SIZE(fs->bits); i++) { + fs->bits[i] = tal_arr(fs, u8, 0); + switch (feature_copy_style(feature, i)) { case FEATURE_DONT_REPRESENT: continue; case FEATURE_REPRESENT: - set_feature_bit(&f, i); + set_feature_bit(&fs->bits[i], feature); continue; case FEATURE_REPRESENT_AS_OPTIONAL: - set_feature_bit(&f, OPTIONAL_FEATURE(i)); + set_feature_bit(&fs->bits[i], OPTIONAL_FEATURE(feature)); continue; } abort(); } - return f; -} - -struct feature_set *features_core_init(const u8 *feature_bits) -{ - assert(!our_features); - our_features = notleak(tal(NULL, struct feature_set)); - - our_features->bits[INIT_FEATURE] - = tal_dup_talarr(our_features, u8, feature_bits); - - /* Make other masks too */ - for (enum feature_place f = INIT_FEATURE+1; f < NUM_FEATURE_PLACE; f++) - our_features->bits[f] = mkfeatures(our_features, f); - - return our_features; -} - -void features_init(struct feature_set *fset TAKES) -{ - assert(!our_features); - - if (taken(fset)) - our_features = notleak(tal_steal(NULL, fset)); - else { - our_features = notleak(tal(NULL, struct feature_set)); - for (size_t i = 0; i < ARRAY_SIZE(fset->bits); i++) - our_features->bits[i] = tal_dup_talarr(our_features, u8, - fset->bits[i]); - } -} - -void features_cleanup(void) -{ - our_features = tal_free(our_features); + return fs; } -bool features_additional(const struct feature_set *newfset) +bool feature_set_or(struct feature_set *a, + const struct feature_set *b TAKES) { /* Check first, before we change anything! */ - for (size_t i = 0; i < ARRAY_SIZE(newfset->bits); i++) { + for (size_t i = 0; i < ARRAY_SIZE(b->bits); i++) { /* FIXME: We could allow a plugin to upgrade an optional feature * to a compulsory one? */ - for (size_t b = 0; b < tal_bytelen(newfset->bits[i])*8; b++) { - if (feature_is_set(newfset->bits[i], b) - && feature_is_set(our_features->bits[i], b)) + for (size_t j = 0; j < tal_bytelen(b->bits[i])*8; j++) { + if (feature_is_set(b->bits[i], j) + && feature_offered(a->bits[i], j)) { + if (taken(b)) + tal_free(b); return false; + } } } - for (size_t i = 0; i < ARRAY_SIZE(newfset->bits); i++) { - for (size_t b = 0; b < tal_bytelen(newfset->bits[i])*8; b++) { - if (feature_is_set(newfset->bits[i], b)) - set_feature_bit(&our_features->bits[i], b); + for (size_t i = 0; i < ARRAY_SIZE(a->bits); i++) { + for (size_t j = 0; j < tal_bytelen(b->bits[i])*8; j++) { + if (feature_is_set(b->bits[i], j)) + set_feature_bit(&a->bits[i], j); } } + + if (taken(b)) + tal_free(b); return true; } @@ -172,21 +137,6 @@ static bool test_bit(const u8 *features, size_t byte, unsigned int bit) return features[tal_count(features) - 1 - byte] & (1 << (bit % 8)); } -u8 *get_offered_nodefeatures(const tal_t *ctx) -{ - return tal_dup_talarr(ctx, u8, our_features->bits[NODE_ANNOUNCE_FEATURE]); -} - -u8 *get_offered_initfeatures(const tal_t *ctx) -{ - return tal_dup_talarr(ctx, u8, our_features->bits[INIT_FEATURE]); -} - -u8 *get_offered_globalinitfeatures(const tal_t *ctx) -{ - return tal_dup_talarr(ctx, u8, our_features->bits[GLOBAL_INIT_FEATURE]); -} - static void clear_feature_bit(u8 *features, u32 bit) { size_t bytenum = bit / 8, bitnum = bit % 8, len = tal_count(features); @@ -203,9 +153,11 @@ static void clear_feature_bit(u8 *features, u32 bit) * - MUST set `len` to the minimum length required to hold the `features` bits * it sets. */ -u8 *get_agreed_channelfeatures(const tal_t *ctx, const u8 *theirfeatures) +u8 *get_agreed_channelfeatures(const tal_t *ctx, + const struct feature_set *ours, + const u8 *theirfeatures) { - u8 *f = tal_dup_talarr(ctx, u8, our_features->bits[CHANNEL_FEATURE]); + u8 *f = tal_dup_talarr(ctx, u8, ours->bits[CHANNEL_FEATURE]); size_t max_len = 0; /* Clear any features which they didn't offer too */ @@ -225,11 +177,6 @@ u8 *get_agreed_channelfeatures(const tal_t *ctx, const u8 *theirfeatures) return f; } -u8 *get_offered_bolt11features(const tal_t *ctx) -{ - return tal_dup_talarr(ctx, u8, our_features->bits[BOLT11_FEATURE]); -} - bool feature_is_set(const u8 *features, size_t bit) { size_t bytenum = bit / 8; @@ -246,10 +193,11 @@ bool feature_offered(const u8 *features, size_t f) || feature_is_set(features, OPTIONAL_FEATURE(f)); } -bool feature_negotiated(const u8 *lfeatures, size_t f) +bool feature_negotiated(const struct feature_set *ours, + const u8 *lfeatures, size_t f) { return feature_offered(lfeatures, f) - && feature_offered(our_features->bits[INIT_FEATURE], f); + && feature_offered(ours->bits[INIT_FEATURE], f); } /** @@ -263,7 +211,9 @@ bool feature_negotiated(const u8 *lfeatures, size_t f) * * Returns -1 on success, or first unsupported feature. */ -static int all_supported_features(const u8 *bitmap) +static int all_supported_features(const struct feature_set *ours, + const u8 *bitmap, + enum feature_place p) { size_t len = tal_count(bitmap) * 8; @@ -272,7 +222,7 @@ static int all_supported_features(const u8 *bitmap) if (!test_bit(bitmap, bitnum/8, bitnum%8)) continue; - if (feature_offered(our_features->bits[INIT_FEATURE], bitnum)) + if (feature_offered(ours->bits[p], bitnum)) continue; return bitnum; @@ -280,15 +230,16 @@ static int all_supported_features(const u8 *bitmap) return -1; } -int features_unsupported(const u8 *features) +int features_unsupported(const struct feature_set *ours, const u8 *theirs, + enum feature_place p) { /* BIT 2 would logically be "compulsory initial_routing_sync", but * that does not exist, so we special case it. */ - if (feature_is_set(features, + if (feature_is_set(theirs, COMPULSORY_FEATURE(OPT_INITIAL_ROUTING_SYNC))) return COMPULSORY_FEATURE(OPT_INITIAL_ROUTING_SYNC); - return all_supported_features(features); + return all_supported_features(ours, theirs, p); } static const char *feature_name(const tal_t *ctx, size_t f) @@ -313,12 +264,13 @@ static const char *feature_name(const tal_t *ctx, size_t f) fnames[f / 2], (f & 1) ? "odd" : "even"); } -const char **list_supported_features(const tal_t *ctx) +const char **list_supported_features(const tal_t *ctx, + const struct feature_set *ours) { const char **list = tal_arr(ctx, const char *, 0); - for (size_t i = 0; i < tal_bytelen(our_features->bits[INIT_FEATURE]) * 8; i++) { - if (test_bit(our_features->bits[INIT_FEATURE], i / 8, i % 8)) + for (size_t i = 0; i < tal_bytelen(ours->bits[INIT_FEATURE]) * 8; i++) { + if (test_bit(ours->bits[INIT_FEATURE], i / 8, i % 8)) tal_arr_expand(&list, feature_name(list, i)); } diff --git a/common/features.h b/common/features.h index ba2c5165f725..cfdcdfb2c9cc 100644 --- a/common/features.h +++ b/common/features.h @@ -18,43 +18,38 @@ struct feature_set { u8 *bits[NUM_FEATURE_PLACE]; }; -/* Initialize core features (for lightningd). */ -struct feature_set *features_core_init(const u8 *features TAKES); - -/* Initialize subdaemon features. */ -void features_init(struct feature_set *fset TAKES); - -/* Free feature allocations */ -void features_cleanup(void); +/* Create feature set for a known feature. */ +struct feature_set *feature_set_for_feature(const tal_t *ctx, int feature); +/* Marshalling a feature set */ struct feature_set *fromwire_feature_set(const tal_t *ctx, const u8 **ptr, size_t *max); void towire_feature_set(u8 **pptr, const struct feature_set *fset); -/* Add features supplied by a plugin: returns false if we already have them */ -bool features_additional(const struct feature_set *feature_set); +/* a |= b, or returns false if features already in a */ +bool feature_set_or(struct feature_set *a, + const struct feature_set *b TAKES); /* Returns -1 if we're OK with all these offered features, otherwise first * unsupported (even) feature. */ -int features_unsupported(const u8 *features); - -/* For sending our features: tal_count() returns length. */ -u8 *get_offered_initfeatures(const tal_t *ctx); -u8 *get_offered_globalinitfeatures(const tal_t *ctx); -u8 *get_offered_nodefeatures(const tal_t *ctx); -u8 *get_offered_bolt11features(const tal_t *ctx); +int features_unsupported(const struct feature_set *ours, const u8 *theirs, + enum feature_place p); /* For the features in channel_announcement */ -u8 *get_agreed_channelfeatures(const tal_t *ctx, const u8 *theirfeatures); +u8 *get_agreed_channelfeatures(const tal_t *ctx, + const struct feature_set *ours, + const u8 *theirfeatures); /* Is this feature bit requested? (Either compulsory or optional) */ bool feature_offered(const u8 *features, size_t f); /* Was this feature bit offered by them and us? */ -bool feature_negotiated(const u8 *lfeatures, size_t f); +bool feature_negotiated(const struct feature_set *ours, + const u8 *features, size_t f); -/* Return a list of what features we advertize. */ -const char **list_supported_features(const tal_t *ctx); +/* Return a list of what (init) features we advertize. */ +const char **list_supported_features(const tal_t *ctx, + const struct feature_set *ours); /* Low-level helpers to deal with big-endian bitfields. */ bool feature_is_set(const u8 *features, size_t bit); diff --git a/common/test/run-bolt11.c b/common/test/run-bolt11.c index 69fd502b5e84..7ce3b17dff18 100644 --- a/common/test/run-bolt11.c +++ b/common/test/run-bolt11.c @@ -64,7 +64,7 @@ static void test_b11(const char *b11str, char *reproduce; struct bolt11_field *b11_extra, *expect_extra; - b11 = bolt11_decode(tmpctx, b11str, hashed_desc, &fail); + b11 = bolt11_decode(tmpctx, b11str, NULL, hashed_desc, &fail); if (!b11) errx(1, "%s:%u:%s", __FILE__, __LINE__, fail); @@ -145,11 +145,11 @@ int main(void) const char *badstr; struct bolt11_field *extra; char *fail; + struct feature_set *fset; wally_init(0); secp256k1_ctx = wally_get_secp_context(); setup_tmpctx(); - features_core_init(NULL); /* BOLT #11: * @@ -268,7 +268,7 @@ int main(void) for (size_t i = 0; i <= strlen(badstr); i++) { if (bolt11_decode(tmpctx, tal_strndup(tmpctx, badstr, i), - NULL, &fail)) + NULL, NULL, &fail)) abort(); assert(strstr(fail, "Bad bech32") || strstr(fail, "Invoices must start with ln")); @@ -461,9 +461,23 @@ int main(void) set_feature_bit(&b11->features, 100); badstr = bolt11_encode(tmpctx, b11, false, test_sign, NULL); assert(streq(badstr, "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q4psqqqqqqqqqqqqqqqpqsqq40wa3khl49yue3zsgm26jrepqr2eghqlx86rttutve3ugd05em86nsefzh4pfurpd9ek9w2vp95zxqnfe2u7ckudyahsa52q66tgzcp6t2dyk")); - assert(!bolt11_decode(tmpctx, badstr, NULL, &fail)); + /* Empty set of allowed bits, ensures this fails! */ + fset = tal(tmpctx, struct feature_set); + fset->bits[BOLT11_FEATURE] = tal_arr(fset, u8, 0); + assert(!bolt11_decode(tmpctx, badstr, fset, NULL, &fail)); assert(streq(fail, "9: unknown feature bit 100")); + /* We'd actually allow this if we either (1) don't check, or (2) accept that feature in + * either compulsory or optional forms. */ + assert(bolt11_decode(tmpctx, badstr, NULL, NULL, &fail)); + + set_feature_bit(&fset->bits[BOLT11_FEATURE], 100); + assert(bolt11_decode(tmpctx, badstr, fset, NULL,&fail)); + + clear_feature_bit(fset->bits[BOLT11_FEATURE], 100); + set_feature_bit(&fset->bits[BOLT11_FEATURE], 101); + assert(bolt11_decode(tmpctx, badstr, fset, NULL, &fail)); + /* BOLT-1fbccd30bb503203e4a255de67f9adb504563425 #11: * * > ### Please send 0.00967878534 BTC for a list of items within one week, amount in pico-BTC @@ -527,53 +541,52 @@ int main(void) * > ### Bech32 checksum is invalid. * > lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrnt */ - assert(!bolt11_decode(tmpctx, "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrnt", NULL, &fail)); + assert(!bolt11_decode(tmpctx, "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrnt", NULL, NULL, &fail)); assert(streq(fail, "Bad bech32 string")); /* BOLT-4e228a7fb4ea78af914d1ce82a63cbce8026279e #11: * > ### Malformed bech32 string (no 1) * > pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny */ - assert(!bolt11_decode(tmpctx, "pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny", NULL, &fail)); + assert(!bolt11_decode(tmpctx, "pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny", NULL, NULL, &fail)); assert(streq(fail, "Bad bech32 string")); /* BOLT-4e228a7fb4ea78af914d1ce82a63cbce8026279e #11: * > ### Malformed bech32 string (mixed case) * > LNBC2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny */ - assert(!bolt11_decode(tmpctx, "LNBC2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny", NULL, &fail)); + assert(!bolt11_decode(tmpctx, "LNBC2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny", NULL, NULL, &fail)); assert(streq(fail, "Bad bech32 string")); /* BOLT-4e228a7fb4ea78af914d1ce82a63cbce8026279e #11: * > ### Signature is not recoverable. * > lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaxtrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspk28uwq */ - assert(!bolt11_decode(tmpctx, "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaxtrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspk28uwq", NULL, &fail)); + assert(!bolt11_decode(tmpctx, "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaxtrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspk28uwq", NULL, NULL, &fail)); assert(streq(fail, "signature recovery failed")); /* BOLT-4e228a7fb4ea78af914d1ce82a63cbce8026279e #11: * > ### String is too short. * > lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6na6hlh */ - assert(!bolt11_decode(tmpctx, "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6na6hlh", NULL, &fail)); + assert(!bolt11_decode(tmpctx, "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6na6hlh", NULL, NULL, &fail)); /* BOLT-4e228a7fb4ea78af914d1ce82a63cbce8026279e #11: * > ### Invalid multiplier * > lnbc2500x1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpujr6jxr9gq9pv6g46y7d20jfkegkg4gljz2ea2a3m9lmvvr95tq2s0kvu70u3axgelz3kyvtp2ywwt0y8hkx2869zq5dll9nelr83zzqqpgl2zg */ - assert(!bolt11_decode(tmpctx, "lnbc2500x1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpujr6jxr9gq9pv6g46y7d20jfkegkg4gljz2ea2a3m9lmvvr95tq2s0kvu70u3axgelz3kyvtp2ywwt0y8hkx2869zq5dll9nelr83zzqqpgl2zg", NULL, &fail)); + assert(!bolt11_decode(tmpctx, "lnbc2500x1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpujr6jxr9gq9pv6g46y7d20jfkegkg4gljz2ea2a3m9lmvvr95tq2s0kvu70u3axgelz3kyvtp2ywwt0y8hkx2869zq5dll9nelr83zzqqpgl2zg", NULL, NULL, &fail)); assert(streq(fail, "Invalid amount postfix 'x'")); /* BOLT- #11: * > ### Invalid sub-millisatoshi precision. * > lnbc2500000001p1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu7hqtk93pkf7sw55rdv4k9z2vj050rxdr6za9ekfs3nlt5lr89jqpdmxsmlj9urqumg0h9wzpqecw7th56tdms40p2ny9q4ddvjsedzcplva53s */ - assert(!bolt11_decode(tmpctx, "lnbc2500000001p1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu7hqtk93pkf7sw55rdv4k9z2vj050rxdr6za9ekfs3nlt5lr89jqpdmxsmlj9urqumg0h9wzpqecw7th56tdms40p2ny9q4ddvjsedzcplva53s", NULL, &fail)); + assert(!bolt11_decode(tmpctx, "lnbc2500000001p1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu7hqtk93pkf7sw55rdv4k9z2vj050rxdr6za9ekfs3nlt5lr89jqpdmxsmlj9urqumg0h9wzpqecw7th56tdms40p2ny9q4ddvjsedzcplva53s", NULL, NULL, &fail)); assert(streq(fail, "Invalid sub-millisatoshi amount '2500000001p'")); /* FIXME: Test the others! */ wally_cleanup(0); tal_free(tmpctx); - features_cleanup(); return 0; } diff --git a/common/test/run-features.c b/common/test/run-features.c index f75f96804dc3..341b2f959f64 100644 --- a/common/test/run-features.c +++ b/common/test/run-features.c @@ -65,32 +65,70 @@ static void test_featurebits_or(void) memeq(result, tal_bytelen(result), control, tal_bytelen(control))); } -static void setup_features(void) +static bool feature_set_eq(const struct feature_set *f1, + const struct feature_set *f2) { - static const u32 default_features[] = { - OPTIONAL_FEATURE(OPT_DATA_LOSS_PROTECT), - OPTIONAL_FEATURE(OPT_UPFRONT_SHUTDOWN_SCRIPT), - OPTIONAL_FEATURE(OPT_GOSSIP_QUERIES), - OPTIONAL_FEATURE(OPT_VAR_ONION), - OPTIONAL_FEATURE(OPT_PAYMENT_SECRET), - OPTIONAL_FEATURE(OPT_BASIC_MPP), - OPTIONAL_FEATURE(OPT_GOSSIP_QUERIES_EX), - OPTIONAL_FEATURE(OPT_STATIC_REMOTEKEY), - }; - u8 *f = tal_arr(NULL, u8, 0); - for (size_t i = 0; i < ARRAY_SIZE(default_features); i++) - set_feature_bit(&f, default_features[i]); - features_core_init(take(f)); + /* We assume minimal sizing */ + for (size_t i = 0; i < ARRAY_SIZE(f1->bits); i++) { + if (!memeq(f1->bits[i], tal_bytelen(f1->bits[i]), + f2->bits[i], tal_bytelen(f2->bits[i]))) + return false; + } + return true; +} + +static void test_feature_set_or(void) +{ + struct feature_set *f1, *f2, *control; + + for (size_t i = 0; i < ARRAY_SIZE(f1->bits); i++) { + f1 = talz(tmpctx, struct feature_set); + f2 = talz(tmpctx, struct feature_set); + control = talz(tmpctx, struct feature_set); + + f1->bits[i] = tal_arr(f1, u8, 0); + f2->bits[i] = tal_arr(f2, u8, 0); + control->bits[i] = tal_arr(control, u8, 0); + + /* or with nothing is a nop */ + set_feature_bit(&f1->bits[i], 0); + set_feature_bit(&control->bits[i], 0); + assert(feature_set_or(f1, f2)); + assert(feature_set_eq(f1, control)); + + /* or compulsory with either compulsory or optional is a fail */ + set_feature_bit(&f2->bits[i], 0); + assert(!feature_set_or(f1, f2)); + assert(feature_set_eq(f1, control)); + assert(!feature_set_or(f2, f1)); + + clear_feature_bit(f2->bits[i], 0); + set_feature_bit(&f2->bits[i], 1); + assert(!feature_set_or(f1, f2)); + assert(feature_set_eq(f1, control)); + assert(!feature_set_or(f2, f1)); + + clear_feature_bit(f2->bits[i], 1); + set_feature_bit(&f2->bits[i], 10); + set_feature_bit(&control->bits[i], 10); + assert(feature_set_or(f1, f2)); + assert(feature_set_eq(f1, control)); + } } int main(void) { - u8 *bits, *lf; + u8 *bits; + struct feature_set *fset; + setup_locale(); wally_init(0); secp256k1_ctx = wally_get_secp_context(); setup_tmpctx(); - setup_features(); + + /* Just some bits to set. */ + fset = feature_set_for_feature(tmpctx, + OPTIONAL_FEATURE(OPT_DATA_LOSS_PROTECT)); bits = tal_arr(tmpctx, u8, 0); for (size_t i = 0; i < 100; i += 3) @@ -136,38 +174,37 @@ int main(void) /* We always support no features. */ memset(bits, 0, tal_count(bits)); - assert(features_unsupported(bits) == -1); + assert(features_unsupported(fset, bits, INIT_FEATURE) == -1); /* We must support our own features. */ - lf = get_offered_initfeatures(tmpctx); - assert(features_unsupported(lf) == -1); + assert(features_unsupported(fset, fset->bits[INIT_FEATURE], INIT_FEATURE) == -1); /* We can add random odd features, no problem. */ for (size_t i = 1; i < 16; i += 2) { - bits = tal_dup_talarr(tmpctx, u8, lf); + bits = tal_dup_talarr(tmpctx, u8, fset->bits[INIT_FEATURE]); set_feature_bit(&bits, i); - assert(features_unsupported(bits) == -1); + assert(features_unsupported(fset, bits, INIT_FEATURE) == -1); } /* We can't add random even features. */ for (size_t i = 0; i < 16; i += 2) { - bits = tal_dup_talarr(tmpctx, u8, lf); + bits = tal_dup_talarr(tmpctx, u8, fset->bits[INIT_FEATURE]); set_feature_bit(&bits, i); /* Special case for missing compulsory feature */ if (i == 2) { - assert(features_unsupported(bits) == i); + assert(features_unsupported(fset, bits, INIT_FEATURE) == i); } else { - assert((features_unsupported(bits) == -1) - == feature_offered(our_features->bits[INIT_FEATURE], i)); + assert((features_unsupported(fset, bits, INIT_FEATURE) == -1) + == feature_offered(fset->bits[INIT_FEATURE], i)); } } test_featurebits_or(); + test_feature_set_or(); wally_cleanup(0); tal_free(tmpctx); take_cleanup(); - features_cleanup(); return 0; } diff --git a/connectd/connectd.c b/connectd/connectd.c index aec7bcb236f6..c6bb4eca6dac 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -154,6 +154,9 @@ struct daemon { /* Allow to define the default behavior of tor services calls*/ bool use_v3_autotor; + + /* Our features, as lightningd told us */ + struct feature_set *fset; }; /* Peers we're trying to reach: we iterate through addrs until we succeed @@ -300,7 +303,7 @@ static bool get_gossipfds(struct daemon *daemon, /*~ The way features generally work is that both sides need to offer it; * we always offer `gossip_queries`, but this check is explicit. */ gossip_queries_feature - = feature_negotiated(features, OPT_GOSSIP_QUERIES); + = feature_negotiated(daemon->fset, features, OPT_GOSSIP_QUERIES); /*~ `initial_routing_sync` is supported by every node, since it was in * the initial lightning specification: it means the peer wants the @@ -436,7 +439,7 @@ struct io_plan *peer_connected(struct io_conn *conn, * - upon receiving unknown _even_ feature bits that are non-zero: * - MUST fail the connection. */ - unsup = features_unsupported(features); + unsup = features_unsupported(daemon->fset, features, INIT_FEATURE); if (unsup != -1) { msg = towire_errorfmt(NULL, NULL, "Unsupported feature %u", unsup); @@ -490,7 +493,7 @@ static struct io_plan *handshake_in_success(struct io_conn *conn, struct node_id id; node_id_from_pubkey(&id, id_key); status_peer_debug(&id, "Connect IN"); - return peer_exchange_initmsg(conn, daemon, cs, &id, addr); + return peer_exchange_initmsg(conn, daemon, daemon->fset, cs, &id, addr); } /*~ When we get a connection in we set up its network address then call @@ -550,7 +553,8 @@ static struct io_plan *handshake_out_success(struct io_conn *conn, node_id_from_pubkey(&id, key); connect->connstate = "Exchanging init messages"; status_peer_debug(&id, "Connect OUT"); - return peer_exchange_initmsg(conn, connect->daemon, cs, &id, addr); + return peer_exchange_initmsg(conn, connect->daemon, + connect->daemon->fset, cs, &id, addr); } struct io_plan *connection_out(struct io_conn *conn, struct connecting *connect) @@ -1201,14 +1205,13 @@ static struct io_plan *connect_init(struct io_conn *conn, struct wireaddr_internal *proposed_wireaddr; enum addr_listen_announce *proposed_listen_announce; struct wireaddr *announcable; - struct feature_set *feature_set; char *tor_password; /* Fields which require allocation are allocated off daemon */ if (!fromwire_connectctl_init( daemon, msg, &chainparams, - &feature_set, + &daemon->fset, &daemon->id, &proposed_wireaddr, &proposed_listen_announce, @@ -1221,9 +1224,6 @@ static struct io_plan *connect_init(struct io_conn *conn, master_badmsg(WIRE_CONNECTCTL_INIT, msg); } - /* Now we know what features to advertize. */ - features_init(take(feature_set)); - if (!pubkey_from_node_id(&daemon->mykey, &daemon->id)) status_failed(STATUS_FAIL_INTERNAL_ERROR, "Invalid id for me %s", diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index a9c425873077..a8c96f19d451 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -135,6 +135,7 @@ static struct io_plan *peer_write_postclose(struct io_conn *conn, struct io_plan *peer_exchange_initmsg(struct io_conn *conn, struct daemon *daemon, + const struct feature_set *fset, const struct crypto_state *cs, const struct node_id *id, const struct wireaddr_internal *addr) @@ -181,8 +182,8 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn, * Finally, we agreed that bits below 13 could be put in both, but * from now on they'll all go in initfeatures. */ peer->msg = towire_init(NULL, - get_offered_globalinitfeatures(tmpctx), - get_offered_initfeatures(tmpctx), + fset->bits[GLOBAL_INIT_FEATURE], + fset->bits[INIT_FEATURE], tlvs); status_peer_io(LOG_IO_OUT, &peer->id, peer->msg); peer->msg = cryptomsg_encrypt_msg(peer, &peer->cs, take(peer->msg)); diff --git a/connectd/peer_exchange_initmsg.h b/connectd/peer_exchange_initmsg.h index 29edbf03eb40..ee7c5ae09906 100644 --- a/connectd/peer_exchange_initmsg.h +++ b/connectd/peer_exchange_initmsg.h @@ -12,6 +12,7 @@ struct wireaddr_internal; /* If successful, calls peer_connected() */ struct io_plan *peer_exchange_initmsg(struct io_conn *conn, struct daemon *daemon, + const struct feature_set *fset, const struct crypto_state *cs, const struct node_id *id, const struct wireaddr_internal *addr); diff --git a/connectd/test/run-initiator-success.c b/connectd/test/run-initiator-success.c index a7fa6fa0af57..07c081b80509 100644 --- a/connectd/test/run-initiator-success.c +++ b/connectd/test/run-initiator-success.c @@ -40,9 +40,6 @@ u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, /* Generated stub for fromwire_u16 */ u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u16 called!\n"); abort(); } -/* Generated stub for notleak_ */ -void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) -{ fprintf(stderr, "notleak_ called!\n"); abort(); } /* Generated stub for towire_u16 */ void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) { fprintf(stderr, "towire_u16 called!\n"); abort(); } diff --git a/devtools/bolt11-cli.c b/devtools/bolt11-cli.c index 0d64dc0c1e42..ce72a8d69756 100644 --- a/devtools/bolt11-cli.c +++ b/devtools/bolt11-cli.c @@ -93,7 +93,7 @@ int main(int argc, char *argv[]) errx(ERROR_USAGE, "Need argument\n%s", opt_usage(argv[0], NULL)); - b11 = bolt11_decode(ctx, argv[2], description, &fail); + b11 = bolt11_decode(ctx, argv[2], NULL, description, &fail); if (!b11) errx(ERROR_BAD_DECODE, "%s", fail); diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index 2734541b3532..3fab950e2e8a 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -38,7 +38,8 @@ static u8 *create_node_announcement(const tal_t *ctx, struct daemon *daemon, towire_wireaddr(&addresses, &daemon->announcable[i]); announcement = - towire_node_announcement(ctx, sig, get_offered_nodefeatures(tmpctx), + towire_node_announcement(ctx, sig, + daemon->fset->bits[NODE_ANNOUNCE_FEATURE], timestamp, &daemon->id, daemon->rgb, daemon->alias, addresses); diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 73d55187de21..46a10f584337 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -831,11 +831,10 @@ static struct io_plan *gossip_init(struct io_conn *conn, u32 *dev_gossip_time; bool dev_fast_gossip, dev_fast_gossip_prune; u32 timestamp; - struct feature_set *feature_set; if (!fromwire_gossipctl_init(daemon, msg, &chainparams, - &feature_set, + &daemon->fset, &daemon->id, daemon->rgb, daemon->alias, @@ -846,7 +845,6 @@ static struct io_plan *gossip_init(struct io_conn *conn, master_badmsg(WIRE_GOSSIPCTL_INIT, msg); } - features_init(take(feature_set)); daemon->rstate = new_routing_state(daemon, &daemon->id, &daemon->peers, diff --git a/gossipd/gossipd.h b/gossipd/gossipd.h index ea262eae7a79..14ae9f736302 100644 --- a/gossipd/gossipd.h +++ b/gossipd/gossipd.h @@ -57,6 +57,9 @@ struct daemon { /* What, if any, gossip we're seeker from peers. */ struct seeker *seeker; + + /* Features lightningd told us to set. */ + struct feature_set *fset; }; /* This represents each peer we're gossiping with */ diff --git a/gossipd/test/run-extended-info.c b/gossipd/test/run-extended-info.c index 6b42ed098e63..9d9e0cd41fee 100644 --- a/gossipd/test/run-extended-info.c +++ b/gossipd/test/run-extended-info.c @@ -78,9 +78,6 @@ void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UN /* Generated stub for master_badmsg */ void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) { fprintf(stderr, "master_badmsg called!\n"); abort(); } -/* Generated stub for notleak_ */ -void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) -{ fprintf(stderr, "notleak_ called!\n"); abort(); } /* Generated stub for peer_supplied_good_gossip */ void peer_supplied_good_gossip(struct peer *peer UNNEEDED, size_t amount UNNEEDED) { fprintf(stderr, "peer_supplied_good_gossip called!\n"); abort(); } diff --git a/gossipd/test/run-next_block_range.c b/gossipd/test/run-next_block_range.c index b8bec9120e2d..0395c292cdfb 100644 --- a/gossipd/test/run-next_block_range.c +++ b/gossipd/test/run-next_block_range.c @@ -38,9 +38,6 @@ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, struct timerel expire UNNEEDED, void (*cb)(void *) UNNEEDED, void *arg UNNEEDED) { fprintf(stderr, "new_reltimer_ called!\n"); abort(); } -/* Generated stub for notleak_ */ -void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) -{ fprintf(stderr, "notleak_ called!\n"); abort(); } /* Generated stub for query_channel_range */ bool query_channel_range(struct daemon *daemon UNNEEDED, struct peer *peer UNNEEDED, diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 69db2ffd97ea..2edeb92d1df3 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -1013,7 +1013,9 @@ static struct command_result *json_invoice(struct command *cmd, info->b11->description_hash = NULL; info->b11->payment_secret = tal_dup(info->b11, struct secret, &payment_secret); - info->b11->features = get_offered_bolt11features(info->b11); + info->b11->features = tal_dup_talarr(info->b11, u8, + cmd->ld->feature_set + ->bits[BOLT11_FEATURE]); #if DEVELOPER info->b11->routes = unpack_routes(info->b11, buffer, routes); @@ -1319,7 +1321,7 @@ static struct command_result *json_decodepay(struct command *cmd, NULL)) return command_param_failed(); - b11 = bolt11_decode(cmd, str, desc, &fail); + b11 = bolt11_decode(cmd, str, cmd->ld->feature_set, desc, &fail); if (!b11) { return command_fail(cmd, LIGHTNINGD, "Invalid bolt11: %s", fail); diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 051cf630523e..649fd5099ea9 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -703,6 +703,39 @@ static void setup_sig_handlers(void) } } +/*~ We actually keep more than one set of features, used in different + * contexts. common/features.c knows how each standard feature is + * presented, so we have it generate the set for each one at a time, and + * combine them. + * + * This is inefficient, but the primitives are useful for adding single + * features later, or adding them when supplied by plugins. */ +static struct feature_set *default_features(const tal_t *ctx) +{ + struct feature_set *ret = NULL; + static const u32 features[] = { + OPTIONAL_FEATURE(OPT_DATA_LOSS_PROTECT), + OPTIONAL_FEATURE(OPT_UPFRONT_SHUTDOWN_SCRIPT), + OPTIONAL_FEATURE(OPT_GOSSIP_QUERIES), + OPTIONAL_FEATURE(OPT_VAR_ONION), + OPTIONAL_FEATURE(OPT_PAYMENT_SECRET), + OPTIONAL_FEATURE(OPT_BASIC_MPP), + OPTIONAL_FEATURE(OPT_GOSSIP_QUERIES_EX), + OPTIONAL_FEATURE(OPT_STATIC_REMOTEKEY), + }; + + for (size_t i = 0; i < ARRAY_SIZE(features); i++) { + struct feature_set *f + = feature_set_for_feature(NULL, features[i]); + if (!ret) + ret = tal_steal(ctx, f); + else + feature_set_or(ret, take(f)); + } + + return ret; +} + int main(int argc, char *argv[]) { struct lightningd *ld; @@ -751,6 +784,9 @@ int main(int argc, char *argv[]) if (!ld->daemon_dir) errx(1, "Could not find daemons"); + /* Set up the feature bits for what we support */ + ld->feature_set = default_features(ld); + /*~ Handle early options; this moves us into --lightning-dir. * Plugins may add new options, which is why we are splitting * between early args (including --plugin registration) and diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index ab280eff4271..75f6c145d642 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -120,7 +120,7 @@ struct lightningd { struct node_id id; /* Feature set we offer. */ - const struct feature_set *feature_set; + struct feature_set *feature_set; /* My name is... my favorite color is... */ u8 *alias; /* At least 32 bytes (zero-filled) */ diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index c020fb423834..f25e86f3d53e 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -220,7 +220,8 @@ wallet_commit_channel(struct lightningd *ld, */ /* i.e. We set it now for the channel permanently. */ option_static_remotekey - = feature_negotiated(uc->peer->features, OPT_STATIC_REMOTEKEY); + = feature_negotiated(ld->feature_set, + uc->peer->features, OPT_STATIC_REMOTEKEY); channel = new_channel(uc->peer, uc->dbid, NULL, /* No shachain yet */ @@ -1009,7 +1010,8 @@ void peer_start_openingd(struct peer *peer, feerate_min(peer->ld, NULL), feerate_max(peer->ld, NULL), peer->features, - feature_negotiated(peer->features, + feature_negotiated(peer->ld->feature_set, + peer->features, OPT_STATIC_REMOTEKEY), send_msg, IFDEV(peer->ld->dev_force_tmp_channel_id, NULL), diff --git a/lightningd/options.c b/lightningd/options.c index 5be2fed728a8..0f65e96f7ba4 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -692,7 +692,7 @@ static char *test_subdaemons_and_exit(struct lightningd *ld) static char *list_features_and_exit(struct lightningd *ld) { - const char **features = list_supported_features(ld); + const char **features = list_supported_features(tmpctx, ld->feature_set); for (size_t i = 0; i < tal_count(features); i++) printf("%s\n", features[i]); exit(0); @@ -1004,34 +1004,11 @@ void setup_color_and_alias(struct lightningd *ld) } } -static struct feature_set *setup_default_features(void) -{ - static const u32 default_features[] = { - OPTIONAL_FEATURE(OPT_DATA_LOSS_PROTECT), - OPTIONAL_FEATURE(OPT_UPFRONT_SHUTDOWN_SCRIPT), - OPTIONAL_FEATURE(OPT_GOSSIP_QUERIES), - OPTIONAL_FEATURE(OPT_VAR_ONION), - OPTIONAL_FEATURE(OPT_PAYMENT_SECRET), - OPTIONAL_FEATURE(OPT_BASIC_MPP), - OPTIONAL_FEATURE(OPT_GOSSIP_QUERIES_EX), - OPTIONAL_FEATURE(OPT_STATIC_REMOTEKEY), - }; - u8 *f = tal_arr(NULL, u8, 0); - - for (size_t i = 0; i < ARRAY_SIZE(default_features); i++) - set_feature_bit(&f, default_features[i]); - - return features_core_init(take(f)); -} - void handle_early_opts(struct lightningd *ld, int argc, char *argv[]) { /* Make ccan/opt use tal for allocations */ setup_option_allocators(); - /* Make sure options are populated. */ - ld->feature_set = setup_default_features(); - /*~ List features immediately, before doing anything interesting */ opt_register_early_noarg("--list-features-only", list_features_and_exit, diff --git a/lightningd/pay.c b/lightningd/pay.c index cce8c641f68d..ff926c2813f4 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1406,7 +1406,7 @@ static struct command_result *json_listsendpays(struct command *cmd, struct bolt11 *b11; char *fail; - b11 = bolt11_decode(cmd, b11str, NULL, &fail); + b11 = bolt11_decode(cmd, b11str, cmd->ld->feature_set, NULL, &fail); if (!b11) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Invalid bolt11: %s", fail); diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 73d66b93808f..f00b66e9b18a 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -916,7 +916,7 @@ bool plugin_parse_getmanifest_response(const char *buffer, return true; } - if (!features_additional(fset)) { + if (!feature_set_or(plugin->plugins->ld->feature_set, fset)) { plugin_kill(plugin, "Custom featurebits already present"); return true; diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index 4425befbc56b..c68a73c67290 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -60,6 +60,13 @@ bool db_in_transaction(struct db *db UNNEEDED) /* Generated stub for fatal */ void fatal(const char *fmt UNNEEDED, ...) { fprintf(stderr, "fatal called!\n"); abort(); } +/* Generated stub for feature_set_for_feature */ +struct feature_set *feature_set_for_feature(const tal_t *ctx UNNEEDED, int feature UNNEEDED) +{ fprintf(stderr, "feature_set_for_feature called!\n"); abort(); } +/* Generated stub for feature_set_or */ +bool feature_set_or(struct feature_set *a UNNEEDED, + const struct feature_set *b TAKES UNNEEDED) +{ fprintf(stderr, "feature_set_or called!\n"); abort(); } /* Generated stub for free_htlcs */ void free_htlcs(struct lightningd *ld UNNEEDED, const struct channel *channel UNNEEDED) { fprintf(stderr, "free_htlcs called!\n"); abort(); } diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index c8635c20930d..daf9e0c81b98 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -23,6 +23,7 @@ void bitcoind_getutxout_(struct bitcoind *bitcoind UNNEEDED, { fprintf(stderr, "bitcoind_getutxout_ called!\n"); abort(); } /* Generated stub for bolt11_decode */ struct bolt11 *bolt11_decode(const tal_t *ctx UNNEEDED, const char *str UNNEEDED, + const struct feature_set *fset UNNEEDED, const char *description UNNEEDED, char **fail UNNEEDED) { fprintf(stderr, "bolt11_decode called!\n"); abort(); } /* Generated stub for bolt11_encode_ */ @@ -137,9 +138,6 @@ u32 get_feerate(const struct fee_states *fee_states UNNEEDED, enum side funder UNNEEDED, enum side side UNNEEDED) { fprintf(stderr, "get_feerate called!\n"); abort(); } -/* Generated stub for get_offered_bolt11features */ -u8 *get_offered_bolt11features(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "get_offered_bolt11features called!\n"); abort(); } /* Generated stub for htlc_is_trimmed */ bool htlc_is_trimmed(enum side htlc_owner UNNEEDED, struct amount_msat htlc_amount UNNEEDED, diff --git a/openingd/openingd.c b/openingd/openingd.c index 6a67c995e9ea..82dcdb3212a0 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -113,6 +113,8 @@ struct state { struct channel *channel; bool option_static_remotekey; + + struct feature_set *fset; }; static u8 *dev_upfront_shutdown_script(const tal_t *ctx) @@ -563,7 +565,7 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) * `payment_basepoint`, or `delayed_payment_basepoint` are not * valid secp256k1 pubkeys in compressed format. */ - if (feature_negotiated(state->features, + if (feature_negotiated(state->fset, state->features, OPT_UPFRONT_SHUTDOWN_SCRIPT)) { if (!fromwire_accept_channel_option_upfront_shutdown_script(state, msg, &id_in, @@ -644,6 +646,7 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) return towire_opening_funder_start_reply(state, funding_output_script, feature_negotiated( + state->fset, state->features, OPT_UPFRONT_SHUTDOWN_SCRIPT)); } @@ -904,7 +907,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) * `payment_basepoint`, or `delayed_payment_basepoint` are not valid * secp256k1 pubkeys in compressed format. */ - if (feature_negotiated(state->features, + if (feature_negotiated(state->fset, state->features, OPT_UPFRONT_SHUTDOWN_SCRIPT)) { if (!fromwire_open_channel_option_upfront_shutdown_script(state, open_channel_msg, &chain_hash, @@ -1481,7 +1484,6 @@ int main(int argc, char *argv[]) struct state *state = tal(NULL, struct state); struct secret *none; struct channel_id *force_tmp_channel_id; - struct feature_set *feature_set; subdaemon_setup(argc, argv); @@ -1493,7 +1495,7 @@ int main(int argc, char *argv[]) msg = wire_sync_read(tmpctx, REQ_FD); if (!fromwire_opening_init(state, msg, &chainparams, - &feature_set, + &state->fset, &state->localconf, &state->max_to_self_delay, &state->min_effective_htlc_capacity, @@ -1509,8 +1511,6 @@ int main(int argc, char *argv[]) &dev_fast_gossip)) master_badmsg(WIRE_OPENING_INIT, msg); - features_init(take(feature_set)); - #if DEVELOPER dev_force_tmp_channel_id = force_tmp_channel_id; #endif diff --git a/plugins/pay.c b/plugins/pay.c index f02ca195a74b..cc55f703e945 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -1283,7 +1283,8 @@ static struct command_result *json_pay(struct command *cmd, NULL)) return command_param_failed(); - b11 = bolt11_decode(cmd, b11str, NULL, &fail); + /* FIXME: We need to know our features! */ + b11 = bolt11_decode(cmd, b11str, NULL, NULL, &fail); if (!b11) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Invalid bolt11: %s", fail);