From cb5dc48cab222cadf426d72e430ee17e9eada733 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 09:29:18 +1030 Subject: [PATCH] lightningd: make setchannelfee handle multiple channels per peer. Signed-off-by: Rusty Russell --- doc/lightning-setchannel.7.md | 4 +- doc/lightning-setchannelfee.7.md | 6 +- lightningd/peer_control.c | 95 ++++++++++++++++---------------- tests/test_pay.py | 2 +- 4 files changed, 56 insertions(+), 51 deletions(-) diff --git a/doc/lightning-setchannel.7.md b/doc/lightning-setchannel.7.md index 65755634c..3f0260f0a 100644 --- a/doc/lightning-setchannel.7.md +++ b/doc/lightning-setchannel.7.md @@ -22,7 +22,9 @@ will accept: we allow 2 a day, with a few extra occasionally). *id* is required and should contain a scid (short channel ID), channel id or peerid (pubkey) of the channel to be modified. If *id* is set to "all", the updates are applied to all channels in states -CHANNELD\_NORMAL or CHANNELD\_AWAITING\_LOCKIN. +CHANNELD\_NORMAL CHANNELD\_AWAITING\_LOCKIN or DUALOPEND_AWAITING_LOCKIN. +If *id* is a peerid, all channels with the +peer in those states are +changed. *feebase* is an optional value in millisatoshi that is added as base fee to any routed payment: if omitted, it is unchanged. It can be a whole number, or a whole diff --git a/doc/lightning-setchannelfee.7.md b/doc/lightning-setchannelfee.7.md index b0f97ca3e..2db2f2dbc 100644 --- a/doc/lightning-setchannelfee.7.md +++ b/doc/lightning-setchannelfee.7.md @@ -12,12 +12,14 @@ DESCRIPTION The **setchannelfee** RPC command sets channel specific routing fees as defined in BOLT \#7. The channel has to be in normal or awaiting state. This can be checked by **listpeers** reporting a *state* of -CHANNELD\_NORMAL or CHANNELD\_AWAITING\_LOCKIN for the channel. +CHANNELD\_NORMAL, CHANNELD\_AWAITING\_LOCKIN or DUALOPEND_AWAITING_LOCKIN for the channel. *id* is required and should contain a scid (short channel ID), channel id or peerid (pubkey) of the channel to be modified. If *id* is set to "all", the fees for all channels are updated that are in state -CHANNELD\_NORMAL or CHANNELD\_AWAITING\_LOCKIN. +CHANNELD\_NORMAL, CHANNELD\_AWAITING\_LOCKIN or +DUALOPEND_AWAITING_LOCKIN. If *id* is a peerid, all channels with the +peer in those states are changed. *base* is an optional value in millisatoshi that is added as base fee to any routed payment. If the parameter is left out, the global config diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 8268993c4..3deece449 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -2145,35 +2145,46 @@ static struct command_result *param_channel_or_all(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, - struct channel **channel) + struct channel ***channels) { struct command_result *res; struct peer *peer; /* early return the easy case */ if (json_tok_streq(buffer, tok, "all")) { - *channel = NULL; + *channels = NULL; return NULL; } - /* Find channel by peer_id */ + /* Find channels by peer_id */ peer = peer_from_json(cmd->ld, buffer, tok); if (peer) { - *channel = peer_active_channel(peer); - if (!*channel) - return command_fail(cmd, LIGHTNINGD, - "Could not find active channel of peer with that id"); - return NULL; + struct channel *channel; + *channels = tal_arr(cmd, struct channel *, 0); + list_for_each(&peer->channels, channel, list) { + if (channel->state != CHANNELD_NORMAL + && channel->state != CHANNELD_AWAITING_LOCKIN + && channel->state != DUALOPEND_AWAITING_LOCKIN) + continue; + tal_arr_expand(channels, channel); + } + if (tal_count(*channels) == 0) + return command_fail(cmd, LIGHTNINGD, + "Could not find any active channels of peer with that id"); + return NULL; /* Find channel by id or scid */ } else { - res = command_find_channel(cmd, buffer, tok, channel); + struct channel *channel; + res = command_find_channel(cmd, buffer, tok, &channel); if (res) return res; /* check channel is found and in valid state */ - if (!*channel) + if (!channel) return command_fail(cmd, LIGHTNINGD, "Could not find channel with that id"); + *channels = tal_arr(cmd, struct channel *, 1); + (*channels)[0] = channel; return NULL; } } @@ -2304,12 +2315,12 @@ static struct command_result *json_setchannelfee(struct command *cmd, { struct json_stream *response; struct peer *peer; - struct channel *channel; + struct channel **channels; u32 *base, *ppm, *delaysecs; /* Parse the JSON command */ if (!param(cmd, buffer, params, - p_req("id", param_channel_or_all, &channel), + p_req("id", param_channel_or_all, &channels), p_opt_def("base", param_msat_u32, &base, cmd->ld->config.fee_base), p_opt_def("ppm", param_number, &ppm, @@ -2318,13 +2329,6 @@ static struct command_result *json_setchannelfee(struct command *cmd, NULL)) return command_param_failed(); - if (channel - && channel->state != CHANNELD_NORMAL - && channel->state != CHANNELD_AWAITING_LOCKIN - && channel->state != DUALOPEND_AWAITING_LOCKIN) - return command_fail(cmd, LIGHTNINGD, - "Channel is in state %s", channel_state_name(channel)); - /* Open JSON response object for later iteration */ response = json_stream_success(cmd); json_add_num(response, "base", *base); @@ -2332,8 +2336,9 @@ static struct command_result *json_setchannelfee(struct command *cmd, json_array_start(response, "channels"); /* If the users requested 'all' channels we need to iterate */ - if (channel == NULL) { + if (channels == NULL) { list_for_each(&cmd->ld->peers, peer, list) { + struct channel *channel; list_for_each(&peer->channels, channel, list) { if (channel->state != CHANNELD_NORMAL && channel->state != CHANNELD_AWAITING_LOCKIN && @@ -2343,10 +2348,12 @@ static struct command_result *json_setchannelfee(struct command *cmd, *delaysecs, response, false); } } - /* single channel should be updated */ + /* single peer should be updated */ } else { - set_channel_config(cmd, channel, base, ppm, NULL, NULL, - *delaysecs, response, false); + for (size_t i = 0; i < tal_count(channels); i++) { + set_channel_config(cmd, channels[i], base, ppm, NULL, NULL, + *delaysecs, response, false); + } } /* Close and return response */ @@ -2376,13 +2383,13 @@ static struct command_result *json_setchannel(struct command *cmd, { struct json_stream *response; struct peer *peer; - struct channel *channel; + struct channel **channels; u32 *base, *ppm, *delaysecs; struct amount_msat *htlc_min, *htlc_max; /* Parse the JSON command */ if (!param(cmd, buffer, params, - p_req("id", param_channel_or_all, &channel), + p_req("id", param_channel_or_all, &channels), p_opt("feebase", param_msat_u32, &base), p_opt("feeppm", param_number, &ppm), p_opt("htlcmin", param_msat, &htlc_min), @@ -2398,37 +2405,31 @@ static struct command_result *json_setchannel(struct command *cmd, "htlcmax cannot be less than htlcmin"); } - if (channel - && channel->state != CHANNELD_NORMAL - && channel->state != CHANNELD_AWAITING_LOCKIN - && channel->state != DUALOPEND_AWAITING_LOCKIN) - return command_fail(cmd, LIGHTNINGD, - "Channel is in state %s", channel_state_name(channel)); - /* Open JSON response object for later iteration */ response = json_stream_success(cmd); json_array_start(response, "channels"); /* If the users requested 'all' channels we need to iterate */ - if (channel == NULL) { + if (channels == NULL) { list_for_each(&cmd->ld->peers, peer, list) { - channel = peer_active_channel(peer); - if (!channel) - continue; - if (channel->state != CHANNELD_NORMAL && - channel->state != CHANNELD_AWAITING_LOCKIN && - channel->state != DUALOPEND_AWAITING_LOCKIN) - continue; - set_channel_config(cmd, channel, base, ppm, + struct channel *channel; + list_for_each(&peer->channels, channel, list) { + if (channel->state != CHANNELD_NORMAL && + channel->state != CHANNELD_AWAITING_LOCKIN && + channel->state != DUALOPEND_AWAITING_LOCKIN) + continue; + set_channel_config(cmd, channel, base, ppm, + htlc_min, htlc_max, + *delaysecs, response, true); + } + } + /* single peer should be updated */ + } else { + for (size_t i = 0; i < tal_count(channels); i++) { + set_channel_config(cmd, channels[i], base, ppm, htlc_min, htlc_max, *delaysecs, response, true); } - - /* single channel should be updated */ - } else { - set_channel_config(cmd, channel, base, ppm, - htlc_min, htlc_max, - *delaysecs, response, true); } /* Close and return response */ diff --git a/tests/test_pay.py b/tests/test_pay.py index 867fec63a..8942c6ccf 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -2001,7 +2001,7 @@ def test_setchannel_usage(node_factory, bitcoind): assert(db_fees[0]['feerate_ppm'] == 143) # check if invalid scid raises proper error - with pytest.raises(RpcError, match=r'-1.*Could not find active channel of peer with that id'): + with pytest.raises(RpcError, match=r'-1.*Could not find any active channels of peer with that id'): result = l1.rpc.setchannel(l3.info['id'], 42, 43) with pytest.raises(RpcError, match=r'-32602.*id: should be a channel ID or short channel ID: invalid token'): result = l1.rpc.setchannel('f42' + scid[3:], 42, 43)