lightningd: make setchannelfee handle multiple channels per peer.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2022-03-23 09:29:18 +10:30
parent b3438e9bba
commit cb5dc48cab
4 changed files with 56 additions and 51 deletions

View File

@@ -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* 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 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 "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 *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 any routed payment: if omitted, it is unchanged. It can be a whole number, or a whole

View File

@@ -12,12 +12,14 @@ DESCRIPTION
The **setchannelfee** RPC command sets channel specific routing fees as The **setchannelfee** RPC command sets channel specific routing fees as
defined in BOLT \#7. The channel has to be in normal or awaiting state. defined in BOLT \#7. The channel has to be in normal or awaiting state.
This can be checked by **listpeers** reporting a *state* of 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* 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 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 "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 *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 any routed payment. If the parameter is left out, the global config

View File

@@ -2145,35 +2145,46 @@ static struct command_result *param_channel_or_all(struct command *cmd,
const char *name, const char *name,
const char *buffer, const char *buffer,
const jsmntok_t *tok, const jsmntok_t *tok,
struct channel **channel) struct channel ***channels)
{ {
struct command_result *res; struct command_result *res;
struct peer *peer; struct peer *peer;
/* early return the easy case */ /* early return the easy case */
if (json_tok_streq(buffer, tok, "all")) { if (json_tok_streq(buffer, tok, "all")) {
*channel = NULL; *channels = NULL;
return NULL; return NULL;
} }
/* Find channel by peer_id */ /* Find channels by peer_id */
peer = peer_from_json(cmd->ld, buffer, tok); peer = peer_from_json(cmd->ld, buffer, tok);
if (peer) { if (peer) {
*channel = peer_active_channel(peer); struct channel *channel;
if (!*channel) *channels = tal_arr(cmd, struct channel *, 0);
return command_fail(cmd, LIGHTNINGD, list_for_each(&peer->channels, channel, list) {
"Could not find active channel of peer with that id"); if (channel->state != CHANNELD_NORMAL
return NULL; && 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 */ /* Find channel by id or scid */
} else { } else {
res = command_find_channel(cmd, buffer, tok, channel); struct channel *channel;
res = command_find_channel(cmd, buffer, tok, &channel);
if (res) if (res)
return res; return res;
/* check channel is found and in valid state */ /* check channel is found and in valid state */
if (!*channel) if (!channel)
return command_fail(cmd, LIGHTNINGD, return command_fail(cmd, LIGHTNINGD,
"Could not find channel with that id"); "Could not find channel with that id");
*channels = tal_arr(cmd, struct channel *, 1);
(*channels)[0] = channel;
return NULL; return NULL;
} }
} }
@@ -2304,12 +2315,12 @@ static struct command_result *json_setchannelfee(struct command *cmd,
{ {
struct json_stream *response; struct json_stream *response;
struct peer *peer; struct peer *peer;
struct channel *channel; struct channel **channels;
u32 *base, *ppm, *delaysecs; u32 *base, *ppm, *delaysecs;
/* Parse the JSON command */ /* Parse the JSON command */
if (!param(cmd, buffer, params, 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, p_opt_def("base", param_msat_u32,
&base, cmd->ld->config.fee_base), &base, cmd->ld->config.fee_base),
p_opt_def("ppm", param_number, &ppm, p_opt_def("ppm", param_number, &ppm,
@@ -2318,13 +2329,6 @@ static struct command_result *json_setchannelfee(struct command *cmd,
NULL)) NULL))
return command_param_failed(); 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 */ /* Open JSON response object for later iteration */
response = json_stream_success(cmd); response = json_stream_success(cmd);
json_add_num(response, "base", *base); json_add_num(response, "base", *base);
@@ -2332,8 +2336,9 @@ static struct command_result *json_setchannelfee(struct command *cmd,
json_array_start(response, "channels"); json_array_start(response, "channels");
/* If the users requested 'all' channels we need to iterate */ /* If the users requested 'all' channels we need to iterate */
if (channel == NULL) { if (channels == NULL) {
list_for_each(&cmd->ld->peers, peer, list) { list_for_each(&cmd->ld->peers, peer, list) {
struct channel *channel;
list_for_each(&peer->channels, channel, list) { list_for_each(&peer->channels, channel, list) {
if (channel->state != CHANNELD_NORMAL && if (channel->state != CHANNELD_NORMAL &&
channel->state != CHANNELD_AWAITING_LOCKIN && channel->state != CHANNELD_AWAITING_LOCKIN &&
@@ -2343,10 +2348,12 @@ static struct command_result *json_setchannelfee(struct command *cmd,
*delaysecs, response, false); *delaysecs, response, false);
} }
} }
/* single channel should be updated */ /* single peer should be updated */
} else { } else {
set_channel_config(cmd, channel, base, ppm, NULL, NULL, for (size_t i = 0; i < tal_count(channels); i++) {
*delaysecs, response, false); set_channel_config(cmd, channels[i], base, ppm, NULL, NULL,
*delaysecs, response, false);
}
} }
/* Close and return response */ /* Close and return response */
@@ -2376,13 +2383,13 @@ static struct command_result *json_setchannel(struct command *cmd,
{ {
struct json_stream *response; struct json_stream *response;
struct peer *peer; struct peer *peer;
struct channel *channel; struct channel **channels;
u32 *base, *ppm, *delaysecs; u32 *base, *ppm, *delaysecs;
struct amount_msat *htlc_min, *htlc_max; struct amount_msat *htlc_min, *htlc_max;
/* Parse the JSON command */ /* Parse the JSON command */
if (!param(cmd, buffer, params, 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("feebase", param_msat_u32, &base),
p_opt("feeppm", param_number, &ppm), p_opt("feeppm", param_number, &ppm),
p_opt("htlcmin", param_msat, &htlc_min), 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"); "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 */ /* Open JSON response object for later iteration */
response = json_stream_success(cmd); response = json_stream_success(cmd);
json_array_start(response, "channels"); json_array_start(response, "channels");
/* If the users requested 'all' channels we need to iterate */ /* If the users requested 'all' channels we need to iterate */
if (channel == NULL) { if (channels == NULL) {
list_for_each(&cmd->ld->peers, peer, list) { list_for_each(&cmd->ld->peers, peer, list) {
channel = peer_active_channel(peer); struct channel *channel;
if (!channel) list_for_each(&peer->channels, channel, list) {
continue; if (channel->state != CHANNELD_NORMAL &&
if (channel->state != CHANNELD_NORMAL && channel->state != CHANNELD_AWAITING_LOCKIN &&
channel->state != CHANNELD_AWAITING_LOCKIN && channel->state != DUALOPEND_AWAITING_LOCKIN)
channel->state != DUALOPEND_AWAITING_LOCKIN) continue;
continue; set_channel_config(cmd, channel, base, ppm,
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, htlc_min, htlc_max,
*delaysecs, response, true); *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 */ /* Close and return response */

View File

@@ -2001,7 +2001,7 @@ def test_setchannel_usage(node_factory, bitcoind):
assert(db_fees[0]['feerate_ppm'] == 143) assert(db_fees[0]['feerate_ppm'] == 143)
# check if invalid scid raises proper error # 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) 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'): 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) result = l1.rpc.setchannel('f42' + scid[3:], 42, 43)