diff --git a/lightningd/options.c b/lightningd/options.c index 2614a73ca..1b8f5a113 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -512,6 +512,27 @@ static char *opt_force_channel_secrets(const char *optarg, return NULL; } +static char *opt_force_featureset(const char *optarg, + struct lightningd *ld) +{ + char **parts = tal_strsplit(tmpctx, optarg, "/", STR_EMPTY_OK); + if (tal_count(parts) != NUM_FEATURE_PLACE) + return "Expected 5 feature sets (init, globalinit, node_announce, channel, bolt11) separated by /"; + for (size_t i = 0; parts[i]; i++) { + char **bits = tal_strsplit(tmpctx, parts[i], ",", STR_EMPTY_OK); + tal_resize(&ld->our_features->bits[i], 0); + + for (size_t j = 0; bits[j]; j++) { + char *endp; + long int n = strtol(bits[j], &endp, 10); + if (*endp || endp == bits[j]) + return "Invalid bitnumber"; + set_feature_bit(&ld->our_features->bits[i], n); + } + } + return NULL; +} + static void dev_register_opts(struct lightningd *ld) { /* We might want to debug plugins, which are started before normal @@ -570,6 +591,8 @@ static void dev_register_opts(struct lightningd *ld) opt_set_bool, &ld->plugins->dev_builtin_plugins_unimportant, "Make builtin plugins unimportant so you can plugin stop them."); + opt_register_arg("--dev-force-features", opt_force_featureset, NULL, ld, + "Force the init/globalinit/node_announce/channel/bolt11 features, each comma-separated bitnumbers"); } #endif /* DEVELOPER */ diff --git a/tests/test_connection.py b/tests/test_connection.py index 844138011..d6b35aa29 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -2349,3 +2349,17 @@ def test_channel_features(node_factory, bitcoind): # l2 should agree. assert only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['features'] == chan['features'] + + +@unittest.skipIf(not DEVELOPER, "need dev-force-features") +def test_nonstatic_channel(node_factory, bitcoind): + """Smoke test for a channel without option_static_remotekey""" + l1, l2 = node_factory.line_graph(2, + opts=[{}, + {'dev-force-features': '////'}]) + chan = only_one(only_one(l1.rpc.listpeers()['peers'])['channels']) + assert 'option_static_remotekey' not in chan['features'] + assert 'option_anchor_outputs' not in chan['features'] + + l1.pay(l2, 1000) + l1.rpc.close(l2.info['id'])