From 168009c105e1d0de121b2923e364b388986359fa Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 14 Aug 2020 03:08:02 +0930 Subject: [PATCH] features: require dependent features at init handshake. This simplifies our test matrix, as we never have to handle talking to peers that specify one but not the other. This is particularly important for option_anchor_outputs which assumes option_static_remotekey. Signed-off-by: Rusty Russell --- common/features.c | 36 ++++++++++++++++++++++++++++++++++++ common/features.h | 6 ++++++ connectd/connectd.c | 9 +++++++++ 3 files changed, 51 insertions(+) diff --git a/common/features.c b/common/features.c index ff193c203..ebf14fbdd 100644 --- a/common/features.c +++ b/common/features.c @@ -77,6 +77,26 @@ static const struct feature_style feature_styles[] = { #endif }; +struct dependency { + size_t depender; + size_t must_also_have; +}; + +static const struct dependency feature_deps[] = { + /* BOLT #9: + * Name | Description | Context | Dependencies | + *... + * `gossip_queries_ex` | ... | ... | `gossip_queries` | + *... + * `payment_secret` | ... | ... | `var_onion_optin` | + *... + * `basic_mpp` | ... | ... | `payment_secret` | + */ + { OPT_GOSSIP_QUERIES_EX, OPT_GOSSIP_QUERIES }, + { OPT_PAYMENT_SECRET, OPT_VAR_ONION }, + { OPT_BASIC_MPP, OPT_PAYMENT_SECRET }, +}; + static enum feature_copy_style feature_copy_style(u32 f, enum feature_place p) { for (size_t i = 0; i < ARRAY_SIZE(feature_styles); i++) { @@ -224,6 +244,22 @@ bool feature_negotiated(const struct feature_set *our_features, && feature_offered(our_features->bits[INIT_FEATURE], f); } +bool feature_check_depends(const u8 *their_features, + size_t *depender, size_t *missing_dependency) +{ + for (size_t i = 0; i < ARRAY_SIZE(feature_deps); i++) { + if (!feature_offered(their_features, feature_deps[i].depender)) + continue; + if (feature_offered(their_features, + feature_deps[i].must_also_have)) + continue; + *depender = feature_deps[i].depender; + *missing_dependency = feature_deps[i].must_also_have; + return false; + } + return true; +} + /** * all_supported_features - Check if we support what's being asked * diff --git a/common/features.h b/common/features.h index f036b798f..9ccd73cc3 100644 --- a/common/features.h +++ b/common/features.h @@ -50,6 +50,12 @@ bool feature_offered(const u8 *features, size_t f); bool feature_negotiated(const struct feature_set *our_features, const u8 *their_features, size_t f); +/* Features can depend on other features: both must be set! + * Sets @depender, @missing_dependency if returns false. + */ +bool feature_check_depends(const u8 *their_features, + size_t *depender, size_t *missing_dependency); + /* Return a list of what (init) features we advertize. */ const char **list_supported_features(const tal_t *ctx, const struct feature_set *fset); diff --git a/connectd/connectd.c b/connectd/connectd.c index d11ac452c..dff9f076a 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -424,6 +424,7 @@ struct io_plan *peer_connected(struct io_conn *conn, u8 *msg; struct per_peer_state *pps; int unsup; + size_t depender, missing; if (node_set_get(&daemon->peers, id)) return peer_reconnected(conn, daemon, id, addr, cs, @@ -451,6 +452,14 @@ struct io_plan *peer_connected(struct io_conn *conn, return io_write(conn, msg, tal_count(msg), io_close_cb, NULL); } + if (!feature_check_depends(their_features, &depender, &missing)) { + msg = towire_errorfmt(NULL, NULL, + "Feature %zu requires feature %zu", + depender, missing); + 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);