Skip to content

Commit

Permalink
lightningd: make setchannelfee handle multiple channels per peer.
Browse files Browse the repository at this point in the history
Signed-off-by: Rusty Russell <[email protected]>
  • Loading branch information
rustyrussell committed Mar 23, 2022
1 parent b3438e9 commit cb5dc48
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 50 deletions.
4 changes: 3 additions & 1 deletion doc/lightning-setchannel.7.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions doc/lightning-setchannelfee.7.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
93 changes: 47 additions & 46 deletions lightningd/peer_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
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 active channel of peer with that id");
"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;
}
}
Expand Down Expand Up @@ -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,
Expand All @@ -2318,22 +2329,16 @@ 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);
json_add_num(response, "ppm", *ppm);
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 &&
Expand All @@ -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 */
Expand Down Expand Up @@ -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),
Expand All @@ -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 */
Expand Down
2 changes: 1 addition & 1 deletion tests/test_pay.py
Original file line number Diff line number Diff line change
Expand Up @@ -2001,7 +2001,7 @@ def channel_get_config(scid):
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)
Expand Down

0 comments on commit cb5dc48

Please sign in to comment.