From ed30eb0a8163a4892257ca99d770b624fb9521e2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Apr 2020 14:33:47 +1030 Subject: [PATCH 01/11] connectd: do feature bits check after init exchange. This will help with the next patch, where we wean off using a global for features: connectd.c has access to the feature bits. Since connectd might now want to send a message, it needs the crypto_state non-const, which makes this less trivial than it would otherwise be. Signed-off-by: Rusty Russell --- connectd/connectd.c | 30 +++++++++++++++++++++------ connectd/connectd.h | 2 +- connectd/handshake.c | 8 +++---- connectd/handshake.h | 8 +++---- connectd/peer_exchange_initmsg.c | 18 ---------------- connectd/test/run-initiator-success.c | 2 +- connectd/test/run-responder-success.c | 2 +- devtools/gossipwith.c | 2 +- 8 files changed, 36 insertions(+), 36 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index e389e032ebb7..aec7bcb236f6 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -413,22 +413,40 @@ struct io_plan *peer_connected(struct io_conn *conn, struct daemon *daemon, const struct node_id *id, const struct wireaddr_internal *addr, - const struct crypto_state *cs, + struct crypto_state *cs, const u8 *features TAKES) { u8 *msg; struct per_peer_state *pps; + int unsup; if (node_set_get(&daemon->peers, id)) return peer_reconnected(conn, daemon, id, addr, cs, features); - /* We've successfully connected. */ - connected_to_peer(daemon, conn, id); - /* We promised we'd take it by marking it TAKEN above; prepare to free it. */ if (taken(features)) tal_steal(tmpctx, features); + /* BOLT #1: + * + * The receiving node: + * ... + * - upon receiving unknown _odd_ feature bits that are non-zero: + * - MUST ignore the bit. + * - upon receiving unknown _even_ feature bits that are non-zero: + * - MUST fail the connection. + */ + unsup = features_unsupported(features); + if (unsup != -1) { + msg = towire_errorfmt(NULL, NULL, "Unsupported feature %u", + unsup); + msg = cryptomsg_encrypt_msg(tmpctx, cs, take(msg)); + return io_write(conn, msg, tal_count(msg), io_close_cb, NULL); + } + + /* We've successfully connected. */ + connected_to_peer(daemon, conn, id); + /* This contains the per-peer state info; gossipd fills in pps->gs */ pps = new_per_peer_state(tmpctx, cs); @@ -466,7 +484,7 @@ struct io_plan *peer_connected(struct io_conn *conn, static struct io_plan *handshake_in_success(struct io_conn *conn, const struct pubkey *id_key, const struct wireaddr_internal *addr, - const struct crypto_state *cs, + struct crypto_state *cs, struct daemon *daemon) { struct node_id id; @@ -524,7 +542,7 @@ static struct io_plan *connection_in(struct io_conn *conn, struct daemon *daemon static struct io_plan *handshake_out_success(struct io_conn *conn, const struct pubkey *key, const struct wireaddr_internal *addr, - const struct crypto_state *cs, + struct crypto_state *cs, struct connecting *connect) { struct node_id id; diff --git a/connectd/connectd.h b/connectd/connectd.h index 8e16e3ab72cd..a66b768dad19 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -18,7 +18,7 @@ struct io_plan *peer_connected(struct io_conn *conn, struct daemon *daemon, const struct node_id *id, const struct wireaddr_internal *addr, - const struct crypto_state *cs, + struct crypto_state *cs, const u8 *features TAKES); #endif /* LIGHTNING_CONNECTD_CONNECTD_H */ diff --git a/connectd/handshake.c b/connectd/handshake.c index 66480c2bd3fa..8cc1fd51b8a0 100644 --- a/connectd/handshake.c +++ b/connectd/handshake.c @@ -181,7 +181,7 @@ struct handshake { struct io_plan *(*cb)(struct io_conn *conn, const struct pubkey *their_id, const struct wireaddr_internal *wireaddr, - const struct crypto_state *cs, + struct crypto_state *cs, void *cbarg); void *cbarg; }; @@ -351,7 +351,7 @@ static struct io_plan *handshake_succeeded(struct io_conn *conn, struct io_plan *(*cb)(struct io_conn *conn, const struct pubkey *their_id, const struct wireaddr_internal *addr, - const struct crypto_state *cs, + struct crypto_state *cs, void *cbarg); void *cbarg; struct pubkey their_id; @@ -968,7 +968,7 @@ struct io_plan *responder_handshake_(struct io_conn *conn, struct io_plan *(*cb)(struct io_conn *, const struct pubkey *, const struct wireaddr_internal *, - const struct crypto_state *, + struct crypto_state *, void *cbarg), void *cbarg) { @@ -990,7 +990,7 @@ struct io_plan *initiator_handshake_(struct io_conn *conn, struct io_plan *(*cb)(struct io_conn *, const struct pubkey *, const struct wireaddr_internal *, - const struct crypto_state *, + struct crypto_state *, void *cbarg), void *cbarg) { diff --git a/connectd/handshake.h b/connectd/handshake.h index 970782eac7b8..1e860e090c6a 100644 --- a/connectd/handshake.h +++ b/connectd/handshake.h @@ -16,7 +16,7 @@ struct pubkey; struct io_conn *, \ const struct pubkey *, \ const struct wireaddr_internal *, \ - const struct crypto_state *), \ + struct crypto_state *), \ (cbarg)) @@ -27,7 +27,7 @@ struct io_plan *initiator_handshake_(struct io_conn *conn, struct io_plan *(*cb)(struct io_conn *, const struct pubkey *, const struct wireaddr_internal *, - const struct crypto_state *, + struct crypto_state *, void *cbarg), void *cbarg); @@ -39,7 +39,7 @@ struct io_plan *initiator_handshake_(struct io_conn *conn, struct io_conn *, \ const struct pubkey *, \ const struct wireaddr_internal *, \ - const struct crypto_state *), \ + struct crypto_state *), \ (cbarg)) struct io_plan *responder_handshake_(struct io_conn *conn, @@ -48,7 +48,7 @@ struct io_plan *responder_handshake_(struct io_conn *conn, struct io_plan *(*cb)(struct io_conn *, const struct pubkey *, const struct wireaddr_internal *, - const struct crypto_state *, + struct crypto_state *, void *cbarg), void *cbarg); diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index 20c9244248cb..a9c425873077 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -44,7 +44,6 @@ static struct io_plan *peer_init_received(struct io_conn *conn, { u8 *msg = cryptomsg_decrypt_body(tmpctx, &peer->cs, peer->msg); u8 *globalfeatures, *features; - int unsup; struct tlv_init_tlvs *tlvs = tlv_init_tlvs_new(msg); if (!msg) @@ -89,23 +88,6 @@ static struct io_plan *peer_init_received(struct io_conn *conn, * window where it was: combine the two. */ features = featurebits_or(tmpctx, take(features), globalfeatures); - /* BOLT #1: - * - * The receiving node: - * ... - * - upon receiving unknown _odd_ feature bits that are non-zero: - * - MUST ignore the bit. - * - upon receiving unknown _even_ feature bits that are non-zero: - * - MUST fail the connection. - */ - unsup = features_unsupported(features); - if (unsup != -1) { - msg = towire_errorfmt(NULL, NULL, "Unsupported feature %u", - unsup); - msg = cryptomsg_encrypt_msg(NULL, &peer->cs, take(msg)); - return io_write(conn, msg, tal_count(msg), io_close_cb, NULL); - } - /* Usually return io_close_taken_fd, but may wait for old peer to * be disconnected if it's a reconnect. */ return peer_connected(conn, peer->daemon, &peer->id, diff --git a/connectd/test/run-initiator-success.c b/connectd/test/run-initiator-success.c index 96e102b5023b..a7fa6fa0af57 100644 --- a/connectd/test/run-initiator-success.c +++ b/connectd/test/run-initiator-success.c @@ -218,7 +218,7 @@ static struct io_plan *test_read(struct io_conn *conn, static struct io_plan *success(struct io_conn *conn UNUSED, const struct pubkey *them, const struct wireaddr_internal *addr UNUSED, - const struct crypto_state *cs, + struct crypto_state *cs, void *unused UNUSED) { assert(pubkey_eq(them, &rs_pub)); diff --git a/connectd/test/run-responder-success.c b/connectd/test/run-responder-success.c index 5a69771408d4..c35400164f41 100644 --- a/connectd/test/run-responder-success.c +++ b/connectd/test/run-responder-success.c @@ -217,7 +217,7 @@ static struct io_plan *test_read(struct io_conn *conn, static struct io_plan *success(struct io_conn *conn UNUSED, const struct pubkey *them UNUSED, const struct wireaddr_internal *addr UNUSED, - const struct crypto_state *cs, + struct crypto_state *cs, void *unused UNUSED) { assert(secret_eq_str(&cs->sk, expect_sk)); diff --git a/devtools/gossipwith.c b/devtools/gossipwith.c index a0ce66f1fc84..1ad73cbc7f20 100644 --- a/devtools/gossipwith.c +++ b/devtools/gossipwith.c @@ -142,7 +142,7 @@ static struct io_plan *simple_read(struct io_conn *conn, static struct io_plan *handshake_success(struct io_conn *conn, const struct pubkey *them, const struct wireaddr_internal *addr, - const struct crypto_state *orig_cs, + struct crypto_state *orig_cs, char **args) { u8 *msg; From 025c8c40dfe6050078e0ba79022410b680910edb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Apr 2020 14:34:47 +1030 Subject: [PATCH 02/11] 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 --- 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 #include -/* 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); From 5d0fad7309fb21f1b843d1d035f178b308bdc6e7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Apr 2020 14:35:47 +1030 Subject: [PATCH 03/11] plugins: add `feature_set` to init object. Shows what features we use in various contexts, including those added by plugins in getmanifest. Signed-off-by: Rusty Russell Changelog-Added: Plugin: `feature_set` object added to `init` --- lightningd/plugin.c | 9 +++++++++ tests/plugins/show_feature_set.py | 17 +++++++++++++++++ tests/test_plugin.py | 11 +++++++++++ 3 files changed, 37 insertions(+) create mode 100755 tests/plugins/show_feature_set.py diff --git a/lightningd/plugin.c b/lightningd/plugin.c index f00b66e9b18a..a40f878fd844 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -1169,6 +1169,15 @@ plugin_populate_init_request(struct plugin *plugin, struct jsonrpc_request *req) json_add_string(req->stream, "rpc-file", ld->rpc_filename); json_add_bool(req->stream, "startup", plugin->plugins->startup); json_add_string(req->stream, "network", chainparams->network_name); + json_object_start(req->stream, "feature_set"); + for (enum feature_place fp = 0; fp < NUM_FEATURE_PLACE; fp++) { + if (plugin_feature_place_names[fp]) { + json_add_hex_talarr(req->stream, + plugin_feature_place_names[fp], + ld->feature_set->bits[fp]); + } + } + json_object_end(req->stream); json_object_end(req->stream); } diff --git a/tests/plugins/show_feature_set.py b/tests/plugins/show_feature_set.py new file mode 100755 index 000000000000..060f59e289ad --- /dev/null +++ b/tests/plugins/show_feature_set.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +from pyln.client import Plugin + +plugin = Plugin() + + +@plugin.init() +def init(options, configuration, plugin): + plugin.feature_set = configuration['feature_set'] + + +@plugin.method('getfeatureset') +def getfeatureset(plugin): + return plugin.feature_set + + +plugin.run() diff --git a/tests/test_plugin.py b/tests/test_plugin.py index c32bacdc6341..2bb49d3bb1e2 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1153,3 +1153,14 @@ def test_hook_crash(node_factory, executor, bitcoind): # Collect the results: [f.result(TIMEOUT) for f in futures] + + +def test_feature_set(node_factory): + plugin = os.path.join(os.path.dirname(__file__), 'plugins/show_feature_set.py') + l1 = node_factory.get_node(options={"plugin": plugin}) + + fs = l1.rpc.call('getfeatureset') + assert fs['init'] == expected_features() + assert fs['node'] == expected_features() + assert fs['channel'] == '' + assert 'invoice' in fs From 48dc1c6db3cf6e1d141fae62da6ff6049d0f9886 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Apr 2020 14:36:47 +1030 Subject: [PATCH 04/11] libplugin: demarshal and stash the feature set given by lightningd. Signed-off-by: Rusty Russell --- plugins/libplugin.c | 40 +++++++++++++++++++++++++++++++++++++++- plugins/libplugin.h | 3 +++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 7653a40cde39..201768195d30 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -82,6 +83,9 @@ struct plugin { /* Timers */ struct timers timers; size_t in_timer; + + /* Feature set for lightningd */ + struct feature_set *fset; }; @@ -148,6 +152,11 @@ jsonrpc_request_start_(struct plugin *plugin, struct command *cmd, return out; } +const struct feature_set *plugin_feature_set(const struct plugin *p) +{ + return p->fset; +} + static void jsonrpc_finish_and_send(struct plugin *p, struct json_stream *js) { json_object_compat_end(js); @@ -710,11 +719,37 @@ static struct io_plan *rpc_conn_init(struct io_conn *conn, rpc_conn_write_request(conn, plugin)); } +static struct feature_set *json_to_feature_set(struct plugin *plugin, + const char *buf, + const jsmntok_t *features) +{ + struct feature_set *fset = talz(plugin, struct feature_set); + const jsmntok_t *t; + size_t i; + + json_for_each_obj(i, t, features) { + enum feature_place p; + if (json_tok_streq(buf, t, "init")) + p = INIT_FEATURE; + else if (json_tok_streq(buf, t, "node")) + p = NODE_ANNOUNCE_FEATURE; + else if (json_tok_streq(buf, t, "channel")) + p = CHANNEL_FEATURE; + else if (json_tok_streq(buf, t, "invoice")) + p = BOLT11_FEATURE; + else + continue; + fset->bits[p] = json_tok_bin_from_hex(fset, buf, t + 1); + } + return fset; +} + static struct command_result *handle_init(struct command *cmd, const char *buf, const jsmntok_t *params) { - const jsmntok_t *configtok, *rpctok, *dirtok, *opttok, *nettok, *t; + const jsmntok_t *configtok, *rpctok, *dirtok, *opttok, *nettok, *fsettok, + *t; struct sockaddr_un addr; size_t i; char *dir, *network; @@ -734,6 +769,9 @@ static struct command_result *handle_init(struct command *cmd, network = json_strdup(tmpctx, buf, nettok); chainparams = chainparams_for_network(network); + fsettok = json_delve(buf, configtok, ".feature_set"); + p->fset = json_to_feature_set(p, buf, fsettok); + rpctok = json_delve(buf, configtok, ".rpc-file"); p->rpc_conn->fd = socket(AF_UNIX, SOCK_STREAM, 0); if (rpctok->end - rpctok->start + 1 > sizeof(addr.sun_path)) diff --git a/plugins/libplugin.h b/plugins/libplugin.h index 18da74066bca..37deff2dbc5a 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -90,6 +90,9 @@ struct plugin_hook { const jsmntok_t *params); }; +/* Return the feature set of the current lightning node */ +const struct feature_set *plugin_feature_set(const struct plugin *p); + /* Helper to create a JSONRPC2 request stream. Send it with `send_outreq`. */ struct out_req * jsonrpc_request_start_(struct plugin *plugin, struct command *cmd, From 6351e82d555bbdd6db73e8d7c1ff7d01c46e6be5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Apr 2020 14:37:47 +1030 Subject: [PATCH 05/11] lightningd: return `features` in connect response. This is useful in general, but in particular it allows fundchannel to avoid YA query to figure out if it can wumbo. Signed-off-by: Rusty Russell Changelog-Added: JSON: `connect` returns `features` of the connected peer on success. --- doc/lightning-connect.7 | 3 ++- doc/lightning-connect.7.md | 3 ++- lightningd/connect_control.c | 13 +++++++------ lightningd/connect_control.h | 2 +- lightningd/peer_control.c | 6 +++--- lightningd/test/run-invoice-select-inchan.c | 2 +- wallet/test/run-wallet.c | 2 +- 7 files changed, 17 insertions(+), 14 deletions(-) diff --git a/doc/lightning-connect.7 b/doc/lightning-connect.7 index f3ec7ebf5a1f..20bac2c6d022 100644 --- a/doc/lightning-connect.7 +++ b/doc/lightning-connect.7 @@ -42,7 +42,8 @@ another node\. Once the peer is connected a channel can be opened with .SH RETURN VALUE -On success the peer \fIid\fR is returned\. +On success the peer \fIid\fR is returned, as well as a hexidecimal \fIfeatures\fR +bitmap\. .SH ERRORS diff --git a/doc/lightning-connect.7.md b/doc/lightning-connect.7.md index 48e0960220cc..572ee6d7d00b 100644 --- a/doc/lightning-connect.7.md +++ b/doc/lightning-connect.7.md @@ -39,7 +39,8 @@ lightning-fundchannel(7). RETURN VALUE ------------ -On success the peer *id* is returned. +On success the peer *id* is returned, as well as a hexidecimal *features* +bitmap. ERRORS ------ diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 1e80e9701521..5e5d119ee56a 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -68,10 +68,11 @@ static struct connect *find_connect(struct lightningd *ld, } static struct command_result *connect_cmd_succeed(struct command *cmd, - const struct node_id *id) + const struct peer *peer) { struct json_stream *response = json_stream_success(cmd); - json_add_node_id(response, "id", id); + json_add_node_id(response, "id", &peer->id); + json_add_hex_talarr(response, "features", peer->features); return command_success(cmd, response); } @@ -139,7 +140,7 @@ static struct command_result *json_connect(struct command *cmd, if (peer->uncommitted_channel || (channel && channel->connected)) { - return connect_cmd_succeed(cmd, &id); + return connect_cmd_succeed(cmd, peer); } } @@ -256,14 +257,14 @@ static void connect_failed(struct lightningd *ld, const u8 *msg) delay_then_reconnect(channel, seconds_to_delay, addrhint); } -void connect_succeeded(struct lightningd *ld, const struct node_id *id) +void connect_succeeded(struct lightningd *ld, const struct peer *peer) { struct connect *c; /* We can have multiple connect commands: fail them all */ - while ((c = find_connect(ld, id)) != NULL) { + while ((c = find_connect(ld, &peer->id)) != NULL) { /* They delete themselves from list */ - connect_cmd_succeed(c->cmd, id); + connect_cmd_succeed(c->cmd, peer); } } diff --git a/lightningd/connect_control.h b/lightningd/connect_control.h index fed15ef6e1dd..64c5adaa8f40 100644 --- a/lightningd/connect_control.h +++ b/lightningd/connect_control.h @@ -12,7 +12,7 @@ void connectd_activate(struct lightningd *ld); void delay_then_reconnect(struct channel *channel, u32 seconds_delay, const struct wireaddr_internal *addrhint TAKES); -void connect_succeeded(struct lightningd *ld, const struct node_id *id); +void connect_succeeded(struct lightningd *ld, const struct peer *peer); void gossip_connect_result(struct lightningd *ld, const u8 *msg); #endif /* LIGHTNING_LIGHTNINGD_CONNECT_CONTROL_H */ diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 20cba0bf8846..a860312bc2dd 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -996,9 +996,6 @@ void peer_connected(struct lightningd *ld, const u8 *msg, per_peer_state_set_fds(hook_payload->pps, peer_fd, gossip_fd, gossip_store_fd); - /* Complete any outstanding connect commands. */ - connect_succeeded(ld, &id); - /* If we're already dealing with this peer, hand off to correct * subdaemon. Otherwise, we'll hand to openingd to wait there. */ peer = peer_by_id(ld, &id); @@ -1010,6 +1007,9 @@ void peer_connected(struct lightningd *ld, const u8 *msg, peer_update_features(peer, features); + /* Complete any outstanding connect commands. */ + connect_succeeded(ld, peer); + /* Can't be opening, since we wouldn't have sent peer_disconnected. */ assert(!peer->uncommitted_channel); hook_payload->channel = peer_active_channel(peer); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index daf9e0c81b98..b42a9593fcda 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -78,7 +78,7 @@ struct command_result *command_success(struct command *cmd UNNEEDED, { fprintf(stderr, "command_success called!\n"); abort(); } /* Generated stub for connect_succeeded */ -void connect_succeeded(struct lightningd *ld UNNEEDED, const struct node_id *id UNNEEDED) +void connect_succeeded(struct lightningd *ld UNNEEDED, const struct peer *peer UNNEEDED) { fprintf(stderr, "connect_succeeded called!\n"); abort(); } /* Generated stub for delay_then_reconnect */ void delay_then_reconnect(struct channel *channel UNNEEDED, u32 seconds_delay UNNEEDED, diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index bd185443c3d1..146d698ce0ef 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -80,7 +80,7 @@ struct command_result *command_success(struct command *cmd UNNEEDED, { fprintf(stderr, "command_success called!\n"); abort(); } /* Generated stub for connect_succeeded */ -void connect_succeeded(struct lightningd *ld UNNEEDED, const struct node_id *id UNNEEDED) +void connect_succeeded(struct lightningd *ld UNNEEDED, const struct peer *peer UNNEEDED) { fprintf(stderr, "connect_succeeded called!\n"); abort(); } /* Generated stub for create_onionreply */ struct onionreply *create_onionreply(const tal_t *ctx UNNEEDED, From d7abc982542f0c8e504f86bf053717a63987a20d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Apr 2020 14:38:47 +1030 Subject: [PATCH 06/11] plugins/pay: use feature set from lightningd to decode bolt11. This means we correctly reject invoices with features we don't understand. Signed-off-by: Rusty Russell --- plugins/pay.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/pay.c b/plugins/pay.c index cc55f703e945..ffae830d690b 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -1283,8 +1283,8 @@ static struct command_result *json_pay(struct command *cmd, NULL)) return command_param_failed(); - /* FIXME: We need to know our features! */ - b11 = bolt11_decode(cmd, b11str, NULL, NULL, &fail); + b11 = bolt11_decode(cmd, b11str, plugin_feature_set(cmd->plugin), + NULL, &fail); if (!b11) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Invalid bolt11: %s", fail); From 322b1c9b1eaef9d382e91662d1b218a51c3f11ff Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Apr 2020 14:39:47 +1030 Subject: [PATCH 07/11] common/features: add option_support_large_channel. Signed-off-by: Rusty Russell --- common/features.c | 4 ++++ common/features.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/common/features.c b/common/features.c index 8a24fb6d4277..58a101904c99 100644 --- a/common/features.c +++ b/common/features.c @@ -52,6 +52,10 @@ static const struct feature_style feature_styles[] = { .copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT, [NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT, [BOLT11_FEATURE] = FEATURE_REPRESENT } }, + { OPT_LARGE_CHANNELS, + .copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT, + [NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT, + [CHANNEL_FEATURE] = FEATURE_REPRESENT_AS_OPTIONAL } }, }; static enum feature_copy_style feature_copy_style(u32 f, enum feature_place p) diff --git a/common/features.h b/common/features.h index cfdcdfb2c9cc..40b681f5dba3 100644 --- a/common/features.h +++ b/common/features.h @@ -92,8 +92,10 @@ u8 *featurebits_or(const tal_t *ctx, const u8 *f1 TAKES, const u8 *f2 TAKES); * * | 14/15 | `payment_secret` |... IN9 ... * | 16/17 | `basic_mpp` |... IN9 ... + * | 18/19 | `option_support_large_channel` |... INC+ ... */ #define OPT_PAYMENT_SECRET 14 #define OPT_BASIC_MPP 16 +#define OPT_LARGE_CHANNELS 18 #endif /* LIGHTNING_COMMON_FEATURES_H */ From 62e32437dd087e4714896339e3740fb63fe55a42 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 3 Apr 2020 10:33:55 +1030 Subject: [PATCH 08/11] lightningd / openingd: remove limits if we negotiate option_support_large_channel. Note that now we check capacity once we've figured out which peer, which broke a test (we returned "unknown peer" instead of "capacity exceeded"), so we rework that too. Signed-off-by: Rusty Russell --- lightningd/opening_control.c | 21 ++++++++++++++------- openingd/openingd.c | 20 ++++++++++++++------ tests/test_connection.py | 8 ++++---- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index f25e86f3d53e..622b9522cbd4 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -1146,12 +1146,6 @@ static struct command_result *json_fund_channel_start(struct command *cmd, NULL)) return command_param_failed(); - if (amount_sat_greater(*amount, chainparams->max_funding)) - return command_fail(cmd, FUND_MAX_EXCEEDED, - "Amount exceeded %s", - type_to_string(tmpctx, struct amount_sat, - &chainparams->max_funding)); - if (push_msat && amount_msat_greater_sat(*push_msat, *amount)) return command_fail(cmd, FUND_CANNOT_AFFORD, "Requested to push_msat of %s is greater than " @@ -1199,6 +1193,20 @@ static struct command_result *json_fund_channel_start(struct command *cmd, return command_fail(cmd, LIGHTNINGD, "Already funding channel"); } + /* BOLT #2: + * - if both nodes advertised `option_support_large_channel`: + * - MAY set `funding_satoshis` greater than or equal to 2^24 satoshi. + * - otherwise: + * - MUST set `funding_satoshis` to less than 2^24 satoshi. + */ + if (!feature_negotiated(cmd->ld->feature_set, + peer->features, OPT_LARGE_CHANNELS) + && amount_sat_greater(*amount, chainparams->max_funding)) + return command_fail(cmd, FUND_MAX_EXCEEDED, + "Amount exceeded %s", + type_to_string(tmpctx, struct amount_sat, + &chainparams->max_funding)); + fc->push = push_msat ? *push_msat : AMOUNT_MSAT(0); fc->channel_flags = OUR_CHANNEL_FLAGS; if (!*announce_channel) { @@ -1207,7 +1215,6 @@ static struct command_result *json_fund_channel_start(struct command *cmd, type_to_string(fc, struct node_id, id)); } - assert(!amount_sat_greater(*amount, chainparams->max_funding)); peer->uncommitted_channel->fc = tal_steal(peer->uncommitted_channel, fc); fc->uc = peer->uncommitted_channel; diff --git a/openingd/openingd.c b/openingd/openingd.c index 82dcdb3212a0..aed7ce18cafb 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -487,9 +487,13 @@ static bool setup_channel_funder(struct state *state) * * The sending node: *... - * - MUST set `funding_satoshis` to less than 2^24 satoshi. + * - if both nodes advertised `option_support_large_channel`: + * - MAY set `funding_satoshis` greater than or equal to 2^24 satoshi. + * - otherwise: + * - MUST set `funding_satoshis` to less than 2^24 satoshi. */ - if (amount_sat_greater(state->funding, chainparams->max_funding)) { + if (!feature_negotiated(state->fset, state->features, OPT_LARGE_CHANNELS) + && amount_sat_greater(state->funding, chainparams->max_funding)) { status_failed(STATUS_FAIL_MASTER_IO, "funding_satoshis must be < %s, not %s", type_to_string(tmpctx, struct amount_sat, @@ -969,11 +973,15 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) return NULL; } - /* BOLT #2 FIXME: + /* BOLT #2: * - * The receiving node ... MUST fail the channel if `funding-satoshis` - * is greater than or equal to 2^24 */ - if (amount_sat_greater(state->funding, chainparams->max_funding)) { + * The receiving node MUST fail the channel if: + *... + * - `funding_satoshis` is greater than or equal to 2^24 and the receiver does not support + * `option_support_large_channel`. */ + /* We choose to require *negotiation*, not just support! */ + if (!feature_negotiated(state->fset, state->features, OPT_LARGE_CHANNELS) + && amount_sat_greater(state->funding, chainparams->max_funding)) { negotiation_failed(state, false, "funding_satoshis %s too large", type_to_string(tmpctx, struct amount_sat, diff --git a/tests/test_connection.py b/tests/test_connection.py index 3e8020445447..89a1d08a39a3 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -888,10 +888,6 @@ def test_funding_external_wallet_corners(node_factory, bitcoind): amount = 2**24 l1.fundwallet(amount + 10000000) - # Fail to open (too large) - with pytest.raises(RpcError, match=r'Amount exceeded 16777215'): - l1.rpc.fundchannel_start(l2.info['id'], amount) - amount = amount - 1 fake_txid = '929764844a8f9938b669a60a1d51a11c9e2613c7eb4776e4126f1f20c0a685c3' fake_txout = 0 @@ -907,6 +903,10 @@ def test_funding_external_wallet_corners(node_factory, bitcoind): with pytest.raises(RpcError, match=r'No channel funding in progress.'): l1.rpc.fundchannel_complete(l2.info['id'], fake_txid, fake_txout) + # Fail to open (too large) + with pytest.raises(RpcError, match=r'Amount exceeded 16777215'): + l1.rpc.fundchannel_start(l2.info['id'], amount + 1) + l1.rpc.fundchannel_start(l2.info['id'], amount) with pytest.raises(RpcError, match=r'Already funding channel'): l1.rpc.fundchannel(l2.info['id'], amount) From 4fc4d7307db6fe7805efd857b92734515353e034 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 3 Apr 2020 10:33:58 +1030 Subject: [PATCH 09/11] lightningd: add large-channels / wumbo option. Signed-off-by: Rusty Russell Changelog-added: `large-channels` option to negotiate opening larger channels. --- doc/lightningd-config.5 | 8 +++++++ doc/lightningd-config.5.md | 7 ++++++ lightningd/options.c | 17 ++++++++++++++ tests/test_connection.py | 46 +++++++++++++++++++++++++++++++++++++- 4 files changed, 77 insertions(+), 1 deletion(-) diff --git a/doc/lightningd-config.5 b/doc/lightningd-config.5 index e5235053454a..16e1e84df266 100644 --- a/doc/lightningd-config.5 +++ b/doc/lightningd-config.5 @@ -281,6 +281,14 @@ extremely busy node for you to even notice\. .SH Lightning channel and HTLC options + \fBlarge-channels\fR +Removes capacity limits for channel creation\. Version 1\.0 of the specification +limited channel sizes to 16777216 satoshi\. With this option (which your +node will advertize to peers), your node will accept larger incoming channels +and if the peer supports it, will open larger channels\. Note: this option +is spelled \fBlarge-channels\fR but it's pronounced \fBwumbo\fR\. + + \fBwatchtime-blocks\fR=\fIBLOCKS\fR How long we need to spot an outdated close attempt: on opening a channel we tell our peer that this is how long they’ll have to wait if they diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 47de08353915..f6989373577c 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -230,6 +230,13 @@ extremely busy node for you to even notice. ### Lightning channel and HTLC options + **large-channels** +Removes capacity limits for channel creation. Version 1.0 of the specification +limited channel sizes to 16777215 satoshi. With this option (which your +node will advertize to peers), your node will accept larger incoming channels +and if the peer supports it, will open larger channels. Note: this option +is spelled **large-channels** but it's pronounced **wumbo**. + **watchtime-blocks**=*BLOCKS* How long we need to spot an outdated close attempt: on opening a channel we tell our peer that this is how long they’ll have to wait if they diff --git a/lightningd/options.c b/lightningd/options.c index 0f65e96f7ba4..9289e6e96dbc 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -742,6 +742,14 @@ static char *opt_start_daemon(struct lightningd *ld) errx(1, "Died with signal %u", WTERMSIG(exitcode)); } +static char *opt_set_wumbo(struct lightningd *ld) +{ + feature_set_or(ld->feature_set, + take(feature_set_for_feature(NULL, + OPTIONAL_FEATURE(OPT_LARGE_CHANNELS)))); + return NULL; +} + static void register_opts(struct lightningd *ld) { /* This happens before plugins started */ @@ -774,6 +782,11 @@ static void register_opts(struct lightningd *ld) &ld->wallet_dsn, "Location of the wallet database."); + /* This affects our features, so set early. */ + opt_register_early_noarg("--large-channels|--wumbo", + opt_set_wumbo, ld, + "Allow channels larger than 0.16777215 BTC"); + opt_register_noarg("--help|-h", opt_lightningd_usage, ld, "Print this message."); opt_register_arg("--rgb", opt_set_rgb, NULL, ld, @@ -1188,6 +1201,10 @@ static void add_config(struct lightningd *ld, ? "false" : "true"); } else if (opt->cb == (void *)opt_set_hsm_password) { json_add_bool(response, "encrypted-hsm", ld->encrypted_hsm); + } else if (opt->cb == (void *)opt_set_wumbo) { + json_add_bool(response, "wumbo", + feature_offered(ld->feature_set->bits[INIT_FEATURE], + OPT_LARGE_CHANNELS)); } else { /* Insert more decodes here! */ assert(!"A noarg option was added but was not handled"); diff --git a/tests/test_connection.py b/tests/test_connection.py index 89a1d08a39a3..0a589216291d 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3,7 +3,7 @@ from fixtures import * # noqa: F401,F403 from fixtures import TEST_NETWORK from flaky import flaky # noqa: F401 -from pyln.client import RpcError +from pyln.client import RpcError, Millisatoshi from utils import ( DEVELOPER, only_one, wait_for, sync_blockheight, VALGRIND, TIMEOUT, SLOW_MACHINE, expected_features @@ -2220,3 +2220,47 @@ def test_pay_disconnect_stress(node_factory, executor): pass fut.result() + + +def test_wumbo_channels(node_factory, bitcoind): + f = bytes.fromhex(expected_features()) + + # OPT_LARGE_CHANNELS = 18 (19 for us). 0x080000 + f = (f[:-3] + bytes([f[-3] | 0x08]) + f[-2:]).hex() + + l1, l2, l3 = node_factory.get_nodes(3, + opts=[{'large-channels': None}, + {'large-channels': None}, + {}]) + conn = l1.rpc.connect(l2.info['id'], 'localhost', port=l2.port) + assert conn['features'] == f + assert only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['features'] == f + + # Now, can we open a giant channel? + l1.fundwallet(1 << 26) + l1.rpc.fundchannel(l2.info['id'], 1 << 24) + + # Get that mined, and announced. + bitcoind.generate_block(6, wait_for_mempool=1) + + # Connect l3, get gossip. + l3.rpc.connect(l1.info['id'], 'localhost', port=l1.port) + wait_for(lambda: len(l3.rpc.listnodes(l1.info['id'])['nodes']) == 1) + wait_for(lambda: 'features' in only_one(l3.rpc.listnodes(l1.info['id'])['nodes'])) + + # Make sure channel capacity is what we expected. + assert ([c['amount_msat'] for c in l3.rpc.listchannels()['channels']] + == [Millisatoshi(str(1 << 24) + "sat")] * 2) + + # Make sure we can't open a wumbo channel if we don't agree. + with pytest.raises(RpcError, match='Amount exceeded'): + l1.rpc.fundchannel(l3.info['id'], 1 << 24) + + # But we can open and announce a normal one. + l1.rpc.fundchannel(l3.info['id'], 'all') + bitcoind.generate_block(6, wait_for_mempool=1) + wait_for(lambda: l1.channel_state(l3) == 'CHANNELD_NORMAL') + + # Make sure l2 sees correct size. + wait_for(lambda: [c['amount_msat'] for c in l2.rpc.listchannels(l1.get_channel_scid(l3))['channels']] + == [Millisatoshi(str((1 << 24) - 1) + "sat")] * 2) From 32caf2cb8bb93a4e066ab976e294d297ce1f3091 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 3 Apr 2020 10:33:58 +1030 Subject: [PATCH 10/11] plugins/fundchannel: make 'all' do the right thing for wumbo. Signed-off-by: Rusty Russell --- doc/lightning-fundchannel.7 | 5 +++-- doc/lightning-fundchannel.7.md | 5 +++-- plugins/fundchannel.c | 27 +++++++++++++++++++++++---- tests/test_connection.py | 14 ++++++++++++++ 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/doc/lightning-fundchannel.7 b/doc/lightning-fundchannel.7 index 4db82f286e50..660aaeb79536 100644 --- a/doc/lightning-fundchannel.7 +++ b/doc/lightning-fundchannel.7 @@ -27,11 +27,12 @@ for the channel\. \fIamount\fR is the amount in satoshis taken from the internal wallet to fund the channel\. The string \fIall\fR can be used to specify all available -funds (or 16777215 satoshi if more is available)\. Otherwise, it is in +funds (or 16777215 satoshi if more is available and large channels were not negotiated with the peer)\. Otherwise, it is in satoshi precision; it can be a whole number, a whole number ending in \fIsat\fR, a whole number ending in \fI000msat\fR, or a number with 1 to 8 decimal places ending in \fIbtc\fR\. The value cannot be less than the dust -limit, currently set to 546, nor more than 16777215 satoshi\. +limit, currently set to 546, nor more than 16777215 satoshi (unless large +channels were negotiated with the peer)\. \fIfeerate\fR is an optional feerate used for the opening transaction and as diff --git a/doc/lightning-fundchannel.7.md b/doc/lightning-fundchannel.7.md index c7fb9bd4c7e8..84426bde1596 100644 --- a/doc/lightning-fundchannel.7.md +++ b/doc/lightning-fundchannel.7.md @@ -27,11 +27,12 @@ for the channel. *amount* is the amount in satoshis taken from the internal wallet to fund the channel. The string *all* can be used to specify all available -funds (or 16777215 satoshi if more is available). Otherwise, it is in +funds (or 16777215 satoshi if more is available and large channels were not negotiated with the peer). Otherwise, it is in satoshi precision; it can be a whole number, a whole number ending in *sat*, a whole number ending in *000msat*, or a number with 1 to 8 decimal places ending in *btc*. The value cannot be less than the dust -limit, currently set to 546, nor more than 16777215 satoshi. +limit, currently set to 546, nor more than 16777215 satoshi (unless large +channels were negotiated with the peer). *feerate* is an optional feerate used for the opening transaction and as initial feerate for commitment and HTLC transactions. It can be one of diff --git a/plugins/fundchannel.c b/plugins/fundchannel.c index a2c1ad884c2c..594cb0f00a0d 100644 --- a/plugins/fundchannel.c +++ b/plugins/fundchannel.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,9 @@ struct funding_req { bool funding_all; struct amount_msat *push_msat; + /* Features offered by this peer. */ + const u8 *features; + bool *announce_channel; u32 *minconf; @@ -329,7 +333,10 @@ static struct command_result *post_dryrun(struct command *cmd, plugin_err(cmd->plugin, "Error creating placebo funding tx, funding_out not found. %s", hex); /* Update funding to actual amount */ - if (fr->funding_all && amount_sat_greater(funding, chainparams->max_funding)) + if (fr->funding_all + && !feature_negotiated(plugin_feature_set(cmd->plugin), + fr->features, OPT_LARGE_CHANNELS) + && amount_sat_greater(funding, chainparams->max_funding)) funding = chainparams->max_funding; fr->funding_str = type_to_string(fr, struct amount_sat, &funding); @@ -341,9 +348,21 @@ static struct command_result *exec_dryrun(struct command *cmd, const jsmntok_t *result, struct funding_req *fr) { - struct out_req *req = jsonrpc_request_start(cmd->plugin, cmd, "txprepare", - post_dryrun, forward_error, - fr); + struct out_req *req; + const jsmntok_t *t; + + /* Stash features so we can wumbo. */ + t = json_get_member(buf, result, "features"); + if (!t) + plugin_err(cmd->plugin, "No features found in connect response?"); + fr->features = json_tok_bin_from_hex(fr, buf, t); + if (!fr->features) + plugin_err(cmd->plugin, "Bad features '%.*s' in connect response?", + t->end - t->start, buf + t->start); + + req = jsonrpc_request_start(cmd->plugin, cmd, "txprepare", + post_dryrun, forward_error, + fr); /* Now that we've tried connecting, we do a 'dry-run' of txprepare, * so we can get an accurate idea of the funding amount */ diff --git a/tests/test_connection.py b/tests/test_connection.py index 0a589216291d..7aac83df5ef9 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -2264,3 +2264,17 @@ def test_wumbo_channels(node_factory, bitcoind): # Make sure l2 sees correct size. wait_for(lambda: [c['amount_msat'] for c in l2.rpc.listchannels(l1.get_channel_scid(l3))['channels']] == [Millisatoshi(str((1 << 24) - 1) + "sat")] * 2) + + # Make sure 'all' works with wumbo peers. + l1.rpc.close(l2.info['id']) + bitcoind.generate_block(1, wait_for_mempool=1) + wait_for(lambda: l1.channel_state(l2) == 'ONCHAIN') + + l1.rpc.connect(l2.info['id'], 'localhost', port=l2.port) + l1.rpc.fundchannel(l2.info['id'], 'all') + bitcoind.generate_block(1, wait_for_mempool=1) + wait_for(lambda: 'CHANNELD_NORMAL' in [c['state'] for c in only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']]) + + # Exact amount depends on fees, but it will be wumbo! + amount = [c['funding_msat'][l1.info['id']] for c in only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'] if c['state'] == 'CHANNELD_NORMAL'][0] + assert Millisatoshi(amount) > Millisatoshi(str((1 << 24) - 1) + "sat") From 6ea51f071c492ce25e2585a93cefac4402cf4173 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 3 Apr 2020 10:33:59 +1030 Subject: [PATCH 11/11] cleanup: make 'u8 *features' and 'struct feature_set *fset' more explicit. It's almost always "their_features" and "our_features" respectively, so make those names clear. Suggested-by: @cdecker Signed-off-by: Rusty Russell --- channeld/channel_wire.csv | 6 ++--- channeld/channeld.c | 15 +++++++----- common/bolt11.c | 14 ++++++----- common/bolt11.h | 2 +- common/features.c | 33 ++++++++++++------------- common/features.h | 11 +++++---- connectd/connect_wire.csv | 2 +- connectd/connectd.c | 41 ++++++++++++++++++-------------- connectd/connectd.h | 2 +- connectd/peer_exchange_initmsg.c | 6 ++--- connectd/peer_exchange_initmsg.h | 2 +- gossipd/gossip_generation.c | 3 ++- gossipd/gossip_wire.csv | 2 +- gossipd/gossipd.c | 2 +- gossipd/gossipd.h | 2 +- lightningd/channel_control.c | 4 ++-- lightningd/connect_control.c | 4 ++-- lightningd/gossip_control.c | 2 +- lightningd/invoice.c | 4 ++-- lightningd/lightningd.c | 2 +- lightningd/lightningd.h | 2 +- lightningd/opening_control.c | 17 ++++++------- lightningd/options.c | 7 +++--- lightningd/pay.c | 2 +- lightningd/peer_control.c | 19 ++++++++------- lightningd/peer_control.h | 2 +- lightningd/plugin.c | 4 ++-- openingd/opening_wire.csv | 2 +- openingd/openingd.c | 22 +++++++++-------- plugins/fundchannel.c | 8 +++---- plugins/libplugin.c | 6 ++--- 31 files changed, 134 insertions(+), 116 deletions(-) diff --git a/channeld/channel_wire.csv b/channeld/channel_wire.csv index c7505f5af338..95e9b7d6be98 100644 --- a/channeld/channel_wire.csv +++ b/channeld/channel_wire.csv @@ -8,7 +8,7 @@ # Begin! (passes gossipd-client fd) msgtype,channel_init,1000 msgdata,channel_init,chainparams,chainparams, -msgdata,channel_init,feature_set,feature_set, +msgdata,channel_init,our_features,feature_set, msgdata,channel_init,funding_txid,bitcoin_txid, msgdata,channel_init,funding_txout,u16, msgdata,channel_init,funding_satoshi,amount_sat, @@ -64,8 +64,8 @@ msgdata,channel_init,init_peer_pkt_len,u16, msgdata,channel_init,init_peer_pkt,u8,init_peer_pkt_len msgdata,channel_init,reached_announce_depth,bool, msgdata,channel_init,last_remote_secret,secret, -msgdata,channel_init,lflen,u16, -msgdata,channel_init,localfeatures,u8,lflen +msgdata,channel_init,flen,u16, +msgdata,channel_init,their_features,u8,flen msgdata,channel_init,upfront_shutdown_script_len,u16, msgdata,channel_init,upfront_shutdown_script,u8,upfront_shutdown_script_len msgdata,channel_init,remote_ann_node_sig,?secp256k1_ecdsa_signature, diff --git a/channeld/channeld.c b/channeld/channeld.c index dc04e03f9956..9768bda91bea 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -74,10 +74,10 @@ struct peer { u64 next_index[NUM_SIDES]; /* Features peer supports. */ - u8 *features; + u8 *their_features; /* Features we support. */ - struct feature_set *fset; + struct feature_set *our_features; /* Tolerable amounts for feerate (only relevant for fundee). */ u32 feerate_min, feerate_max; @@ -418,7 +418,9 @@ 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->fset, peer->features); + u8 *cannounce, *features + = get_agreed_channelfeatures(tmpctx, peer->our_features, + peer->their_features); if (peer->channel_direction == 0) { first = LOCAL; @@ -2328,7 +2330,8 @@ 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->fset, peer->features, + dataloss_protect = feature_negotiated(peer->our_features, + peer->their_features, OPT_DATA_LOSS_PROTECT); /* Both these options give us extra fields to check. */ @@ -3059,7 +3062,7 @@ static void init_channel(struct peer *peer) msg = wire_sync_read(tmpctx, MASTER_FD); if (!fromwire_channel_init(peer, msg, &chainparams, - &peer->fset, + &peer->our_features, &funding_txid, &funding_txout, &funding, &minimum_depth, @@ -3105,7 +3108,7 @@ static void init_channel(struct peer *peer) &funding_signed, &peer->announce_depth_reached, &last_remote_per_commit_secret, - &peer->features, + &peer->their_features, &peer->remote_upfront_shutdown_script, &remote_ann_node_sig, &remote_ann_bitcoin_sig, diff --git a/common/bolt11.c b/common/bolt11.c index 08173ef72bd2..9acaac645d4d 100644 --- a/common/bolt11.c +++ b/common/bolt11.c @@ -489,7 +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, + const struct feature_set *our_features, struct hash_u5 *hu5, u5 **data, size_t *data_len, size_t data_length) @@ -512,9 +512,10 @@ static char *decode_9(struct bolt11 *b11, * - if the `9` field contains unknown _even_ bits that are non-zero: * - MUST fail the payment. */ - /* We skip this check for the cli tool, which sets fset to NULL */ - if (fset) { - badf = features_unsupported(fset, b11->features, BOLT11_FEATURE); + /* We skip this check for the cli tool, which sets our_features to NULL */ + if (our_features) { + badf = features_unsupported(our_features, + b11->features, BOLT11_FEATURE); if (badf != -1) return tal_fmt(b11, "9: unknown feature bit %i", badf); } @@ -545,7 +546,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 struct feature_set *our_features, const char *description, char **fail) { char *hrp, *amountstr, *prefix; @@ -740,7 +741,8 @@ struct bolt11 *bolt11_decode(const tal_t *ctx, const char *str, data_length); break; case '9': - problem = decode_9(b11, fset, &hu5, &data, &data_len, + problem = decode_9(b11, our_features, &hu5, + &data, &data_len, data_length); break; case 's': diff --git a/common/bolt11.h b/common/bolt11.h index dae6da562039..0f301241f328 100644 --- a/common/bolt11.h +++ b/common/bolt11.h @@ -80,7 +80,7 @@ struct bolt11 { * 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 struct feature_set *our_features, const char *description, char **fail); /* Initialize an empty bolt11 struct with optional amount */ diff --git a/common/features.c b/common/features.c index 58a101904c99..ba20abda4ffa 100644 --- a/common/features.c +++ b/common/features.c @@ -158,17 +158,17 @@ static void clear_feature_bit(u8 *features, u32 bit) * it sets. */ u8 *get_agreed_channelfeatures(const tal_t *ctx, - const struct feature_set *ours, - const u8 *theirfeatures) + const struct feature_set *our_features, + const u8 *their_features) { - u8 *f = tal_dup_talarr(ctx, u8, ours->bits[CHANNEL_FEATURE]); + u8 *f = tal_dup_talarr(ctx, u8, our_features->bits[CHANNEL_FEATURE]); size_t max_len = 0; /* Clear any features which they didn't offer too */ for (size_t i = 0; i < 8 * tal_count(f); i += 2) { if (!feature_offered(f, i)) continue; - if (!feature_offered(theirfeatures, i)) { + if (!feature_offered(their_features, i)) { clear_feature_bit(f, COMPULSORY_FEATURE(i)); clear_feature_bit(f, OPTIONAL_FEATURE(i)); continue; @@ -197,11 +197,11 @@ bool feature_offered(const u8 *features, size_t f) || feature_is_set(features, OPTIONAL_FEATURE(f)); } -bool feature_negotiated(const struct feature_set *ours, - const u8 *lfeatures, size_t f) +bool feature_negotiated(const struct feature_set *our_features, + const u8 *their_features, size_t f) { - return feature_offered(lfeatures, f) - && feature_offered(ours->bits[INIT_FEATURE], f); + return feature_offered(their_features, f) + && feature_offered(our_features->bits[INIT_FEATURE], f); } /** @@ -215,7 +215,7 @@ bool feature_negotiated(const struct feature_set *ours, * * Returns -1 on success, or first unsupported feature. */ -static int all_supported_features(const struct feature_set *ours, +static int all_supported_features(const struct feature_set *our_features, const u8 *bitmap, enum feature_place p) { @@ -226,7 +226,7 @@ static int all_supported_features(const struct feature_set *ours, if (!test_bit(bitmap, bitnum/8, bitnum%8)) continue; - if (feature_offered(ours->bits[p], bitnum)) + if (feature_offered(our_features->bits[p], bitnum)) continue; return bitnum; @@ -234,16 +234,17 @@ static int all_supported_features(const struct feature_set *ours, return -1; } -int features_unsupported(const struct feature_set *ours, const u8 *theirs, +int features_unsupported(const struct feature_set *our_features, + const u8 *their_features, 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(theirs, + if (feature_is_set(their_features, COMPULSORY_FEATURE(OPT_INITIAL_ROUTING_SYNC))) return COMPULSORY_FEATURE(OPT_INITIAL_ROUTING_SYNC); - return all_supported_features(ours, theirs, p); + return all_supported_features(our_features, their_features, p); } static const char *feature_name(const tal_t *ctx, size_t f) @@ -269,12 +270,12 @@ static const char *feature_name(const tal_t *ctx, size_t f) } const char **list_supported_features(const tal_t *ctx, - const struct feature_set *ours) + const struct feature_set *fset) { const char **list = tal_arr(ctx, const char *, 0); - 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)) + for (size_t i = 0; i < tal_bytelen(fset->bits[INIT_FEATURE]) * 8; i++) { + if (test_bit(fset->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 40b681f5dba3..bf6b534e1c72 100644 --- a/common/features.h +++ b/common/features.h @@ -32,24 +32,25 @@ bool feature_set_or(struct feature_set *a, /* Returns -1 if we're OK with all these offered features, otherwise first * unsupported (even) feature. */ -int features_unsupported(const struct feature_set *ours, const u8 *theirs, +int features_unsupported(const struct feature_set *our_features, + const u8 *their_features, enum feature_place p); /* For the features in channel_announcement */ u8 *get_agreed_channelfeatures(const tal_t *ctx, - const struct feature_set *ours, + const struct feature_set *our_features, 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 struct feature_set *ours, - const u8 *features, size_t f); +bool feature_negotiated(const struct feature_set *our_features, + const u8 *their_features, size_t f); /* Return a list of what (init) features we advertize. */ const char **list_supported_features(const tal_t *ctx, - const struct feature_set *ours); + const struct feature_set *fset); /* Low-level helpers to deal with big-endian bitfields. */ bool feature_is_set(const u8 *features, size_t bit); diff --git a/connectd/connect_wire.csv b/connectd/connect_wire.csv index cf8bf988b778..ef11459aed20 100644 --- a/connectd/connect_wire.csv +++ b/connectd/connect_wire.csv @@ -6,7 +6,7 @@ msgtype,connectctl_init,2000 msgdata,connectctl_init,chainparams,chainparams, -msgdata,connectctl_init,feature_set,feature_set, +msgdata,connectctl_init,our_features,feature_set, msgdata,connectctl_init,id,node_id, msgdata,connectctl_init,num_wireaddrs,u16, msgdata,connectctl_init,wireaddrs,wireaddr_internal,num_wireaddrs diff --git a/connectd/connectd.c b/connectd/connectd.c index c6bb4eca6dac..68e35637ef96 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -156,7 +156,7 @@ struct daemon { bool use_v3_autotor; /* Our features, as lightningd told us */ - struct feature_set *fset; + struct feature_set *our_features; }; /* Peers we're trying to reach: we iterate through addrs until we succeed @@ -294,7 +294,7 @@ static void connected_to_peer(struct daemon *daemon, */ static bool get_gossipfds(struct daemon *daemon, const struct node_id *id, - const u8 *features, + const u8 *their_features, struct per_peer_state *pps) { bool gossip_queries_feature, initial_routing_sync, success; @@ -303,13 +303,14 @@ 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(daemon->fset, features, OPT_GOSSIP_QUERIES); + = feature_negotiated(daemon->our_features, their_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 * backlog of existing gossip. */ initial_routing_sync - = feature_offered(features, OPT_INITIAL_ROUTING_SYNC); + = feature_offered(their_features, OPT_INITIAL_ROUTING_SYNC); /*~ We do this communication sync, since gossipd is our friend and * it's easier. If gossipd fails, we fail. */ @@ -348,7 +349,7 @@ struct peer_reconnected { struct node_id id; struct wireaddr_internal addr; struct crypto_state cs; - const u8 *features; + const u8 *their_features; }; /*~ For simplicity, lightningd only ever deals with a single connection per @@ -365,7 +366,7 @@ static struct io_plan *retry_peer_connected(struct io_conn *conn, /*~ Usually the pattern is to return this directly, but we have to free * our temporary structure. */ plan = peer_connected(conn, pr->daemon, &pr->id, &pr->addr, &pr->cs, - take(pr->features)); + take(pr->their_features)); tal_free(pr); return plan; } @@ -377,7 +378,7 @@ static struct io_plan *peer_reconnected(struct io_conn *conn, const struct node_id *id, const struct wireaddr_internal *addr, const struct crypto_state *cs, - const u8 *features TAKES) + const u8 *their_features TAKES) { u8 *msg; struct peer_reconnected *pr; @@ -397,7 +398,7 @@ static struct io_plan *peer_reconnected(struct io_conn *conn, /*~ Note that tal_dup_talarr() will do handle the take() of features * (turning it into a simply tal_steal() in those cases). */ - pr->features = tal_dup_talarr(pr, u8, features); + pr->their_features = tal_dup_talarr(pr, u8, their_features); /*~ ccan/io supports waiting on an address: in this case, the key in * the peer set. When someone calls `io_wake()` on that address, it @@ -417,18 +418,19 @@ struct io_plan *peer_connected(struct io_conn *conn, const struct node_id *id, const struct wireaddr_internal *addr, struct crypto_state *cs, - const u8 *features TAKES) + const u8 *their_features TAKES) { u8 *msg; struct per_peer_state *pps; int unsup; if (node_set_get(&daemon->peers, id)) - return peer_reconnected(conn, daemon, id, addr, cs, features); + return peer_reconnected(conn, daemon, id, addr, cs, + their_features); /* We promised we'd take it by marking it TAKEN above; prepare to free it. */ - if (taken(features)) - tal_steal(tmpctx, features); + if (taken(their_features)) + tal_steal(tmpctx, their_features); /* BOLT #1: * @@ -439,7 +441,8 @@ 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(daemon->fset, features, INIT_FEATURE); + unsup = features_unsupported(daemon->our_features, their_features, + INIT_FEATURE); if (unsup != -1) { msg = towire_errorfmt(NULL, NULL, "Unsupported feature %u", unsup); @@ -454,11 +457,11 @@ struct io_plan *peer_connected(struct io_conn *conn, pps = new_per_peer_state(tmpctx, cs); /* If gossipd can't give us a file descriptor, we give up connecting. */ - if (!get_gossipfds(daemon, id, features, pps)) + if (!get_gossipfds(daemon, id, their_features, pps)) return io_close(conn); /* Create message to tell master peer has connected. */ - msg = towire_connect_peer_connected(NULL, id, addr, pps, features); + msg = towire_connect_peer_connected(NULL, id, addr, pps, their_features); /*~ daemon_conn is a message queue for inter-daemon communication: we * queue up the `connect_peer_connected` message to tell lightningd @@ -493,7 +496,8 @@ 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, daemon->fset, cs, &id, addr); + return peer_exchange_initmsg(conn, daemon, daemon->our_features, + cs, &id, addr); } /*~ When we get a connection in we set up its network address then call @@ -554,7 +558,8 @@ static struct io_plan *handshake_out_success(struct io_conn *conn, connect->connstate = "Exchanging init messages"; status_peer_debug(&id, "Connect OUT"); return peer_exchange_initmsg(conn, connect->daemon, - connect->daemon->fset, cs, &id, addr); + connect->daemon->our_features, + cs, &id, addr); } struct io_plan *connection_out(struct io_conn *conn, struct connecting *connect) @@ -1211,7 +1216,7 @@ static struct io_plan *connect_init(struct io_conn *conn, if (!fromwire_connectctl_init( daemon, msg, &chainparams, - &daemon->fset, + &daemon->our_features, &daemon->id, &proposed_wireaddr, &proposed_listen_announce, diff --git a/connectd/connectd.h b/connectd/connectd.h index a66b768dad19..e5fbd35b4555 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -19,6 +19,6 @@ struct io_plan *peer_connected(struct io_conn *conn, const struct node_id *id, const struct wireaddr_internal *addr, struct crypto_state *cs, - const u8 *features TAKES); + const u8 *their_features TAKES); #endif /* LIGHTNING_CONNECTD_CONNECTD_H */ diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index a8c96f19d451..7c7427e5d8c4 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -135,7 +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 feature_set *our_features, const struct crypto_state *cs, const struct node_id *id, const struct wireaddr_internal *addr) @@ -182,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, - fset->bits[GLOBAL_INIT_FEATURE], - fset->bits[INIT_FEATURE], + our_features->bits[GLOBAL_INIT_FEATURE], + our_features->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 ee7c5ae09906..9948ba3498cb 100644 --- a/connectd/peer_exchange_initmsg.h +++ b/connectd/peer_exchange_initmsg.h @@ -12,7 +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 feature_set *our_features, const struct crypto_state *cs, const struct node_id *id, const struct wireaddr_internal *addr); diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index 3fab950e2e8a..773f8f5b5380 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -39,7 +39,8 @@ static u8 *create_node_announcement(const tal_t *ctx, struct daemon *daemon, announcement = towire_node_announcement(ctx, sig, - daemon->fset->bits[NODE_ANNOUNCE_FEATURE], + daemon->our_features->bits + [NODE_ANNOUNCE_FEATURE], timestamp, &daemon->id, daemon->rgb, daemon->alias, addresses); diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index 706faae6a676..60a638fa58e3 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -6,7 +6,7 @@ # Initialize the gossip daemon. msgtype,gossipctl_init,3000 msgdata,gossipctl_init,chainparams,chainparams, -msgdata,gossipctl_init,feature_set,feature_set, +msgdata,gossipctl_init,our_features,feature_set, msgdata,gossipctl_init,id,node_id, msgdata,gossipctl_init,rgb,u8,3 msgdata,gossipctl_init,alias,u8,32 diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 46a10f584337..73d225cec891 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -834,7 +834,7 @@ static struct io_plan *gossip_init(struct io_conn *conn, if (!fromwire_gossipctl_init(daemon, msg, &chainparams, - &daemon->fset, + &daemon->our_features, &daemon->id, daemon->rgb, daemon->alias, diff --git a/gossipd/gossipd.h b/gossipd/gossipd.h index 14ae9f736302..66da99adf90a 100644 --- a/gossipd/gossipd.h +++ b/gossipd/gossipd.h @@ -59,7 +59,7 @@ struct daemon { struct seeker *seeker; /* Features lightningd told us to set. */ - struct feature_set *fset; + struct feature_set *our_features; }; /* This represents each peer we're gossiping with */ diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 01abc330351f..699173aa6b51 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -471,7 +471,7 @@ void peer_start_channeld(struct channel *channel, initmsg = towire_channel_init(tmpctx, chainparams, - ld->feature_set, + ld->our_features, &channel->funding_txid, channel->funding_outnum, channel->funding, @@ -517,7 +517,7 @@ void peer_start_channeld(struct channel *channel, funding_signed, reached_announce_depth, &last_remote_per_commit_secret, - channel->peer->features, + channel->peer->their_features, channel->remote_upfront_shutdown_script, remote_ann_node_sig, remote_ann_bitcoin_sig, diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 5e5d119ee56a..3229e3d29cc2 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -72,7 +72,7 @@ static struct command_result *connect_cmd_succeed(struct command *cmd, { struct json_stream *response = json_stream_success(cmd); json_add_node_id(response, "id", &peer->id); - json_add_hex_talarr(response, "features", peer->features); + json_add_hex_talarr(response, "features", peer->their_features); return command_success(cmd, response); } @@ -365,7 +365,7 @@ int connectd_init(struct lightningd *ld) msg = towire_connectctl_init( tmpctx, chainparams, - ld->feature_set, + ld->our_features, &ld->id, wireaddrs, listen_announce, diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 0b6760fa6516..4018d0699257 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -209,7 +209,7 @@ void gossip_init(struct lightningd *ld, int connectd_fd) msg = towire_gossipctl_init( tmpctx, chainparams, - ld->feature_set, + ld->our_features, &ld->id, ld->rgb, ld->alias, diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 2edeb92d1df3..43153e2569b5 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -1014,7 +1014,7 @@ static struct command_result *json_invoice(struct command *cmd, info->b11->payment_secret = tal_dup(info->b11, struct secret, &payment_secret); info->b11->features = tal_dup_talarr(info->b11, u8, - cmd->ld->feature_set + cmd->ld->our_features ->bits[BOLT11_FEATURE]); #if DEVELOPER @@ -1321,7 +1321,7 @@ static struct command_result *json_decodepay(struct command *cmd, NULL)) return command_param_failed(); - b11 = bolt11_decode(cmd, str, cmd->ld->feature_set, desc, &fail); + b11 = bolt11_decode(cmd, str, cmd->ld->our_features, desc, &fail); if (!b11) { return command_fail(cmd, LIGHTNINGD, "Invalid bolt11: %s", fail); diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 649fd5099ea9..31e6ec9997d6 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -785,7 +785,7 @@ int main(int argc, char *argv[]) errx(1, "Could not find daemons"); /* Set up the feature bits for what we support */ - ld->feature_set = default_features(ld); + ld->our_features = default_features(ld); /*~ Handle early options; this moves us into --lightning-dir. * Plugins may add new options, which is why we are splitting diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 75f6c145d642..a239122df98a 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -120,7 +120,7 @@ struct lightningd { struct node_id id; /* Feature set we offer. */ - struct feature_set *feature_set; + struct feature_set *our_features; /* 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 622b9522cbd4..fe8067ca34c8 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -220,8 +220,9 @@ wallet_commit_channel(struct lightningd *ld, */ /* i.e. We set it now for the channel permanently. */ option_static_remotekey - = feature_negotiated(ld->feature_set, - uc->peer->features, OPT_STATIC_REMOTEKEY); + = feature_negotiated(ld->our_features, + uc->peer->their_features, + OPT_STATIC_REMOTEKEY); channel = new_channel(uc->peer, uc->dbid, NULL, /* No shachain yet */ @@ -1000,7 +1001,7 @@ void peer_start_openingd(struct peer *peer, msg = towire_opening_init(NULL, chainparams, - peer->ld->feature_set, + peer->ld->our_features, &uc->our_config, max_to_self_delay, min_effective_htlc_capacity, @@ -1009,9 +1010,9 @@ void peer_start_openingd(struct peer *peer, uc->minimum_depth, feerate_min(peer->ld, NULL), feerate_max(peer->ld, NULL), - peer->features, - feature_negotiated(peer->ld->feature_set, - peer->features, + peer->their_features, + feature_negotiated(peer->ld->our_features, + peer->their_features, OPT_STATIC_REMOTEKEY), send_msg, IFDEV(peer->ld->dev_force_tmp_channel_id, NULL), @@ -1199,8 +1200,8 @@ static struct command_result *json_fund_channel_start(struct command *cmd, * - otherwise: * - MUST set `funding_satoshis` to less than 2^24 satoshi. */ - if (!feature_negotiated(cmd->ld->feature_set, - peer->features, OPT_LARGE_CHANNELS) + if (!feature_negotiated(cmd->ld->our_features, + peer->their_features, OPT_LARGE_CHANNELS) && amount_sat_greater(*amount, chainparams->max_funding)) return command_fail(cmd, FUND_MAX_EXCEEDED, "Amount exceeded %s", diff --git a/lightningd/options.c b/lightningd/options.c index 9289e6e96dbc..1cc9bd9d72b6 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(tmpctx, ld->feature_set); + const char **features = list_supported_features(tmpctx, ld->our_features); for (size_t i = 0; i < tal_count(features); i++) printf("%s\n", features[i]); exit(0); @@ -744,7 +744,7 @@ static char *opt_start_daemon(struct lightningd *ld) static char *opt_set_wumbo(struct lightningd *ld) { - feature_set_or(ld->feature_set, + feature_set_or(ld->our_features, take(feature_set_for_feature(NULL, OPTIONAL_FEATURE(OPT_LARGE_CHANNELS)))); return NULL; @@ -1203,7 +1203,8 @@ static void add_config(struct lightningd *ld, json_add_bool(response, "encrypted-hsm", ld->encrypted_hsm); } else if (opt->cb == (void *)opt_set_wumbo) { json_add_bool(response, "wumbo", - feature_offered(ld->feature_set->bits[INIT_FEATURE], + feature_offered(ld->our_features + ->bits[INIT_FEATURE], OPT_LARGE_CHANNELS)); } else { /* Insert more decodes here! */ diff --git a/lightningd/pay.c b/lightningd/pay.c index ff926c2813f4..38e2e71710b2 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, cmd->ld->feature_set, NULL, &fail); + b11 = bolt11_decode(cmd, b11str, cmd->ld->our_features, NULL, &fail); if (!b11) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Invalid bolt11: %s", fail); diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index a860312bc2dd..61c6cc911785 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -74,10 +74,11 @@ static void destroy_peer(struct peer *peer) list_del_from(&peer->ld->peers, &peer->list); } -static void peer_update_features(struct peer *peer, const u8 *features TAKES) +static void peer_update_features(struct peer *peer, + const u8 *their_features TAKES) { - tal_free(peer->features); - peer->features = tal_dup_talarr(peer, u8, features); + tal_free(peer->their_features); + peer->their_features = tal_dup_talarr(peer, u8, their_features); } struct peer *new_peer(struct lightningd *ld, u64 dbid, @@ -92,7 +93,7 @@ struct peer *new_peer(struct lightningd *ld, u64 dbid, peer->id = *id; peer->uncommitted_channel = NULL; peer->addr = *addr; - peer->features = NULL; + peer->their_features = NULL; list_head_init(&peer->channels); peer->direction = node_id_idx(&peer->ld->id, &peer->id); #if DEVELOPER @@ -851,7 +852,7 @@ peer_connected_serialize(struct peer_connected_hook_payload *payload, json_add_string( stream, "addr", type_to_string(stream, struct wireaddr_internal, &payload->addr)); - json_add_hex_talarr(stream, "features", p->features); + json_add_hex_talarr(stream, "features", p->their_features); json_object_end(stream); /* .peer */ } @@ -980,7 +981,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg, int peer_fd, int gossip_fd, int gossip_store_fd) { struct node_id id; - u8 *features; + u8 *their_features; struct peer *peer; struct peer_connected_hook_payload *hook_payload; @@ -989,7 +990,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg, if (!fromwire_connect_peer_connected(hook_payload, msg, &id, &hook_payload->addr, &hook_payload->pps, - &features)) + &their_features)) fatal("Connectd gave bad CONNECT_PEER_CONNECTED message %s", tal_hex(msg, msg)); @@ -1005,7 +1006,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg, tal_steal(peer, hook_payload); hook_payload->peer = peer; - peer_update_features(peer, features); + peer_update_features(peer, their_features); /* Complete any outstanding connect commands. */ connect_succeeded(ld, peer); @@ -1173,7 +1174,7 @@ static void json_add_peer(struct lightningd *ld, struct wireaddr_internal, &p->addr)); json_array_end(response); - json_add_hex_talarr(response, "features", p->features); + json_add_hex_talarr(response, "features", p->their_features); } json_array_start(response, "channels"); diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index 0d53760a9c9a..58261f16fb2e 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -40,7 +40,7 @@ struct peer { struct wireaddr_internal addr; /* We keep a copy of their feature bits */ - const u8 *features; + const u8 *their_features; /* If we open a channel our direction will be this */ u8 direction; diff --git a/lightningd/plugin.c b/lightningd/plugin.c index a40f878fd844..52e82ce0cd8e 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -916,7 +916,7 @@ bool plugin_parse_getmanifest_response(const char *buffer, return true; } - if (!feature_set_or(plugin->plugins->ld->feature_set, fset)) { + if (!feature_set_or(plugin->plugins->ld->our_features, fset)) { plugin_kill(plugin, "Custom featurebits already present"); return true; @@ -1174,7 +1174,7 @@ plugin_populate_init_request(struct plugin *plugin, struct jsonrpc_request *req) if (plugin_feature_place_names[fp]) { json_add_hex_talarr(req->stream, plugin_feature_place_names[fp], - ld->feature_set->bits[fp]); + ld->our_features->bits[fp]); } } json_object_end(req->stream); diff --git a/openingd/opening_wire.csv b/openingd/opening_wire.csv index 8c5254cd59f4..ef1adb13f501 100644 --- a/openingd/opening_wire.csv +++ b/openingd/opening_wire.csv @@ -7,7 +7,7 @@ msgtype,opening_init,6000 # Which network are we configured for? msgdata,opening_init,chainparams,chainparams, -msgdata,opening_init,feature_set,feature_set, +msgdata,opening_init,our_features,feature_set, # Base configuration we'll offer (channel reserve will vary with amount) msgdata,opening_init,our_config,channel_config, # Minimum/maximum configuration values we'll accept diff --git a/openingd/openingd.c b/openingd/openingd.c index aed7ce18cafb..46e33f315cee 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -67,7 +67,7 @@ struct state { struct per_peer_state *pps; /* Features they offered */ - u8 *features; + u8 *their_features; /* Constraints on a channel they open. */ u32 minimum_depth; @@ -114,7 +114,7 @@ struct state { bool option_static_remotekey; - struct feature_set *fset; + struct feature_set *our_features; }; static u8 *dev_upfront_shutdown_script(const tal_t *ctx) @@ -492,7 +492,8 @@ static bool setup_channel_funder(struct state *state) * - otherwise: * - MUST set `funding_satoshis` to less than 2^24 satoshi. */ - if (!feature_negotiated(state->fset, state->features, OPT_LARGE_CHANNELS) + if (!feature_negotiated(state->our_features, + state->their_features, OPT_LARGE_CHANNELS) && amount_sat_greater(state->funding, chainparams->max_funding)) { status_failed(STATUS_FAIL_MASTER_IO, "funding_satoshis must be < %s, not %s", @@ -569,7 +570,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->fset, state->features, + if (feature_negotiated(state->our_features, state->their_features, OPT_UPFRONT_SHUTDOWN_SCRIPT)) { if (!fromwire_accept_channel_option_upfront_shutdown_script(state, msg, &id_in, @@ -650,8 +651,8 @@ 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, + state->our_features, + state->their_features, OPT_UPFRONT_SHUTDOWN_SCRIPT)); } @@ -911,7 +912,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->fset, state->features, + if (feature_negotiated(state->our_features, state->their_features, OPT_UPFRONT_SHUTDOWN_SCRIPT)) { if (!fromwire_open_channel_option_upfront_shutdown_script(state, open_channel_msg, &chain_hash, @@ -980,7 +981,8 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) * - `funding_satoshis` is greater than or equal to 2^24 and the receiver does not support * `option_support_large_channel`. */ /* We choose to require *negotiation*, not just support! */ - if (!feature_negotiated(state->fset, state->features, OPT_LARGE_CHANNELS) + if (!feature_negotiated(state->our_features, state->their_features, + OPT_LARGE_CHANNELS) && amount_sat_greater(state->funding, chainparams->max_funding)) { negotiation_failed(state, false, "funding_satoshis %s too large", @@ -1503,7 +1505,7 @@ int main(int argc, char *argv[]) msg = wire_sync_read(tmpctx, REQ_FD); if (!fromwire_opening_init(state, msg, &chainparams, - &state->fset, + &state->our_features, &state->localconf, &state->max_to_self_delay, &state->min_effective_htlc_capacity, @@ -1512,7 +1514,7 @@ int main(int argc, char *argv[]) &state->our_funding_pubkey, &state->minimum_depth, &state->min_feerate, &state->max_feerate, - &state->features, + &state->their_features, &state->option_static_remotekey, &inner, &force_tmp_channel_id, diff --git a/plugins/fundchannel.c b/plugins/fundchannel.c index 594cb0f00a0d..10b9206cc757 100644 --- a/plugins/fundchannel.c +++ b/plugins/fundchannel.c @@ -27,7 +27,7 @@ struct funding_req { struct amount_msat *push_msat; /* Features offered by this peer. */ - const u8 *features; + const u8 *their_features; bool *announce_channel; u32 *minconf; @@ -335,7 +335,7 @@ static struct command_result *post_dryrun(struct command *cmd, /* Update funding to actual amount */ if (fr->funding_all && !feature_negotiated(plugin_feature_set(cmd->plugin), - fr->features, OPT_LARGE_CHANNELS) + fr->their_features, OPT_LARGE_CHANNELS) && amount_sat_greater(funding, chainparams->max_funding)) funding = chainparams->max_funding; @@ -355,8 +355,8 @@ static struct command_result *exec_dryrun(struct command *cmd, t = json_get_member(buf, result, "features"); if (!t) plugin_err(cmd->plugin, "No features found in connect response?"); - fr->features = json_tok_bin_from_hex(fr, buf, t); - if (!fr->features) + fr->their_features = json_tok_bin_from_hex(fr, buf, t); + if (!fr->their_features) plugin_err(cmd->plugin, "Bad features '%.*s' in connect response?", t->end - t->start, buf + t->start); diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 201768195d30..02cb0dd1253d 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -85,7 +85,7 @@ struct plugin { size_t in_timer; /* Feature set for lightningd */ - struct feature_set *fset; + struct feature_set *our_features; }; @@ -154,7 +154,7 @@ jsonrpc_request_start_(struct plugin *plugin, struct command *cmd, const struct feature_set *plugin_feature_set(const struct plugin *p) { - return p->fset; + return p->our_features; } static void jsonrpc_finish_and_send(struct plugin *p, struct json_stream *js) @@ -770,7 +770,7 @@ static struct command_result *handle_init(struct command *cmd, chainparams = chainparams_for_network(network); fsettok = json_delve(buf, configtok, ".feature_set"); - p->fset = json_to_feature_set(p, buf, fsettok); + p->our_features = json_to_feature_set(p, buf, fsettok); rpctok = json_delve(buf, configtok, ".rpc-file"); p->rpc_conn->fd = socket(AF_UNIX, SOCK_STREAM, 0);