diff --git a/channeld/channeld.c b/channeld/channeld.c index 0e082539f..ac3240ba8 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -60,6 +60,7 @@ #define PEER_FD 3 #define GOSSIP_FD 4 #define HSM_FD 5 +#define min(x, y) ((x) < (y) ? (x) : (y)) struct commit_sigs { struct peer *peer; @@ -227,6 +228,25 @@ static const u8 *hsm_req(const tal_t *ctx, const u8 *req TAKES) return msg; } +/* + * The maximum msat that this node will accept for an htlc. + * It's flagged as an optional field in `channel_update`. + * + * We advertise the maximum value possible, defined as the smaller + * of the remote's maximum in-flight HTLC or the total channel + * capacity minus the cumulative reserve. + * FIXME: does this need fuzz? + */ +static const u64 advertised_htlc_max(const u64 funding_msat, + const struct channel_config *our_config, + const struct channel_config *remote_config) +{ + u64 cumulative_reserve_msat = (our_config->channel_reserve_satoshis + + remote_config->channel_reserve_satoshis) * 1000; + return min(remote_config->max_htlc_value_in_flight_msat, + funding_msat - cumulative_reserve_msat); +} + /* Create and send channel_update to gossipd (and maybe peer) */ static void send_channel_update(struct peer *peer, int disable_flag) { @@ -247,7 +267,11 @@ static void send_channel_update(struct peer *peer, int disable_flag) peer->cltv_delta, peer->conf[REMOTE].htlc_minimum_msat, peer->fee_base, - peer->fee_per_satoshi); + peer->fee_per_satoshi, + advertised_htlc_max( + peer->channel->funding_msat, + &peer->conf[LOCAL], + &peer->conf[REMOTE])); wire_sync_write(GOSSIP_FD, take(msg)); } @@ -2306,7 +2330,6 @@ static void handle_offer_htlc(struct peer *peer, const u8 *inmsg) /* FIXME: Fuzz the boundaries a bit to avoid probing? */ case CHANNEL_ERR_MAX_HTLC_VALUE_EXCEEDED: - /* FIXME: We should advertise this? */ failcode = WIRE_TEMPORARY_CHANNEL_FAILURE; failmsg = tal_fmt(inmsg, "Maximum value exceeded"); goto failed; diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index 66f7f43bf..15c4ac0a2 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -130,6 +130,7 @@ gossip_local_channel_update,,cltv_expiry_delta,u16 gossip_local_channel_update,,htlc_minimum_msat,u64 gossip_local_channel_update,,fee_base_msat,u32 gossip_local_channel_update,,fee_proportional_millionths,u32 +gossip_local_channel_update,,htlc_maximum_msat,u64 gossip_local_channel_close,3027 gossip_local_channel_close,,short_channel_id,struct short_channel_id diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 3e6be87cf..386c45413 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -942,6 +942,7 @@ static void update_local_channel(struct daemon *daemon, u64 htlc_minimum_msat, u32 fee_base_msat, u32 fee_proportional_millionths, + u64 htlc_maximum_msat, const char *caller) { secp256k1_ecdsa_signature dummy_sig; @@ -949,10 +950,6 @@ static void update_local_channel(struct daemon *daemon, u32 timestamp = time_now().ts.tv_sec; u8 message_flags, channel_flags; - /* `message_flags` are optional. - * Currently, not set by c-lightning */ - message_flags = 0; - /* So valgrind doesn't complain */ memset(&dummy_sig, 0, sizeof(dummy_sig)); @@ -965,7 +962,10 @@ static void update_local_channel(struct daemon *daemon, if (disable) channel_flags |= ROUTING_FLAGS_DISABLED; - update = towire_channel_update(tmpctx, &dummy_sig, + // We set the htlc_maximum_msat value + message_flags = 0 | ROUTING_OPT_HTLC_MAX_MSAT; + + update = towire_channel_update_option_channel_htlc_max(tmpctx, &dummy_sig, &daemon->rstate->chain_hash, &chan->scid, timestamp, @@ -973,7 +973,8 @@ static void update_local_channel(struct daemon *daemon, cltv_expiry_delta, htlc_minimum_msat, fee_base_msat, - fee_proportional_millionths); + fee_proportional_millionths, + htlc_maximum_msat); if (!wire_sync_write(HSM_FD, towire_hsm_cupdate_sig_req(tmpctx, update))) { @@ -1026,6 +1027,7 @@ static void maybe_update_local_channel(struct daemon *daemon, hc->htlc_minimum_msat, hc->base_fee, hc->proportional_fee, + hc->htlc_maximum_msat, __func__); } @@ -1093,7 +1095,8 @@ out: /* Return true if the information has changed. */ static bool halfchan_new_info(const struct half_chan *hc, u16 cltv_delta, u64 htlc_minimum_msat, - u32 fee_base_msat, u32 fee_proportional_millionths) + u32 fee_base_msat, u32 fee_proportional_millionths, + u64 htlc_maximum_msat) { if (!is_halfchan_defined(hc)) return true; @@ -1101,7 +1104,8 @@ static bool halfchan_new_info(const struct half_chan *hc, return hc->delay != cltv_delta || hc->htlc_minimum_msat != htlc_minimum_msat || hc->base_fee != fee_base_msat - || hc->proportional_fee != fee_proportional_millionths; + || hc->proportional_fee != fee_proportional_millionths + || hc->htlc_maximum_msat != htlc_maximum_msat; } static void handle_local_channel_update(struct peer *peer, const u8 *msg) @@ -1111,6 +1115,7 @@ static void handle_local_channel_update(struct peer *peer, const u8 *msg) bool disable; u16 cltv_expiry_delta; u64 htlc_minimum_msat; + u64 htlc_maximum_msat; u32 fee_base_msat; u32 fee_proportional_millionths; int direction; @@ -1121,7 +1126,8 @@ static void handle_local_channel_update(struct peer *peer, const u8 *msg) &cltv_expiry_delta, &htlc_minimum_msat, &fee_base_msat, - &fee_proportional_millionths)) { + &fee_proportional_millionths, + &htlc_maximum_msat)) { status_broken("peer %s bad local_channel_update %s", type_to_string(tmpctx, struct pubkey, &peer->id), tal_hex(tmpctx, msg)); @@ -1151,7 +1157,8 @@ static void handle_local_channel_update(struct peer *peer, const u8 *msg) * Or, if it's an unannounced channel (only sending to peer). */ if (halfchan_new_info(&chan->half[direction], cltv_expiry_delta, htlc_minimum_msat, - fee_base_msat, fee_proportional_millionths) + fee_base_msat, fee_proportional_millionths, + htlc_maximum_msat) || ((chan->half[direction].channel_flags & ROUTING_FLAGS_DISABLED) && !disable) || !is_chan_public(chan)) { @@ -1161,6 +1168,7 @@ static void handle_local_channel_update(struct peer *peer, const u8 *msg) htlc_minimum_msat, fee_base_msat, fee_proportional_millionths, + htlc_maximum_msat, __func__); } @@ -1748,6 +1756,7 @@ static void gossip_send_keepalive_update(struct daemon *daemon, hc->htlc_minimum_msat, hc->base_fee, hc->proportional_fee, + hc->htlc_maximum_msat, __func__); } diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index 95642c528..2684fe4c9 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -653,6 +653,7 @@ static struct io_plan *handle_channel_update_sig(struct io_conn *conn, struct short_channel_id scid; u32 timestamp, fee_base_msat, fee_proportional_mill; u64 htlc_minimum_msat; + u64 htlc_maximum_msat; u8 message_flags, channel_flags; u16 cltv_expiry_delta; struct bitcoin_blkid chain_hash; @@ -661,10 +662,11 @@ static struct io_plan *handle_channel_update_sig(struct io_conn *conn, if (!fromwire_hsm_cupdate_sig_req(tmpctx, msg_in, &cu)) return bad_req(conn, c, msg_in); - if (!fromwire_channel_update(cu, &sig, &chain_hash, - &scid, ×tamp, &message_flags, &channel_flags, - &cltv_expiry_delta, &htlc_minimum_msat, - &fee_base_msat, &fee_proportional_mill)) { + if (!fromwire_channel_update_option_channel_htlc_max(cu, &sig, + &chain_hash, &scid, ×tamp, &message_flags, + &channel_flags, &cltv_expiry_delta, + &htlc_minimum_msat, &fee_base_msat, + &fee_proportional_mill, &htlc_maximum_msat)) { return bad_req_fmt(conn, c, msg_in, "Bad inner channel_update"); } if (tal_count(cu) < offset) @@ -676,10 +678,11 @@ static struct io_plan *handle_channel_update_sig(struct io_conn *conn, sign_hash(&node_pkey, &hash, &sig); - cu = towire_channel_update(tmpctx, &sig, &chain_hash, + cu = towire_channel_update_option_channel_htlc_max(tmpctx, &sig, &chain_hash, &scid, timestamp, message_flags, channel_flags, cltv_expiry_delta, htlc_minimum_msat, - fee_base_msat, fee_proportional_mill); + fee_base_msat, fee_proportional_mill, + htlc_maximum_msat); return req_reply(conn, c, take(towire_hsm_cupdate_sig_reply(NULL, cu))); } diff --git a/tests/test_pay.py b/tests/test_pay.py index fa39512e0..93d49a4da 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -150,7 +150,7 @@ def test_pay_get_error_with_update(node_factory): # channel_update, and it should patch it to include a type prefix. The # prefix 0x0102 should be in the channel_update, but not in the # onionreply (negation of 0x0102 in the RE) - l1.daemon.wait_for_log(r'Extracted channel_update 0102.*from onionreply 10070080(?!.*0102)') + l1.daemon.wait_for_log(r'Extracted channel_update 0102.*from onionreply 10070088[0-9a-fA-F]{88}') # And now monitor for l1 to apply the channel_update we just extracted l1.daemon.wait_for_log('Received channel_update for channel {}\(.\) now DISABLED was ACTIVE \(from error\)'.format(chanid2))