diff --git a/fuzz/fuzz_targets/router_target.rs b/fuzz/fuzz_targets/router_target.rs index f7b373cdbc9..9e6d4e42ae6 100644 --- a/fuzz/fuzz_targets/router_target.rs +++ b/fuzz/fuzz_targets/router_target.rs @@ -206,6 +206,10 @@ pub fn do_test(data: &[u8]) { remote_network_id: get_pubkey!(), channel_value_satoshis: slice_to_be64(get_slice!(8)), user_id: 0, + their_cltv_expiry_delta: 0, + their_htlc_minimum_msat: 0, + their_fee_base_msat: 0, + their_fee_proportional_millionths: 0, }); } Some(&first_hops_vec[..]) diff --git a/src/ln/channel.rs b/src/ln/channel.rs index 296d97c164e..af323125a42 100644 --- a/src/ln/channel.rs +++ b/src/ln/channel.rs @@ -325,6 +325,9 @@ pub(super) struct Channel { their_to_self_delay: u16, //implied by BREAKDOWN_TIMEOUT: our_to_self_delay: u16, their_max_accepted_htlcs: u16, + their_cltv_expiry_delta: u16, + their_fee_base_msat: u32, + their_fee_proportional_millionths: u32, //implied by OUR_MAX_HTLCS: our_max_accepted_htlcs: u16, minimum_depth: u32, @@ -491,6 +494,9 @@ impl Channel { our_htlc_minimum_msat: Channel::derive_our_htlc_minimum_msat(feerate), their_to_self_delay: 0, their_max_accepted_htlcs: 0, + their_cltv_expiry_delta: 0, + their_fee_base_msat: 0, + their_fee_proportional_millionths: 0, minimum_depth: 0, // Filled in in accept_channel their_funding_pubkey: None, @@ -681,6 +687,9 @@ impl Channel { our_htlc_minimum_msat: Channel::derive_our_htlc_minimum_msat(msg.feerate_per_kw as u64), their_to_self_delay: msg.to_self_delay, their_max_accepted_htlcs: msg.max_accepted_htlcs, + their_cltv_expiry_delta: 0, + their_fee_base_msat: 0, + their_fee_proportional_millionths: 0, minimum_depth: Channel::derive_minimum_depth(msg.funding_satoshis*1000, msg.push_msat), their_funding_pubkey: Some(msg.funding_pubkey), @@ -2040,6 +2049,20 @@ impl Channel { } + /// Set channel fields with remote peer forwarding parameters, used mainly by ChannelDetails to feed the Router + /// If remote peer set htlc_minimum_msat to full channel value, we reject the whole update and close channel + pub fn channel_update(&mut self, msg: &msgs::ChannelUpdate) -> Result<(), ChannelError> { + if msg.contents.htlc_minimum_msat >= (self.channel_value_satoshis - self.their_channel_reserve_satoshis) * 1000 { + return Err(ChannelError::Close("Minimum htlc value is full channel value")); + } + self.their_cltv_expiry_delta = msg.contents.cltv_expiry_delta; + self.their_htlc_minimum_msat = msg.contents.htlc_minimum_msat; + self.their_fee_base_msat = msg.contents.fee_base_msat; + self.their_fee_proportional_millionths = msg.contents.fee_proportional_millionths; + + Ok(()) + } + /// Adds a pending update to this channel. See the doc for send_htlc for /// further details on the optionness of the return value. /// You MUST call send_commitment prior to any other calls on this Channel @@ -2641,6 +2664,18 @@ impl Channel { self.our_htlc_minimum_msat } + pub fn get_their_cltv_expiry_delta(&self) -> u16 { + self.their_cltv_expiry_delta + } + + pub fn get_their_fee_base_msat(&self) -> u32 { + self.their_fee_base_msat + } + + pub fn get_their_fee_proportional_millionths(&self) -> u32 { + self.their_fee_proportional_millionths + } + pub fn get_value_satoshis(&self) -> u64 { self.channel_value_satoshis } @@ -3587,6 +3622,9 @@ impl Writeable for Channel { self.our_htlc_minimum_msat.write(writer)?; self.their_to_self_delay.write(writer)?; self.their_max_accepted_htlcs.write(writer)?; + self.their_cltv_expiry_delta.write(writer)?; + self.their_fee_base_msat.write(writer)?; + self.their_fee_proportional_millionths.write(writer)?; self.minimum_depth.write(writer)?; write_option!(self.their_funding_pubkey); @@ -3761,6 +3799,9 @@ impl ReadableArgs> for Channel { let our_htlc_minimum_msat = Readable::read(reader)?; let their_to_self_delay = Readable::read(reader)?; let their_max_accepted_htlcs = Readable::read(reader)?; + let their_cltv_expiry_delta = Readable::read(reader)?; + let their_fee_base_msat = Readable::read(reader)?; + let their_fee_proportional_millionths = Readable::read(reader)?; let minimum_depth = Readable::read(reader)?; let their_funding_pubkey = read_option!(); @@ -3838,6 +3879,9 @@ impl ReadableArgs> for Channel { our_htlc_minimum_msat, their_to_self_delay, their_max_accepted_htlcs, + their_cltv_expiry_delta, + their_fee_base_msat, + their_fee_proportional_millionths, minimum_depth, their_funding_pubkey, diff --git a/src/ln/channelmanager.rs b/src/ln/channelmanager.rs index a4723646971..c144d612ae3 100644 --- a/src/ln/channelmanager.rs +++ b/src/ln/channelmanager.rs @@ -384,6 +384,14 @@ pub struct ChannelDetails { pub channel_value_satoshis: u64, /// The user_id passed in to create_channel, or 0 if the channel was inbound. pub user_id: u64, + /// The CLTV delta subtracted by the remote peer as a forwarding node to any incoming HTLC's cltv_expiry + pub their_cltv_expiry_delta: u16, + /// The minimum HTLC amount for which the remote peer as a forwarding node is ready to relay + pub their_htlc_minimum_msat: u64, + /// The base fee the remote peer as a forwarding node will take to relay any HTLC. + pub their_fee_base_msat: u32, + /// The proportional fee on amount forwarded by a HTLC the remote peer will take to relay. + pub their_fee_proportional_millionths: u32, } macro_rules! handle_error { @@ -539,6 +547,10 @@ impl ChannelManager { remote_network_id: channel.get_their_node_id(), channel_value_satoshis: channel.get_value_satoshis(), user_id: channel.get_user_id(), + their_cltv_expiry_delta: channel.get_their_cltv_expiry_delta(), + their_htlc_minimum_msat: channel.get_their_htlc_minimum_msat(), + their_fee_base_msat: channel.get_their_fee_base_msat(), + their_fee_proportional_millionths: channel.get_their_fee_proportional_millionths(), }); } res @@ -560,6 +572,10 @@ impl ChannelManager { remote_network_id: channel.get_their_node_id(), channel_value_satoshis: channel.get_value_satoshis(), user_id: channel.get_user_id(), + their_cltv_expiry_delta: channel.get_their_cltv_expiry_delta(), + their_htlc_minimum_msat: channel.get_their_htlc_minimum_msat(), + their_fee_base_msat: channel.get_their_fee_base_msat(), + their_fee_proportional_millionths: channel.get_their_fee_proportional_millionths(), }); } } @@ -2444,6 +2460,29 @@ impl ChannelManager { Ok(()) } + fn internal_channel_update(&self, their_node_id: &PublicKey, msg: &msgs::ChannelUpdate) -> Result<(), MsgHandleErrInternal> { + let mut channel_state_lock = self.channel_state.lock().unwrap(); + let channel_state = channel_state_lock.borrow_parts(); + let chan_id = match channel_state.short_to_id.get(&msg.contents.short_channel_id) { + Some(chan_id) => chan_id.clone(), + None => { + // It's not a local channel + return Ok(()) + } + }; + match channel_state.by_id.entry(chan_id) { + hash_map::Entry::Occupied(mut chan) => { + if chan.get().get_their_node_id() != *their_node_id { + //TODO: see issue #153, need a consistent behavior on obnoxious behavior from random node + return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", chan_id)); + } + try_chan_entry!(self, chan.get_mut().channel_update(&msg), channel_state, chan); + }, + hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", chan_id)) + } + Ok(()) + } + fn internal_channel_reestablish(&self, their_node_id: &PublicKey, msg: &msgs::ChannelReestablish) -> Result<(), MsgHandleErrInternal> { let mut channel_state_lock = self.channel_state.lock().unwrap(); let channel_state = channel_state_lock.borrow_parts(); @@ -2776,6 +2815,11 @@ impl ChannelMessageHandler for ChannelManager { handle_error!(self, self.internal_announcement_signatures(their_node_id, msg), their_node_id) } + fn handle_channel_update(&self, their_node_id: &PublicKey, msg: &msgs::ChannelUpdate) -> Result<(), HandleError> { + let _ = self.total_consistency_lock.read().unwrap(); + handle_error!(self, self.internal_channel_update(their_node_id, msg), their_node_id) + } + fn handle_channel_reestablish(&self, their_node_id: &PublicKey, msg: &msgs::ChannelReestablish) -> Result<(), HandleError> { let _ = self.total_consistency_lock.read().unwrap(); handle_error!(self, self.internal_channel_reestablish(their_node_id, msg), their_node_id) diff --git a/src/ln/msgs.rs b/src/ln/msgs.rs index 354376649c7..805ec14b5db 100644 --- a/src/ln/msgs.rs +++ b/src/ln/msgs.rs @@ -559,6 +559,8 @@ pub trait ChannelMessageHandler : events::MessageSendEventsProvider + Send + Syn // Channel-to-announce: /// Handle an incoming announcement_signatures message from the given peer. fn handle_announcement_signatures(&self, their_node_id: &PublicKey, msg: &AnnouncementSignatures) -> Result<(), HandleError>; + /// Handle an incoming channel_update message from the given peer. + fn handle_channel_update(&self, their_node_id: &PublicKey, msg: &ChannelUpdate) -> Result<(), HandleError>; // Connection loss/reestablish: /// Indicates a connection to the peer failed/an existing connection was lost. If no connection diff --git a/src/ln/peer_handler.rs b/src/ln/peer_handler.rs index 1ea825473a4..f0cb7166f43 100644 --- a/src/ln/peer_handler.rs +++ b/src/ln/peer_handler.rs @@ -739,6 +739,7 @@ impl PeerManager { }, 258 => { let msg = try_potential_decodeerror!(msgs::ChannelUpdate::read(&mut reader)); + try_potential_handleerror!(self.message_handler.chan_handler.handle_channel_update(&peer.their_node_id.unwrap(), &msg)); let should_forward = try_potential_handleerror!(self.message_handler.route_handler.handle_channel_update(&msg)); if should_forward { diff --git a/src/ln/router.rs b/src/ln/router.rs index 3de73ccc059..f993f5493f4 100644 --- a/src/ln/router.rs +++ b/src/ln/router.rs @@ -1292,6 +1292,10 @@ mod tests { remote_network_id: node8.clone(), channel_value_satoshis: 0, user_id: 0, + their_cltv_expiry_delta: 0, + their_htlc_minimum_msat: 0, + their_fee_base_msat: 0, + their_fee_proportional_millionths: 0, }]; let route = router.get_route(&node3, Some(&our_chans), &Vec::new(), 100, 42).unwrap(); assert_eq!(route.hops.len(), 2); @@ -1367,6 +1371,10 @@ mod tests { remote_network_id: node4.clone(), channel_value_satoshis: 0, user_id: 0, + their_cltv_expiry_delta: 0, + their_htlc_minimum_msat: 0, + their_fee_base_msat: 0, + their_fee_proportional_millionths: 0, }]; let route = router.get_route(&node7, Some(&our_chans), &last_hops, 100, 42).unwrap(); assert_eq!(route.hops.len(), 2); diff --git a/src/util/config.rs b/src/util/config.rs index a3abdbedeec..ddf4d70ad99 100644 --- a/src/util/config.rs +++ b/src/util/config.rs @@ -31,7 +31,9 @@ pub struct ChannelHandshakeLimits { /// only applies to inbound channels. pub min_funding_satoshis: u64, /// The remote node sets a limit on the minimum size of HTLCs we can send to them. This allows - /// you to limit the maximum minimum-size they can require. + /// you to limit the maximum minimum-size they can require. Remote node may increase its + /// htlc_minimum_msat beyond limits with a channel_update msg, as it's equivalent to them + /// just rejecting all HTLCs we can't enforce it. pub max_htlc_minimum_msat: u64, /// The remote node sets a limit on the maximum value of pending HTLCs to them at any given /// time to limit their funds exposure to HTLCs. This allows you to set a minimum such value. diff --git a/src/util/test_utils.rs b/src/util/test_utils.rs index 33fb63b5c3d..518435e93e8 100644 --- a/src/util/test_utils.rs +++ b/src/util/test_utils.rs @@ -133,6 +133,9 @@ impl msgs::ChannelMessageHandler for TestChannelMessageHandler { fn handle_announcement_signatures(&self, _their_node_id: &PublicKey, _msg: &msgs::AnnouncementSignatures) -> Result<(), HandleError> { Err(HandleError { err: "", action: None }) } + fn handle_channel_update(&self, _their_node_id: &PublicKey, _msg: &msgs::ChannelUpdate) -> Result<(), HandleError> { + Err(HandleError { err: "", action: None }) + } fn handle_channel_reestablish(&self, _their_node_id: &PublicKey, _msg: &msgs::ChannelReestablish) -> Result<(), HandleError> { Err(HandleError { err: "", action: None }) }