lightningd: new setchannel command.

Based on setchannelfee, but expanded to allow setting max htlc amount (and others
in future?).

The main differences:
1. It doesn't change values which are not specified (that would be hard to
   add fields to!)
2. It says exactly what all values are in any potentially changed channels.

Changelog-Added: JSON-RPC: new `setchannel` command generalizes `setchannelfee`.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2022-03-21 11:28:28 +10:30
parent 4fdcee9a11
commit 66e264d6b3
6 changed files with 274 additions and 14 deletions

View File

@ -1176,6 +1176,31 @@ class LightningRpc(UnixDomainSocketRpc):
}
return self.call("setchannelfee", payload)
def setchannel(self, id, feebase=None, feeppm=None, htlcmax=None, enforcedelay=None):
"""Set configuration a channel/peer {id} (or 'all').
{feebase} is a value in millisatoshi that is added as base fee
to any routed payment.
{feeppm} is a value added proportionally per-millionths to any
routed payment volume in satoshi.
{htlcmax} is the maximum (outgoing) htlc amount to allow and
advertize.
{enforcedelay} is the number of seconds before enforcing this
change.
"""
payload = {
"id": id,
"feebase": feebase,
"feeppm": feeppm,
"htlcmax": htlcmax,
"enforcedelay": enforcedelay,
}
return self.call("setchannel", payload)
def stop(self):
"""
Shut down the lightningd process.

View File

@ -65,6 +65,7 @@ MANPAGES := doc/lightning-cli.1 \
doc/lightning-sendonion.7 \
doc/lightning-sendonionmessage.7 \
doc/lightning-sendpay.7 \
doc/lightning-setchannel.7 \
doc/lightning-setchannelfee.7 \
doc/lightning-sendcustommsg.7 \
doc/lightning-signmessage.7 \

View File

@ -97,6 +97,7 @@ c-lightning Documentation
lightning-sendonionmessage <lightning-sendonionmessage.7.md>
lightning-sendpay <lightning-sendpay.7.md>
lightning-sendpsbt <lightning-sendpsbt.7.md>
lightning-setchannel <lightning-setchannel.7.md>
lightning-setchannelfee <lightning-setchannelfee.7.md>
lightning-signmessage <lightning-signmessage.7.md>
lightning-signpsbt <lightning-signpsbt.7.md>

View File

@ -0,0 +1,89 @@
lightning-setchannel -- Command for configuring fees / maximum htlc on a lightning channel
===========================================================================================
SYNOPSIS
--------
**setchannel** *id* [*feebase*] [*feeppm*] [*htlcmax*] [*enforcedelay*]
DESCRIPTION
-----------
The **setchannel** RPC command sets channel specific routing fees, and
`htlc_maximum_msat` 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.
*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.
*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
number ending in *msat* or *sat*, or a number with three decimal places
ending in *sat*, or a number with 1 to 11 decimal places ending in
*btc*.
*feeppm* is an optional value that is added proportionally per-millionths
to any routed payment volume in satoshi. For example, if ppm is 1,000
and 1,000,000 satoshi is being routed through the channel, an
proportional fee of 1,000 satoshi is added, resulting in a 0.1% fee.
*htlcmax* is an optional value that limits how large an HTLC we will
send: if omitted, it is unchanged (the default is no effective
limit). It can be a whole number, or a whole number ending in *msat*
or *sat*, or a number with three decimal places ending in *sat*, or a
number with 1 to 11 decimal places ending in *btc*.
*enforcedelay* is the number of seconds to delay before enforcing the
new fees/htlc max (default 600, which is ten minutes). This gives the
network a chance to catch up with the new rates and avoids rejecting
HTLCs before they do. This only has an effect if rates are increased
(we always allow users to overpay fees) or *htlcmax* is decreased, and
only applied to a single rate increase per channel (we don't remember
an arbitrary number of prior feerates) and if the node is restarted
the updated configuration is enforced immediately.
RETURN VALUE
------------
[comment]: # (GENERATE-FROM-SCHEMA-START)
On success, an object containing **channels** is returned. It is an array of objects, where each object contains:
- **peer_id** (pubkey): The node_id of the peer
- **channel_id** (hex): The channel_id of the channel (always 64 characters)
- **fee_base_msat** (msat): The resulting feebase (this is the BOLT #7 name)
- **fee_proportional_millionths** (u32): The resulting feeppm (this is the BOLT #7 name)
- **maximum_htlc_out_msat** (msat): The resulting htlcmax we will advertize (the BOLT #7 name is htlc_maximum_msat)
- **short_channel_id** (short_channel_id, optional): the short_channel_id (if locked in)
[comment]: # (GENERATE-FROM-SCHEMA-END)
ERRORS
------
The following error codes may occur:
- -1: Channel is in incorrect state, i.e. Catchall nonspecific error.
- -32602: JSONRPC2\_INVALID\_PARAMS, i.e. Given id is not a channel ID
or short channel ID.
AUTHOR
------
Michael Schmoock <<michael@schmoock.net>> is the author of this
feature. Rusty Russell <<rusty@rustcorp.com.au>> is mainly
responsible for the c-lightning project.
SEE ALSO
--------
lightningd-config(5), lightning-fundchannel(7),
lightning-listchannels(7), lightning-listpeers(7)
RESOURCES
---------
Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:25c6733af784e8a21a8eed4bcb0f12767ae49d16fe623187ae5313b5bb5cdd80)

View File

@ -0,0 +1,53 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"additionalProperties": false,
"required": [
"channels"
],
"properties": {
"channels": {
"type": "array",
"description": "channel(s) set, and their resulting configuration",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"peer_id",
"channel_id",
"fee_base_msat",
"fee_proportional_millionths",
"maximum_htlc_out_msat"
],
"properties": {
"peer_id": {
"type": "pubkey",
"description": "The node_id of the peer"
},
"channel_id": {
"type": "hex",
"description": "The channel_id of the channel",
"minLength": 64,
"maxLength": 64
},
"short_channel_id": {
"type": "short_channel_id",
"description": "the short_channel_id (if locked in)"
},
"fee_base_msat": {
"type": "msat",
"description": "The resulting feebase (this is the BOLT #7 name)"
},
"fee_proportional_millionths": {
"type": "u32",
"description": "The resulting feeppm (this is the BOLT #7 name)"
},
"maximum_htlc_out_msat": {
"type": "msat",
"description": "The resulting htlcmax we will advertize (the BOLT #7 name is htlc_maximum_msat)"
}
}
}
}
}
}

View File

@ -2007,13 +2007,20 @@ static struct command_result *param_msat_u32(struct command *cmd,
return NULL;
}
static void set_channel_fees(struct command *cmd, struct channel *channel,
u32 base, u32 ppm, u32 delaysecs,
struct json_stream *response)
static void set_channel_config(struct command *cmd, struct channel *channel,
u32 *base,
u32 *ppm,
struct amount_msat *htlc_max,
u32 delaysecs,
struct json_stream *response,
bool add_details)
{
/* We only need to defer values if we *increase* them; we always
* allow users to overpay fees. */
if (base > channel->feerate_base || ppm > channel->feerate_ppm) {
/* We only need to defer values if we *increase* fees (or drop
* max); we always allow users to overpay fees. */
if ((base && *base > channel->feerate_base)
|| (ppm && *ppm > channel->feerate_ppm)
|| (htlc_max
&& amount_msat_less(*htlc_max, channel->htlc_maximum_msat))) {
channel->old_feerate_timeout
= timeabs_add(time_now(), time_from_sec(delaysecs));
channel->old_feerate_base = channel->feerate_base;
@ -2022,14 +2029,18 @@ static void set_channel_fees(struct command *cmd, struct channel *channel,
}
/* set new values */
channel->feerate_base = base;
channel->feerate_ppm = ppm;
if (base)
channel->feerate_base = *base;
if (ppm)
channel->feerate_ppm = *ppm;
if (htlc_max)
channel->htlc_maximum_msat = *htlc_max;
/* tell channeld to make a send_channel_update */
if (channel->owner && streq(channel->owner->name, "channeld"))
subd_send_msg(channel->owner,
take(towire_channeld_config_channel(NULL, &base, &ppm,
NULL)));
take(towire_channeld_config_channel(NULL, base, ppm,
htlc_max)));
/* save values to database */
wallet_channel_save(cmd->ld->wallet, channel);
@ -2041,6 +2052,17 @@ static void set_channel_fees(struct command *cmd, struct channel *channel,
type_to_string(tmpctx, struct channel_id, &channel->cid));
if (channel->scid)
json_add_short_channel_id(response, "short_channel_id", channel->scid);
/* setchannel lists these explicitly */
if (add_details) {
json_add_amount_msat_only(response, "fee_base_msat",
amount_msat(channel->feerate_base));
json_add_u32(response, "fee_proportional_millionths",
channel->feerate_ppm);
json_add_amount_msat_only(response,
"maximum_htlc_out_msat",
channel->htlc_maximum_msat);
}
json_object_end(response);
}
@ -2088,14 +2110,14 @@ static struct command_result *json_setchannelfee(struct command *cmd,
channel->state != CHANNELD_AWAITING_LOCKIN &&
channel->state != DUALOPEND_AWAITING_LOCKIN)
continue;
set_channel_fees(cmd, channel, *base, *ppm, *delaysecs,
response);
set_channel_config(cmd, channel, base, ppm, NULL,
*delaysecs, response, false);
}
/* single channel should be updated */
} else {
set_channel_fees(cmd, channel, *base, *ppm, *delaysecs,
response);
set_channel_config(cmd, channel, base, ppm, NULL,
*delaysecs, response, false);
}
/* Close and return response */
@ -2117,6 +2139,75 @@ static const struct json_command setchannelfee_command = {
};
AUTODATA(json_command, &setchannelfee_command);
static struct command_result *json_setchannel(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct json_stream *response;
struct peer *peer;
struct channel *channel;
u32 *base, *ppm, *delaysecs;
struct amount_msat *htlc_max;
/* Parse the JSON command */
if (!param(cmd, buffer, params,
p_req("id", param_channel_or_all, &channel),
p_opt("feebase", param_msat_u32, &base),
p_opt("feeppm", param_number, &ppm),
p_opt("htlcmax", param_msat, &htlc_max),
p_opt_def("enforcedelay", param_number, &delaysecs, 600),
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_array_start(response, "channels");
/* If the users requested 'all' channels we need to iterate */
if (channel == 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, htlc_max,
*delaysecs, response, true);
}
/* single channel should be updated */
} else {
set_channel_config(cmd, channel, base, ppm, htlc_max,
*delaysecs, response, true);
}
/* Close and return response */
json_array_end(response);
return command_success(cmd, response);
}
static const struct json_command setchannel_command = {
"setchannel",
"channels",
json_setchannel,
"Sets fees and/or htlc_max for channel with {id} "
"(either peer ID, channel ID, short channel ID or 'all'). "
"If {feebase}, {feeppm} or {htlcmax} is missing, it is unchanged."
"{base} can also be defined in other units, for example '1sat'. "
"If {id} is 'all', the fees will be applied for all channels. "
};
AUTODATA(json_command, &setchannel_command);
#if DEVELOPER
static struct command_result *json_sign_last_tx(struct command *cmd,
const char *buffer,