setchannel: add minhtlc

Suggested by @m-schmook, I realized that if we append it later I'll
never get it right: I expect parameters min and max, not max and min!

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Added: Protocol: you can now alter the `htlc_minimum_msat` and `htlc_maximum_msat` your node advertizes.
This commit is contained in:
Rusty Russell 2022-03-21 11:28:54 +10:30
parent f29890ed66
commit 999c734bb5
20 changed files with 186 additions and 43 deletions

View file

@ -124,7 +124,9 @@ struct peer {
* the channel_updates. */
u32 fee_base;
u32 fee_per_satoshi;
struct amount_msat htlc_maximum_msat;
/* Note: the real min constraint is channel->config[REMOTE].htlc_minimum:
* they could kill the channel if we violate that! */
struct amount_msat htlc_minimum_msat, htlc_maximum_msat;
/* The scriptpubkey to use for shutting down. */
u32 *final_index;
@ -361,7 +363,7 @@ static void send_channel_update(struct peer *peer, int disable_flag)
disable_flag
== ROUTING_FLAGS_DISABLED,
peer->cltv_delta,
peer->channel->config[REMOTE].htlc_minimum,
peer->htlc_minimum_msat,
peer->fee_base,
peer->fee_per_satoshi,
peer->htlc_maximum_msat);
@ -3429,10 +3431,13 @@ static void handle_blockheight(struct peer *peer, const u8 *inmsg)
static void handle_config_channel(struct peer *peer, const u8 *inmsg)
{
u32 *base, *ppm;
struct amount_msat *htlc_max;
struct amount_msat *htlc_min, *htlc_max;
bool changed;
if (!fromwire_channeld_config_channel(inmsg, inmsg, &base, &ppm, &htlc_max))
if (!fromwire_channeld_config_channel(inmsg, inmsg,
&base, &ppm,
&htlc_min,
&htlc_max))
master_badmsg(WIRE_CHANNELD_CONFIG_CHANNEL, inmsg);
/* only send channel updates if values actually changed */
@ -3445,6 +3450,10 @@ static void handle_config_channel(struct peer *peer, const u8 *inmsg)
peer->fee_per_satoshi = *ppm;
changed = true;
}
if (htlc_min && !amount_msat_eq(*htlc_min, peer->htlc_minimum_msat)) {
peer->htlc_minimum_msat = *htlc_min;
changed = true;
}
if (htlc_max && !amount_msat_eq(*htlc_max, peer->htlc_maximum_msat)) {
peer->htlc_maximum_msat = *htlc_max;
changed = true;
@ -3755,6 +3764,7 @@ static void init_channel(struct peer *peer)
&opener,
&peer->fee_base,
&peer->fee_per_satoshi,
&peer->htlc_minimum_msat,
&peer->htlc_maximum_msat,
&local_msat,
&points[LOCAL],

View file

@ -35,6 +35,7 @@ msgdata,channeld_init,old_remote_per_commit,pubkey,
msgdata,channeld_init,opener,enum side,
msgdata,channeld_init,fee_base,u32,
msgdata,channeld_init,fee_proportional,u32,
msgdata,channeld_init,htlc_minimum_msat,amount_msat,
msgdata,channeld_init,htlc_maximum_msat,amount_msat,
msgdata,channeld_init,local_msatoshi,amount_msat,
msgdata,channeld_init,our_basepoints,basepoints,
@ -220,6 +221,7 @@ msgdata,channeld_fail_fallen_behind,remote_per_commitment_point,?pubkey,
msgtype,channeld_config_channel,1029
msgdata,channeld_config_channel,feerate_base,?u32,
msgdata,channeld_config_channel,feerate_ppm,?u32,
msgdata,channeld_config_channel,htlc_minimum,?amount_msat,
msgdata,channeld_config_channel,htlc_maximum,?amount_msat,
# When we receive announcement_signatures for channel announce

Can't render this file because it has a wrong number of fields in line 14.

View file

@ -1176,7 +1176,7 @@ class LightningRpc(UnixDomainSocketRpc):
}
return self.call("setchannelfee", payload)
def setchannel(self, id, feebase=None, feeppm=None, htlcmax=None, enforcedelay=None):
def setchannel(self, id, feebase=None, feeppm=None, htlcmin=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
@ -1185,6 +1185,9 @@ class LightningRpc(UnixDomainSocketRpc):
{feeppm} is a value added proportionally per-millionths to any
routed payment volume in satoshi.
{htlcmin} is the minimum (outgoing) htlc amount to allow and
advertize.
{htlcmax} is the maximum (outgoing) htlc amount to allow and
advertize.
@ -1196,6 +1199,7 @@ class LightningRpc(UnixDomainSocketRpc):
"id": id,
"feebase": feebase,
"feeppm": feeppm,
"htlcmin": htlcmin,
"htlcmax": htlcmax,
"enforcedelay": enforcedelay,
}

View file

@ -87,6 +87,7 @@ On success, an object containing **peers** is returned. It is an array of objec
- **spendable_msat** (msat, optional): total we could send through channel
- **receivable_msat** (msat, optional): total peer could send through channel
- **minimum_htlc_in_msat** (msat, optional): the minimum amount HTLC we accept
- **minimum_htlc_out_msat** (msat, optional): the minimum amount HTLC we will send
- **maximum_htlc_out_msat** (msat, optional): the maximum amount HTLC we will send
- **their_to_self_delay** (u32, optional): the number of blocks before they can take their funds if they unilateral close
- **our_to_self_delay** (u32, optional): the number of blocks before we can take our funds if we unilateral close
@ -379,4 +380,4 @@ Main web site: <https://github.com/ElementsProject/lightning> Lightning
RFC site (BOLT \#9):
<https://github.com/lightningnetwork/lightning-rfc/blob/master/09-features.md>
[comment]: # ( SHA256STAMP:8e30caf48aed46acc7c053a355867dc8b8624035dba4ea7668d30d86b8d827cd)
[comment]: # ( SHA256STAMP:147b7008c8f4acb031df625e0731614339a75ee5861cb9f40cd542b1017e3660)

View file

@ -1,20 +1,24 @@
lightning-setchannel -- Command for configuring fees / maximum htlc on a lightning channel
lightning-setchannel -- Command for configuring fees / htlc range advertized for a channel
===========================================================================================
SYNOPSIS
--------
**setchannel** *id* [*feebase*] [*feeppm*] [*htlcmax*] [*enforcedelay*]
**setchannel** *id* [*feebase*] [*feeppm*] [*htlcmin*] [*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
`htlc_minimum_msat` or `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.
These changes (for a public channel) will be broadcast to the rest of
the network (though many nodes limit the rate of such changes they
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
@ -31,6 +35,13 @@ 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.
*htlcmin* is an optional value that limits how small an HTLC we will
send: if omitted, it is unchanged (the default is no lower 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*. The peer also enforces a
minimum for the channel: setting it below will be ignored.
*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*
@ -55,8 +66,11 @@ On success, an object containing **channels** is returned. It is an array of ob
- **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)
- **minimum_htlc_out_msat** (msat): The resulting htlcmin we will advertize (the BOLT #7 name is htlc_minimum_msat)
- **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)
- the following warnings are possible:
- **warning_htlcmin_too_low**: The requested htlcmin was too low for this peer, so we set it to the minimum they will allow
[comment]: # (GENERATE-FROM-SCHEMA-END)
@ -86,4 +100,4 @@ RESOURCES
Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:25c6733af784e8a21a8eed4bcb0f12767ae49d16fe623187ae5313b5bb5cdd80)
[comment]: # ( SHA256STAMP:0f153e7dddce61bc921b3743472f11316c5984b9b1459cac1b201d6f51ec1be1)

View file

@ -418,6 +418,10 @@
"type": "msat",
"description": "the minimum amount HTLC we accept"
},
"minimum_htlc_out_msat": {
"type": "msat",
"description": "the minimum amount HTLC we will send"
},
"maximum_htlc_out_msat": {
"type": "msat",
"description": "the maximum amount HTLC we will send"
@ -776,6 +780,7 @@
"spendable_msat": {},
"receivable_msat": {},
"minimum_htlc_in_msat": {},
"minimum_htlc_out_msat": {},
"maximum_htlc_out_msat": {},
"spendable_msatoshi": {},
"receivable_msatoshi": {},
@ -864,6 +869,7 @@
"spendable_msat": {},
"receivable_msat": {},
"minimum_htlc_in_msat": {},
"minimum_htlc_out_msat": {},
"maximum_htlc_out_msat": {},
"spendable_msatoshi": {},
"receivable_msatoshi": {},
@ -953,6 +959,7 @@
"spendable_msat": {},
"receivable_msat": {},
"minimum_htlc_in_msat": {},
"minimum_htlc_out_msat": {},
"maximum_htlc_out_msat": {},
"spendable_msatoshi": {},
"receivable_msatoshi": {},
@ -1041,6 +1048,7 @@
"spendable_msat": {},
"receivable_msat": {},
"minimum_htlc_in_msat": {},
"minimum_htlc_out_msat": {},
"maximum_htlc_out_msat": {},
"spendable_msatoshi": {},
"receivable_msatoshi": {},

View file

@ -17,6 +17,7 @@
"channel_id",
"fee_base_msat",
"fee_proportional_millionths",
"minimum_htlc_out_msat",
"maximum_htlc_out_msat"
],
"properties": {
@ -42,6 +43,14 @@
"type": "u32",
"description": "The resulting feeppm (this is the BOLT #7 name)"
},
"minimum_htlc_out_msat": {
"type": "msat",
"description": "The resulting htlcmin we will advertize (the BOLT #7 name is htlc_minimum_msat)"
},
"warning_htlcmin_too_low": {
"type": "string",
"description": "The requested htlcmin was too low for this peer, so we set it to the minimum they will allow"
},
"maximum_htlc_out_msat": {
"type": "msat",
"description": "The resulting htlcmax we will advertize (the BOLT #7 name is htlc_maximum_msat)"

View file

@ -410,10 +410,11 @@ struct channel *new_channel(struct peer *peer, u64 dbid,
secp256k1_ecdsa_signature *lease_commit_sig STEALS,
u32 lease_chan_max_msat,
u16 lease_chan_max_ppt,
struct amount_msat htlc_minimum_msat,
struct amount_msat htlc_maximum_msat)
{
struct channel *channel = tal(peer->ld, struct channel);
struct amount_msat htlc_max;
struct amount_msat htlc_min, htlc_max;
assert(dbid != 0);
channel->peer = peer;
@ -509,8 +510,12 @@ struct channel *new_channel(struct peer *peer, u64 dbid,
channel->blockheight_states = dup_height_states(channel, height_states);
channel->channel_update = NULL;
/* DB migration, for example, sets this to bignum; correct
* here */
/* DB migration, for example, sets min to 0, max to large: fixup */
htlc_min = channel->channel_info.their_config.htlc_minimum;
if (amount_msat_greater(htlc_min, htlc_minimum_msat))
channel->htlc_minimum_msat = htlc_min;
else
channel->htlc_minimum_msat = htlc_minimum_msat;
htlc_max = htlc_max_possible_send(channel);
if (amount_msat_less(htlc_max, htlc_maximum_msat))
channel->htlc_maximum_msat = htlc_max;

View file

@ -196,8 +196,8 @@ struct channel {
* peer via option_data_loss_protect? */
const struct pubkey *future_per_commitment_point;
/* Max htlc amount allowed in channel. */
struct amount_msat htlc_maximum_msat;
/* Min/max htlc amount allowed in channel. */
struct amount_msat htlc_minimum_msat, htlc_maximum_msat;
/* Feerate per channel */
u32 feerate_base, feerate_ppm;
@ -205,7 +205,7 @@ struct channel {
/* But allow these feerates/htlcs up until this time. */
struct timeabs old_feerate_timeout;
u32 old_feerate_base, old_feerate_ppm;
struct amount_msat old_htlc_maximum_msat;
struct amount_msat old_htlc_minimum_msat, old_htlc_maximum_msat;
/* If they used option_upfront_shutdown_script. */
const u8 *remote_upfront_shutdown_script;
@ -315,6 +315,7 @@ struct channel *new_channel(struct peer *peer, u64 dbid,
secp256k1_ecdsa_signature *lease_commit_sig STEALS,
u32 lease_chan_max_msat,
u16 lease_chan_max_ppt,
struct amount_msat htlc_minimum_msat,
struct amount_msat htlc_maximum_msat);
/* new_inflight - Create a new channel_inflight for a channel */

View file

@ -702,6 +702,7 @@ void peer_start_channeld(struct channel *channel,
channel->opener,
channel->feerate_base,
channel->feerate_ppm,
channel->htlc_minimum_msat,
channel->htlc_maximum_msat,
channel->our_msat,
&channel->local_basepoints,

View file

@ -1110,6 +1110,7 @@ wallet_update_channel(struct lightningd *ld,
channel->msat_to_us_min = our_msat;
channel->msat_to_us_max = our_msat;
channel->lease_expiry = lease_expiry;
channel->htlc_minimum_msat = channel->channel_info.their_config.htlc_minimum;
channel->htlc_maximum_msat = htlc_max_possible_send(channel);
tal_free(channel->lease_commit_sig);
@ -1253,6 +1254,7 @@ wallet_commit_channel(struct lightningd *ld,
channel->lease_chan_max_msat = lease_chan_max_msat;
channel->lease_chan_max_ppt = lease_chan_max_ppt;
channel->htlc_minimum_msat = channel_info->their_config.htlc_minimum;
channel->htlc_maximum_msat = htlc_max_possible_send(channel);
/* Now we finally put it in the database. */

View file

@ -210,6 +210,7 @@ wallet_commit_channel(struct lightningd *ld,
take(new_height_states(NULL, uc->fc ? LOCAL : REMOTE,
&lease_start_blockheight)),
0, NULL, 0, 0, /* No leases on v1s */
AMOUNT_MSAT(0), /* No htlc_minimum_msat */
AMOUNT_MSAT(-1ULL)); /* No htlc_maximum_msat */
/* Now we finally put it in the database. */

View file

@ -861,6 +861,9 @@ static void json_add_channel(struct lightningd *ld,
channel->our_config.htlc_minimum,
"htlc_minimum_msat",
"minimum_htlc_in_msat");
json_add_amount_msat_only(response,
"minimum_htlc_out_msat",
channel->htlc_minimum_msat);
json_add_amount_msat_only(response,
"maximum_htlc_out_msat",
channel->htlc_maximum_msat);
@ -2010,21 +2013,27 @@ static struct command_result *param_msat_u32(struct command *cmd,
static void set_channel_config(struct command *cmd, struct channel *channel,
u32 *base,
u32 *ppm,
struct amount_msat *htlc_min,
struct amount_msat *htlc_max,
u32 delaysecs,
struct json_stream *response,
bool add_details)
{
bool warn_cannot_set_min = false;
/* We only need to defer values if we *increase* fees (or drop
* max); we always allow users to overpay fees. */
* max, increase min); we always allow users to overpay fees. */
if ((base && *base > channel->feerate_base)
|| (ppm && *ppm > channel->feerate_ppm)
|| (htlc_min
&& amount_msat_greater(*htlc_min, channel->htlc_minimum_msat))
|| (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;
channel->old_feerate_ppm = channel->feerate_ppm;
channel->old_htlc_minimum_msat = channel->htlc_minimum_msat;
channel->old_htlc_maximum_msat = channel->htlc_maximum_msat;
}
@ -2033,6 +2042,17 @@ static void set_channel_config(struct command *cmd, struct channel *channel,
channel->feerate_base = *base;
if (ppm)
channel->feerate_ppm = *ppm;
if (htlc_min) {
struct amount_msat actual_min;
/* We can't send something they'll refuse: check that here. */
actual_min = channel->channel_info.their_config.htlc_minimum;
if (amount_msat_less(*htlc_min, actual_min)) {
warn_cannot_set_min = true;
channel->htlc_minimum_msat = actual_min;
} else
channel->htlc_minimum_msat = *htlc_min;
}
if (htlc_max)
channel->htlc_maximum_msat = *htlc_max;
@ -2040,7 +2060,7 @@ static void set_channel_config(struct command *cmd, struct channel *channel,
if (channel->owner && streq(channel->owner->name, "channeld"))
subd_send_msg(channel->owner,
take(towire_channeld_config_channel(NULL, base, ppm,
htlc_max)));
htlc_min, htlc_max)));
/* save values to database */
wallet_channel_save(cmd->ld->wallet, channel);
@ -2059,6 +2079,12 @@ static void set_channel_config(struct command *cmd, struct channel *channel,
amount_msat(channel->feerate_base));
json_add_u32(response, "fee_proportional_millionths",
channel->feerate_ppm);
json_add_amount_msat_only(response,
"minimum_htlc_out_msat",
channel->htlc_minimum_msat);
if (warn_cannot_set_min)
json_add_string(response, "warning_htlcmin_too_low",
"Set minimum_htlc_out_msat to minimum allowed by peer");
json_add_amount_msat_only(response,
"maximum_htlc_out_msat",
channel->htlc_maximum_msat);
@ -2110,13 +2136,13 @@ static struct command_result *json_setchannelfee(struct command *cmd,
channel->state != CHANNELD_AWAITING_LOCKIN &&
channel->state != DUALOPEND_AWAITING_LOCKIN)
continue;
set_channel_config(cmd, channel, base, ppm, NULL,
set_channel_config(cmd, channel, base, ppm, NULL, NULL,
*delaysecs, response, false);
}
/* single channel should be updated */
} else {
set_channel_config(cmd, channel, base, ppm, NULL,
set_channel_config(cmd, channel, base, ppm, NULL, NULL,
*delaysecs, response, false);
}
@ -2149,13 +2175,14 @@ static struct command_result *json_setchannel(struct command *cmd,
struct peer *peer;
struct channel *channel;
u32 *base, *ppm, *delaysecs;
struct amount_msat *htlc_max;
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_opt("feebase", param_msat_u32, &base),
p_opt("feeppm", param_number, &ppm),
p_opt("htlcmin", param_msat, &htlc_min),
p_opt("htlcmax", param_msat, &htlc_max),
p_opt_def("enforcedelay", param_number, &delaysecs, 600),
NULL))
@ -2182,13 +2209,15 @@ static struct command_result *json_setchannel(struct command *cmd,
channel->state != CHANNELD_AWAITING_LOCKIN &&
channel->state != DUALOPEND_AWAITING_LOCKIN)
continue;
set_channel_config(cmd, channel, base, ppm, htlc_max,
set_channel_config(cmd, channel, base, ppm,
htlc_min, htlc_max,
*delaysecs, response, true);
}
/* single channel should be updated */
} else {
set_channel_config(cmd, channel, base, ppm, htlc_max,
set_channel_config(cmd, channel, base, ppm,
htlc_min, htlc_max,
*delaysecs, response, true);
}

View file

@ -656,16 +656,18 @@ static void forward_htlc(struct htlc_in *hin,
"Allowing payment using older feerate");
}
if (amount_msat_greater(amt_to_forward, next->htlc_maximum_msat)) {
/* Are we in old-max grace-period? */
if (amount_msat_greater(amt_to_forward, next->htlc_maximum_msat)
|| amount_msat_less(amt_to_forward, next->htlc_minimum_msat)) {
/* Are we in old-range grace-period? */
if (!time_before(time_now(), next->old_feerate_timeout)
|| amount_msat_less(amt_to_forward, next->old_htlc_minimum_msat)
|| amount_msat_greater(amt_to_forward, next->old_htlc_maximum_msat)) {
failmsg = towire_temporary_channel_failure(tmpctx,
get_channel_update(next));
goto fail;
}
log_info(hin->key.channel->log,
"Allowing large htlc using older htlc_maximum_msat");
"Allowing htlc using older htlc_minimum/maximum_msat");
}
if (!check_cltv(hin, cltv_expiry, outgoing_cltv_value,

View file

@ -114,6 +114,8 @@ routehint_candidates(const tal_t *ctx,
continue;
}
/* FIXME: we don't actually check htlc_minimum_msat! */
/* If they set an htlc_maximum_msat, consider that the
* capacity ceiling. We *could* do multiple HTLCs,
* but presumably that would defeat the spirit of the

View file

@ -626,7 +626,7 @@ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED)
void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED)
{ fprintf(stderr, "towire_channel_id called!\n"); abort(); }
/* Generated stub for towire_channeld_config_channel */
u8 *towire_channeld_config_channel(const tal_t *ctx UNNEEDED, u32 *feerate_base UNNEEDED, u32 *feerate_ppm UNNEEDED, struct amount_msat *htlc_maximum UNNEEDED)
u8 *towire_channeld_config_channel(const tal_t *ctx UNNEEDED, u32 *feerate_base UNNEEDED, u32 *feerate_ppm UNNEEDED, struct amount_msat *htlc_minimum UNNEEDED, struct amount_msat *htlc_maximum UNNEEDED)
{ fprintf(stderr, "towire_channeld_config_channel called!\n"); abort(); }
/* Generated stub for towire_channeld_dev_memleak */
u8 *towire_channeld_dev_memleak(const tal_t *ctx UNNEEDED)

View file

@ -1882,7 +1882,7 @@ def test_setchannel_usage(node_factory, bitcoind):
def channel_get_config(scid):
return l1.db.query(
'SELECT feerate_base, feerate_ppm, htlc_maximum_msat FROM channels '
'SELECT feerate_base, feerate_ppm, htlc_minimum_msat, htlc_maximum_msat FROM channels '
'WHERE short_channel_id=\'{}\';'.format(scid))
# get short channel id
@ -1900,8 +1900,8 @@ def test_setchannel_usage(node_factory, bitcoind):
assert peers[0]['channels'][0]['fee_proportional_millionths'] == DEF_PPM
assert peers[0]['channels'][0]['maximum_htlc_out_msat'] == MAX_HTLC
# custom setchannel scid <feebase> <feeppm> <htlcmax>
result = l1.rpc.setchannel(scid, 1337, 137, 133337)
# custom setchannel scid <feebase> <feeppm> <htlcmin> <htlcmax>
result = l1.rpc.setchannel(scid, 1337, 137, 17, 133337)
# check result format
assert(len(result['channels']) == 1)
@ -1910,22 +1910,26 @@ def test_setchannel_usage(node_factory, bitcoind):
assert(result['channels'][0]['short_channel_id'] == scid)
assert(result['channels'][0]['fee_base_msat'] == 1337)
assert(result['channels'][0]['fee_proportional_millionths'] == 137)
assert(result['channels'][0]['minimum_htlc_out_msat'] == 17)
assert(result['channels'][0]['maximum_htlc_out_msat'] == 133337)
# check if custom values made it into the database
db_fees = channel_get_config(scid)
assert(db_fees[0]['feerate_base'] == 1337)
assert(db_fees[0]['feerate_ppm'] == 137)
assert(db_fees[0]['htlc_minimum_msat'] == 17)
assert(db_fees[0]['htlc_maximum_msat'] == 133337)
# also check for updated values in `listpeers`
peers = l1.rpc.listpeers()['peers']
assert peers[0]['channels'][0]['fee_base_msat'] == Millisatoshi(1337)
assert peers[0]['channels'][0]['fee_proportional_millionths'] == 137
assert peers[0]['channels'][0]['minimum_htlc_out_msat'] == 17
assert peers[0]['channels'][0]['maximum_htlc_out_msat'] == 133337
# wait for gossip and check if l1 sees new fees in listchannels
wait_for(lambda: [c['base_fee_millisatoshi'] for c in l1.rpc.listchannels(scid)['channels']] == [DEF_BASE, 1337])
wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid)['channels']] == [DEF_PPM, 137])
wait_for(lambda: [c['htlc_minimum_msat'] for c in l1.rpc.listchannels(scid)['channels']] == [0, 17])
wait_for(lambda: [c['htlc_maximum_msat'] for c in l1.rpc.listchannels(scid)['channels']] == [MAX_HTLC, 133337])
# also test with named and missing parameters
@ -1935,6 +1939,7 @@ def test_setchannel_usage(node_factory, bitcoind):
assert(result['channels'][0]['short_channel_id'] == scid)
assert(result['channels'][0]['fee_base_msat'] == 1337)
assert(result['channels'][0]['fee_proportional_millionths'] == 42)
assert result['channels'][0]['minimum_htlc_out_msat'] == 17
assert(result['channels'][0]['maximum_htlc_out_msat'] == 133337)
result = l1.rpc.setchannel(feebase=43, id=scid)
@ -1943,6 +1948,16 @@ def test_setchannel_usage(node_factory, bitcoind):
assert(result['channels'][0]['short_channel_id'] == scid)
assert(result['channels'][0]['fee_base_msat'] == 43)
assert(result['channels'][0]['fee_proportional_millionths'] == 42)
assert result['channels'][0]['minimum_htlc_out_msat'] == 17
assert(result['channels'][0]['maximum_htlc_out_msat'] == 133337)
result = l1.rpc.setchannel(htlcmin=45, id=scid)
assert(len(result['channels']) == 1)
assert(re.match('^[0-9a-f]{64}$', result['channels'][0]['channel_id']))
assert(result['channels'][0]['short_channel_id'] == scid)
assert(result['channels'][0]['fee_base_msat'] == 43)
assert(result['channels'][0]['fee_proportional_millionths'] == 42)
assert result['channels'][0]['minimum_htlc_out_msat'] == 45
assert(result['channels'][0]['maximum_htlc_out_msat'] == 133337)
result = l1.rpc.setchannel(htlcmax=43333, id=scid)
@ -1951,6 +1966,7 @@ def test_setchannel_usage(node_factory, bitcoind):
assert(result['channels'][0]['short_channel_id'] == scid)
assert(result['channels'][0]['fee_base_msat'] == 43)
assert(result['channels'][0]['fee_proportional_millionths'] == 42)
assert result['channels'][0]['minimum_htlc_out_msat'] == 45
assert(result['channels'][0]['maximum_htlc_out_msat'] == 43333)
# check if negative fees raise error and DB keeps values
@ -2079,6 +2095,7 @@ def test_setchannel_routing(node_factory, bitcoind):
DEF_BASE = 1
DEF_PPM = 10
MAX_HTLC = Millisatoshi(int(FUNDAMOUNT * 1000 * 0.99))
MIN_HTLC = Millisatoshi(0)
l1, l2, l3 = node_factory.line_graph(
3, announce_channels=True, wait_for_announce=True,
@ -2089,11 +2106,12 @@ def test_setchannel_routing(node_factory, bitcoind):
scid = l2.get_channel_scid(l3)
# TEST CUSTOM VALUES
l2.rpc.setchannel(scid, 1337, 137, 4000000, enforcedelay=0)
l2.rpc.setchannel(scid, 1337, 137, 17, 4000000, enforcedelay=0)
# wait for l1 to see updated channel via gossip
wait_for(lambda: [c['base_fee_millisatoshi'] for c in l1.rpc.listchannels(scid)['channels']] == [1337, DEF_BASE])
wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid)['channels']] == [137, DEF_PPM])
wait_for(lambda: [c['htlc_minimum_msat'] for c in l1.rpc.listchannels(scid)['channels']] == [17, MIN_HTLC])
wait_for(lambda: [c['htlc_maximum_msat'] for c in l1.rpc.listchannels(scid)['channels']] == [4000000, MAX_HTLC])
# test fees are applied to HTLC forwards
@ -2136,7 +2154,7 @@ def test_setchannel_routing(node_factory, bitcoind):
# In case l3 includes a routehint, we need to make sure they also know
# about the new fees, otherwise we may end up with the old feerate
wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['htlc_maximum_msat'], c['active']) for c in l3.rpc.listchannels(scid)['channels']] == [(1337, 137, 4000000, True), (DEF_BASE, DEF_PPM, MAX_HTLC, True)])
wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['htlc_minimum_msat'], c['htlc_maximum_msat'], c['active']) for c in l3.rpc.listchannels(scid)['channels']] == [(1337, 137, 17, 4000000, True), (DEF_BASE, DEF_PPM, MIN_HTLC, MAX_HTLC, True)])
# do and check actual payment
inv = l3.rpc.invoice(4000000, 'test_setchannel_2', 'desc')
@ -2153,9 +2171,33 @@ def test_setchannel_routing(node_factory, bitcoind):
l1.rpc.sendpay(route_ok, inv['payment_hash'], payment_secret=inv['payment_secret'])
l1.rpc.waitsendpay(inv['payment_hash'])
# Now try below minimum
route_ok = l1.rpc.getroute(l3.info['id'], 17, 1)["route"]
assert len(route_ok) == 2
assert route_ok[0]['msatoshi'] == 1337 + 17
assert route_ok[1]['msatoshi'] == 17
route_bad = copy.deepcopy(route_ok)
route_bad[0]['msatoshi'] = 1337 + 16
route_bad[1]['msatoshi'] = 16
route_bad[0]['amount_msat'] = Millisatoshi(1337 + 16)
route_bad[1]['amount_msat'] = Millisatoshi(16)
assert route_bad != route_ok
inv = l3.rpc.invoice(17, 'test_setchannel_3', 'desc')
# This will fail.
l1.rpc.sendpay(route_bad, inv['payment_hash'], payment_secret=inv['payment_secret'])
with pytest.raises(RpcError, match='WIRE_TEMPORARY_CHANNEL_FAILURE'):
l1.rpc.waitsendpay(inv['payment_hash'])
# This will succeed
l1.rpc.sendpay(route_ok, inv['payment_hash'], payment_secret=inv['payment_secret'])
l1.rpc.waitsendpay(inv['payment_hash'])
# Check that this one warns about capacity!
inv = l3.rpc.call('invoice', {'msatoshi': 4001793,
'label': 'test_setchannel_3',
'label': 'test_setchannel_4',
'description': 'desc'})
assert 'warning_capacity' in inv
@ -2213,6 +2255,7 @@ def test_setchannel_restart(node_factory, bitcoind):
# - l1 routing can be made to l3 and global (1 10) fees are applied
DEF_BASE = 1
DEF_PPM = 10
MIN_HTLC = Millisatoshi(0)
MAX_HTLC = Millisatoshi(int(FUNDAMOUNT * 1000 * 0.99))
OPTS = {'may_reconnect': True, 'fee-base': DEF_BASE, 'fee-per-satoshi': DEF_PPM}
@ -2223,7 +2266,7 @@ def test_setchannel_restart(node_factory, bitcoind):
scid23 = l2.get_channel_scid(l3)
# l2 set custom fees
l2.rpc.setchannel(scid23, 1337, 137, 500001)
l2.rpc.setchannel(scid23, 1337, 137, 17, 500001)
# restart l2 and reconnect
l2.restart()
@ -2234,11 +2277,11 @@ def test_setchannel_restart(node_factory, bitcoind):
wait_for(lambda: [c['active'] for c in l1.rpc.listchannels(scid12)['channels']] == [True, True])
# l1 wait for channel update from l2
wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['htlc_maximum_msat'], c['active']) for c in l1.rpc.listchannels(scid23)['channels']] == [(1337, 137, 500001, True), (DEF_BASE, DEF_PPM, MAX_HTLC, True)])
wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['htlc_minimum_msat'], c['htlc_maximum_msat'], c['active']) for c in l1.rpc.listchannels(scid23)['channels']] == [(1337, 137, 17, 500001, True), (DEF_BASE, DEF_PPM, MIN_HTLC, MAX_HTLC, True)])
# In case l3 includes a routehint, we need to make sure they also know
# about the new fees, otherwise we may end up with the old feerate
wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['htlc_maximum_msat'], c['active']) for c in l3.rpc.listchannels(scid23)['channels']] == [(1337, 137, 500001, True), (DEF_BASE, DEF_PPM, MAX_HTLC, True)])
wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['htlc_minimum_msat'], c['htlc_maximum_msat'], c['active']) for c in l3.rpc.listchannels(scid23)['channels']] == [(1337, 137, 17, 500001, True), (DEF_BASE, DEF_PPM, MIN_HTLC, MAX_HTLC, True)])
# l1 can make payment to l3 with custom fees being applied
# Note: BOLT #7 math works out to 1405 msat fees
@ -2269,7 +2312,7 @@ def test_setchannel_all(node_factory, bitcoind):
scid3 = l1.get_channel_scid(l3)
# now try to set all (two) channels using wildcard syntax
result = l1.rpc.setchannel("all", 0xDEAD, 0xBEEF, 0xCAFE)
result = l1.rpc.setchannel("all", 0xDEAD, 0xBEEF, 0xBAD, 0xCAFE)
wait_for(lambda: [c['base_fee_millisatoshi'] for c in l1.rpc.listchannels(scid2)['channels']] == [DEF_BASE, 0xDEAD])
wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid2)['channels']] == [DEF_PPM, 0xBEEF])
@ -2281,11 +2324,13 @@ def test_setchannel_all(node_factory, bitcoind):
assert result['channels'][0]['short_channel_id'] == scid2
assert result['channels'][0]['fee_base_msat'] == 0xDEAD
assert result['channels'][0]['fee_proportional_millionths'] == 0xBEEF
assert result['channels'][0]['minimum_htlc_out_msat'] == 0xBAD
assert result['channels'][0]['maximum_htlc_out_msat'] == 0xCAFE
assert result['channels'][1]['peer_id'] == l3.info['id']
assert result['channels'][1]['short_channel_id'] == scid3
assert result['channels'][1]['fee_base_msat'] == 0xDEAD
assert result['channels'][1]['fee_proportional_millionths'] == 0xBEEF
assert result['channels'][1]['minimum_htlc_out_msat'] == 0xBAD
assert result['channels'][1]['maximum_htlc_out_msat'] == 0xCAFE

View file

@ -871,6 +871,7 @@ static struct migration dbmigrations[] = {
{SQL("ALTER TABLE channel_funding_inflights ADD lease_fee BIGINT DEFAULT 0"), NULL},
/* Default is too big; we set to max after loading */
{SQL("ALTER TABLE channels ADD htlc_maximum_msat BIGINT DEFAULT 2100000000000000"), NULL},
{SQL("ALTER TABLE channels ADD htlc_minimum_msat BIGINT DEFAULT 0"), NULL},
};
/**

View file

@ -694,7 +694,7 @@ void topology_add_sync_waiter_(const tal_t *ctx UNNEEDED,
u8 *towire_channel_disabled(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "towire_channel_disabled called!\n"); abort(); }
/* Generated stub for towire_channeld_config_channel */
u8 *towire_channeld_config_channel(const tal_t *ctx UNNEEDED, u32 *feerate_base UNNEEDED, u32 *feerate_ppm UNNEEDED, struct amount_msat *htlc_maximum UNNEEDED)
u8 *towire_channeld_config_channel(const tal_t *ctx UNNEEDED, u32 *feerate_base UNNEEDED, u32 *feerate_ppm UNNEEDED, struct amount_msat *htlc_minimum UNNEEDED, struct amount_msat *htlc_maximum UNNEEDED)
{ fprintf(stderr, "towire_channeld_config_channel called!\n"); abort(); }
/* Generated stub for towire_channeld_dev_memleak */
u8 *towire_channeld_dev_memleak(const tal_t *ctx UNNEEDED)
@ -1598,6 +1598,7 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx)
100,
lease_commit_sig,
7777, 22,
AMOUNT_MSAT(0),
AMOUNT_MSAT(-1ULL));
db_begin_transaction(w->db);
CHECK(!wallet_err);

View file

@ -1263,7 +1263,7 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm
struct pubkey local_funding_pubkey;
struct pubkey *future_per_commitment_point;
struct amount_sat funding_sat, our_funding_sat;
struct amount_msat push_msat, our_msat, msat_to_us_min, msat_to_us_max, htlc_maximum_msat;
struct amount_msat push_msat, our_msat, msat_to_us_min, msat_to_us_max, htlc_minimum_msat, htlc_maximum_msat;
struct channel_type *type;
secp256k1_ecdsa_signature *lease_commit_sig;
u32 lease_chan_max_msat;
@ -1403,6 +1403,7 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm
db_col_amount_msat(stmt, "msatoshi_local", &our_msat);
db_col_amount_msat(stmt, "msatoshi_to_us_min", &msat_to_us_min);
db_col_amount_msat(stmt, "msatoshi_to_us_max", &msat_to_us_max);
db_col_amount_msat(stmt, "htlc_minimum_msat", &htlc_minimum_msat);
db_col_amount_msat(stmt, "htlc_maximum_msat", &htlc_maximum_msat);
if (!db_col_is_null(stmt, "lease_commit_sig")) {
@ -1480,6 +1481,7 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm
lease_commit_sig,
lease_chan_max_msat,
lease_chan_max_ppt,
htlc_minimum_msat,
htlc_maximum_msat);
if (!wallet_channel_load_inflights(w, chan)) {
@ -1574,6 +1576,7 @@ static bool wallet_channels_load_active(struct wallet *w)
", lease_commit_sig"
", lease_chan_max_msat"
", lease_chan_max_ppt"
", htlc_minimum_msat"
", htlc_maximum_msat"
" FROM channels"
" WHERE state != ?;")); //? 0
@ -1855,8 +1858,9 @@ void wallet_channel_save(struct wallet *w, struct channel *chan)
" lease_commit_sig=?," // 39
" lease_chan_max_msat=?," // 40
" lease_chan_max_ppt=?," // 41
" htlc_maximum_msat=?" // 42
" WHERE id=?")); // 43
" htlc_minimum_msat=?," // 42
" htlc_maximum_msat=?" // 43
" WHERE id=?")); // 44
db_bind_u64(stmt, 0, chan->their_shachain.id);
if (chan->scid)
db_bind_short_channel_id(stmt, 1, chan->scid);
@ -1919,8 +1923,9 @@ void wallet_channel_save(struct wallet *w, struct channel *chan)
db_bind_null(stmt, 40);
db_bind_null(stmt, 41);
}
db_bind_amount_msat(stmt, 42, &chan->htlc_maximum_msat);
db_bind_u64(stmt, 43, chan->dbid);
db_bind_amount_msat(stmt, 42, &chan->htlc_minimum_msat);
db_bind_amount_msat(stmt, 43, &chan->htlc_maximum_msat);
db_bind_u64(stmt, 44, chan->dbid);
db_exec_prepared_v2(take(stmt));
wallet_channel_config_save(w, &chan->channel_info.their_config);