Browse Source

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 <rusty@rustcorp.com.au>
bump-pyln-proto
Rusty Russell 4 years ago
parent
commit
168009c105
  1. 36
      common/features.c
  2. 6
      common/features.h
  3. 9
      connectd/connectd.c

36
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
*

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

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

Loading…
Cancel
Save