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