Merge pull request #651 from naumenkogs/2020-06-routing-data-improvements
authorMatt Corallo <649246+TheBlueMatt@users.noreply.github.com>
Mon, 27 Jul 2020 17:18:13 +0000 (10:18 -0700)
committerGitHub <noreply@github.com>
Mon, 27 Jul 2020 17:18:13 +0000 (10:18 -0700)
Routing improvements

14 files changed:
fuzz/src/bin/gen_target.sh
fuzz/src/msg_targets/gen_target.sh
fuzz/src/msg_targets/mod.rs
fuzz/src/msg_targets/msg_channel_update.rs
fuzz/src/msg_targets/utils.rs
fuzz/src/router.rs
fuzz/targets.h
lightning/src/ln/channel.rs
lightning/src/ln/channelmanager.rs
lightning/src/ln/functional_tests.rs
lightning/src/ln/msgs.rs
lightning/src/routing/network_graph.rs
lightning/src/routing/router.rs
lightning/src/util/test_utils.rs

index 9b5629e7ce7f8b2425e40ced5e29a7f4d882a407..fb1f839a53f8f371458d35be2e56468b51447770 100755 (executable)
@@ -31,12 +31,12 @@ GEN_TEST msg_update_fee msg_targets::
 GEN_TEST msg_update_fulfill_htlc msg_targets::
 
 GEN_TEST msg_channel_announcement msg_targets::
-GEN_TEST msg_channel_update msg_targets::
 GEN_TEST msg_node_announcement msg_targets::
 
 GEN_TEST msg_update_add_htlc msg_targets::
 GEN_TEST msg_error_message msg_targets::
-GEN_TEST msg_onion_hop_data msg_targets::
+GEN_TEST msg_channel_update msg_targets::
 
+GEN_TEST msg_onion_hop_data msg_targets::
 GEN_TEST msg_ping msg_targets::
 GEN_TEST msg_pong msg_targets::
index 0121e4eb821990ef5c104d95f15fe1f13ae212a8..b9381fc3a9a8df4f1ecc8407487dd35f2fe1480d 100755 (executable)
@@ -29,11 +29,11 @@ GEN_TEST UpdateFee test_msg ""
 GEN_TEST UpdateFulfillHTLC test_msg ""
 
 GEN_TEST ChannelAnnouncement test_msg_exact ""
-GEN_TEST ChannelUpdate test_msg_exact ""
 GEN_TEST NodeAnnouncement test_msg_exact ""
 
 GEN_TEST UpdateAddHTLC test_msg_hole ", 85, 33"
 GEN_TEST ErrorMessage test_msg_hole ", 32, 2"
+GEN_TEST ChannelUpdate test_msg_hole ", 108, 1"
 
 GEN_TEST Init test_msg_simple ""
 GEN_TEST OnionHopData test_msg_simple ""
index 69fc4e74219b8cf20e81e5cb49937918c596ccf5..8c5dd6971712703d13f60c9133b5324aa957310d 100644 (file)
@@ -16,10 +16,10 @@ pub mod msg_update_fail_malformed_htlc;
 pub mod msg_update_fee;
 pub mod msg_update_fulfill_htlc;
 pub mod msg_channel_announcement;
-pub mod msg_channel_update;
 pub mod msg_node_announcement;
 pub mod msg_update_add_htlc;
 pub mod msg_error_message;
+pub mod msg_channel_update;
 pub mod msg_init;
 pub mod msg_onion_hop_data;
 pub mod msg_ping;
index d0326bfa4316aa74136c7d110fc9fcb3ad922258..c428c1c76c3188341aecde4bb8ac20254bb038ed 100644 (file)
@@ -8,11 +8,11 @@ use utils::test_logger;
 
 #[inline]
 pub fn msg_channel_update_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
-       test_msg_exact!(msgs::ChannelUpdate, data);
+       test_msg_hole!(msgs::ChannelUpdate, data, 108, 1);
 }
 
 #[no_mangle]
 pub extern "C" fn msg_channel_update_run(data: *const u8, datalen: usize) {
        let data = unsafe { std::slice::from_raw_parts(data, datalen) };
-       test_msg_exact!(msgs::ChannelUpdate, data);
+       test_msg_hole!(msgs::ChannelUpdate, data, 108, 1);
 }
index 6125e5afd611d3dde51a8713b7b3c4f4cd097edf..7e3bf52e4c7cef431206173dc64f436560182642 100644 (file)
@@ -78,8 +78,8 @@ macro_rules! test_msg_exact {
        }
 }
 
-// Tests a message that must survive roundtrip exactly, modulo one "hole" which may be set to 0s on
-// re-serialization.
+// Tests a message that must survive roundtrip exactly, modulo one "hole" which may be set to
+// any value on re-serialization.
 #[macro_export]
 macro_rules! test_msg_hole {
        ($MsgType: path, $data: ident, $hole: expr, $hole_len: expr) => {
index 3a6b5bdf40cd9af51eb0335de6fbdfd521abe9f9..31e552f5906a518d883d877dc7dcec86e87de1f3 100644 (file)
@@ -172,12 +172,12 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
                                let _ = net_graph_msg_handler.handle_channel_announcement(&decode_msg_with_len16!(msgs::ChannelAnnouncement, 64*4, 32+8+33*4));
                        },
                        2 => {
-                               let _ = net_graph_msg_handler.handle_channel_update(&decode_msg!(msgs::ChannelUpdate, 128));
+                               let _ = net_graph_msg_handler.handle_channel_update(&decode_msg!(msgs::ChannelUpdate, 136));
                        },
                        3 => {
                                match get_slice!(1)[0] {
                                        0 => {
-                                               net_graph_msg_handler.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelUpdateMessage {msg: decode_msg!(msgs::ChannelUpdate, 128)});
+                                               net_graph_msg_handler.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelUpdateMessage {msg: decode_msg!(msgs::ChannelUpdate, 136)});
                                        },
                                        1 => {
                                                let short_channel_id = slice_to_be64(get_slice!(8));
index 430f614fb09154bc72d19428e7503022d2df7d73..fe94a3913b6dc855ba43dd8e983a44cd46c18cbc 100644 (file)
@@ -22,10 +22,10 @@ void msg_update_fail_malformed_htlc_run(const unsigned char* data, size_t data_l
 void msg_update_fee_run(const unsigned char* data, size_t data_len);
 void msg_update_fulfill_htlc_run(const unsigned char* data, size_t data_len);
 void msg_channel_announcement_run(const unsigned char* data, size_t data_len);
-void msg_channel_update_run(const unsigned char* data, size_t data_len);
 void msg_node_announcement_run(const unsigned char* data, size_t data_len);
 void msg_update_add_htlc_run(const unsigned char* data, size_t data_len);
 void msg_error_message_run(const unsigned char* data, size_t data_len);
+void msg_channel_update_run(const unsigned char* data, size_t data_len);
 void msg_onion_hop_data_run(const unsigned char* data, size_t data_len);
 void msg_ping_run(const unsigned char* data, size_t data_len);
 void msg_pong_run(const unsigned char* data, size_t data_len);
index fc3b4fd81746f7dc878a0bf138735ee805026c01..c416c676d78c514723c19e93a77446233ad5a810 100644 (file)
@@ -3128,6 +3128,18 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
                self.our_htlc_minimum_msat
        }
 
+       /// Allowed in any state (including after shutdown)
+       pub fn get_announced_htlc_max_msat(&self) -> u64 {
+               return cmp::min(
+                       // Upper bound by capacity. We make it a bit less than full capacity to prevent attempts
+                       // to use full capacity. This is an effort to reduce routing failures, because in many cases
+                       // channel might have been used to route very small values (either by honest users or as DoS).
+                       self.channel_value_satoshis * 9 / 10,
+
+                       Channel::<ChanSigner>::get_our_max_htlc_value_in_flight_msat(self.channel_value_satoshis)
+               );
+       }
+
        /// Allowed in any state (including after shutdown)
        pub fn get_their_htlc_minimum_msat(&self) -> u64 {
                self.our_htlc_minimum_msat
index d591515c467ab34658fef44d4e97303f91c38a19..5466b3d23c8c96dafd651003ce85c268964d632a 100644 (file)
@@ -34,7 +34,7 @@ use ln::features::{InitFeatures, NodeFeatures};
 use routing::router::{Route, RouteHop};
 use ln::msgs;
 use ln::onion_utils;
-use ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
+use ln::msgs::{ChannelMessageHandler, DecodeError, LightningError, OptionalField};
 use chain::keysinterface::{ChannelKeys, KeysInterface, KeysManager, InMemoryChannelKeys};
 use util::config::UserConfig;
 use util::{byte_utils, events};
@@ -1186,7 +1186,8 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
                                                        res.extend_from_slice(&byte_utils::be32_to_array(msg.cltv_expiry));
                                                }
                                                else if code == 0x1000 | 20 {
-                                                       res.extend_from_slice(&byte_utils::be16_to_array(chan_update.contents.flags));
+                                                       // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
+                                                       res.extend_from_slice(&byte_utils::be16_to_array(0));
                                                }
                                                res.extend_from_slice(&chan_update.encode_with_len()[..]);
                                        }
@@ -1212,9 +1213,10 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
                        chain_hash: self.genesis_hash,
                        short_channel_id: short_channel_id,
                        timestamp: chan.get_update_time_counter(),
-                       flags: (!were_node_one) as u16 | ((!chan.is_live() as u16) << 1),
+                       flags: (!were_node_one) as u8 | ((!chan.is_live() as u8) << 1),
                        cltv_expiry_delta: CLTV_EXPIRY_DELTA,
                        htlc_minimum_msat: chan.get_our_htlc_minimum_msat(),
+                       htlc_maximum_msat: OptionalField::Present(chan.get_announced_htlc_max_msat()),
                        fee_base_msat: chan.get_our_fee_base_msat(&self.fee_estimator),
                        fee_proportional_millionths: chan.get_fee_proportional_millionths(),
                        excess_data: Vec::new(),
@@ -2494,7 +2496,8 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
                                                        let reason = if let Ok(upd) = self.get_channel_update(chan) {
                                                                onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
                                                                        let mut res = Vec::with_capacity(8 + 128);
-                                                                       res.extend_from_slice(&byte_utils::be16_to_array(upd.contents.flags));
+                                                                       // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
+                                                                       res.extend_from_slice(&byte_utils::be16_to_array(0));
                                                                        res.extend_from_slice(&upd.encode_with_len()[..]);
                                                                        res
                                                                }[..])
index ce9ec1d5853adc9851fc4475de83b7f312570877..35371c56ffc836c866655fe792b75e448bd666f5 100644 (file)
@@ -15,7 +15,7 @@ use ln::{chan_utils, onion_utils};
 use routing::router::{Route, RouteHop, get_route};
 use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
 use ln::msgs;
-use ln::msgs::{ChannelMessageHandler,RoutingMessageHandler,HTLCFailChannelUpdate, ErrorAction};
+use ln::msgs::{ChannelMessageHandler,RoutingMessageHandler,HTLCFailChannelUpdate, ErrorAction, OptionalField};
 use util::enforcing_trait_impls::EnforcingChannelKeys;
 use util::{byte_utils, test_utils};
 use util::events::{Event, EventsProvider, MessageSendEvent, MessageSendEventsProvider};
@@ -6058,6 +6058,7 @@ impl msgs::ChannelUpdate {
                                flags: 0,
                                cltv_expiry_delta: 0,
                                htlc_minimum_msat: 0,
+                               htlc_maximum_msat: OptionalField::Absent,
                                fee_base_msat: 0,
                                fee_proportional_millionths: 0,
                                excess_data: vec![],
index bd5d23504b19961b63224c301853ce26971fd68e..554cf7290721951fd3f8eaf00d820e723c48da81 100644 (file)
@@ -427,9 +427,10 @@ pub(crate) struct UnsignedChannelUpdate {
        pub(crate) chain_hash: BlockHash,
        pub(crate) short_channel_id: u64,
        pub(crate) timestamp: u32,
-       pub(crate) flags: u16,
+       pub(crate) flags: u8,
        pub(crate) cltv_expiry_delta: u16,
        pub(crate) htlc_minimum_msat: u64,
+       pub(crate) htlc_maximum_msat: OptionalField<u64>,
        pub(crate) fee_base_msat: u32,
        pub(crate) fee_proportional_millionths: u32,
        pub(crate) excess_data: Vec<u8>,
@@ -517,7 +518,7 @@ pub enum HTLCFailChannelUpdate {
 /// As we wish to serialize these differently from Option<T>s (Options get a tag byte, but
 /// OptionalFeild simply gets Present if there are enough bytes to read into it), we have a
 /// separate enum type for them.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Debug)]
 pub enum OptionalField<T> {
        /// Optional field is included in message
        Present(T),
@@ -742,6 +743,26 @@ impl Readable for OptionalField<Script> {
        }
 }
 
+impl Writeable for OptionalField<u64> {
+       fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
+               match *self {
+                       OptionalField::Present(ref value) => {
+                               value.write(w)?;
+                       },
+                       OptionalField::Absent => {}
+               }
+               Ok(())
+       }
+}
+
+impl Readable for OptionalField<u64> {
+       fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+               let value: u64 = Readable::read(r)?;
+               Ok(OptionalField::Present(value))
+       }
+}
+
+
 impl_writeable_len_match!(AcceptChannel, {
                {AcceptChannel{ shutdown_scriptpubkey: OptionalField::Present(ref script), .. }, 270 + 2 + script.len()},
                {_, 270}
@@ -1180,15 +1201,23 @@ impl_writeable_len_match!(ChannelAnnouncement, {
 
 impl Writeable for UnsignedChannelUpdate {
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
-               w.size_hint(64 + self.excess_data.len());
+               let mut size = 64 + self.excess_data.len();
+               let mut message_flags: u8 = 0;
+               if let OptionalField::Present(_) = self.htlc_maximum_msat {
+                       size += 8;
+                       message_flags = 1;
+               }
+               w.size_hint(size);
                self.chain_hash.write(w)?;
                self.short_channel_id.write(w)?;
                self.timestamp.write(w)?;
-               self.flags.write(w)?;
+               let all_flags = self.flags as u16 | ((message_flags as u16) << 8);
+               all_flags.write(w)?;
                self.cltv_expiry_delta.write(w)?;
                self.htlc_minimum_msat.write(w)?;
                self.fee_base_msat.write(w)?;
                self.fee_proportional_millionths.write(w)?;
+               self.htlc_maximum_msat.write(w)?;
                w.write_all(&self.excess_data[..])?;
                Ok(())
        }
@@ -1196,15 +1225,22 @@ impl Writeable for UnsignedChannelUpdate {
 
 impl Readable for UnsignedChannelUpdate {
        fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+               let has_htlc_maximum_msat;
                Ok(Self {
                        chain_hash: Readable::read(r)?,
                        short_channel_id: Readable::read(r)?,
                        timestamp: Readable::read(r)?,
-                       flags: Readable::read(r)?,
+                       flags: {
+                               let flags: u16 = Readable::read(r)?;
+                               let message_flags = flags >> 8;
+                               has_htlc_maximum_msat = (message_flags as i32 & 1) == 1;
+                               flags as u8
+                       },
                        cltv_expiry_delta: Readable::read(r)?,
                        htlc_minimum_msat: Readable::read(r)?,
                        fee_base_msat: Readable::read(r)?,
                        fee_proportional_millionths: Readable::read(r)?,
+                       htlc_maximum_msat: if has_htlc_maximum_msat { Readable::read(r)? } else { OptionalField::Absent },
                        excess_data: {
                                let mut excess_data = vec![];
                                r.read_to_end(&mut excess_data)?;
@@ -1597,7 +1633,7 @@ mod tests {
                do_encoding_node_announcement(false, false, true, false, true, false, false);
        }
 
-       fn do_encoding_channel_update(direction: bool, disable: bool, htlc_maximum_msat: bool) {
+       fn do_encoding_channel_update(direction: bool, disable: bool, htlc_maximum_msat: bool, excess_data: bool) {
                let secp_ctx = Secp256k1::new();
                let (privkey_1, _) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
                let sig_1 = get_sig_on!(privkey_1, secp_ctx, String::from("01010101010101010101010101010101"));
@@ -1605,12 +1641,13 @@ mod tests {
                        chain_hash: BlockHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(),
                        short_channel_id: 2316138423780173,
                        timestamp: 20190119,
-                       flags: if direction { 1 } else { 0 } | if disable { 1 << 1 } else { 0 } | if htlc_maximum_msat { 1 << 8 } else { 0 },
+                       flags: if direction { 1 } else { 0 } | if disable { 1 << 1 } else { 0 },
                        cltv_expiry_delta: 144,
                        htlc_minimum_msat: 1000000,
+                       htlc_maximum_msat: if htlc_maximum_msat { OptionalField::Present(131355275467161) } else { OptionalField::Absent },
                        fee_base_msat: 10000,
                        fee_proportional_millionths: 20,
-                       excess_data: if htlc_maximum_msat { vec![0, 0, 0, 0, 59, 154, 202, 0] } else { Vec::new() }
+                       excess_data: if excess_data { vec![0, 0, 0, 0, 59, 154, 202, 0] } else { Vec::new() }
                };
                let channel_update = msgs::ChannelUpdate {
                        signature: sig_1,
@@ -1636,6 +1673,9 @@ mod tests {
                }
                target_value.append(&mut hex::decode("009000000000000f42400000271000000014").unwrap());
                if htlc_maximum_msat {
+                       target_value.append(&mut hex::decode("0000777788889999").unwrap());
+               }
+               if excess_data {
                        target_value.append(&mut hex::decode("000000003b9aca00").unwrap());
                }
                assert_eq!(encoded_value, target_value);
@@ -1643,11 +1683,16 @@ mod tests {
 
        #[test]
        fn encoding_channel_update() {
-               do_encoding_channel_update(false, false, false);
-               do_encoding_channel_update(true, false, false);
-               do_encoding_channel_update(false, true, false);
-               do_encoding_channel_update(false, false, true);
-               do_encoding_channel_update(true, true, true);
+               do_encoding_channel_update(false, false, false, false);
+               do_encoding_channel_update(false, false, false, true);
+               do_encoding_channel_update(true, false, false, false);
+               do_encoding_channel_update(true, false, false, true);
+               do_encoding_channel_update(false, true, false, false);
+               do_encoding_channel_update(false, true, false, true);
+               do_encoding_channel_update(false, false, true, false);
+               do_encoding_channel_update(false, false, true, true);
+               do_encoding_channel_update(true, true, true, false);
+               do_encoding_channel_update(true, true, true, true);
        }
 
        fn do_encoding_open_channel(random_bit: bool, shutdown: bool) {
index 9c93b246f0762b72e2601151d5eff2f64ca7cdee..f9644002140d72b14d93b6243dad21fb1c810ea7 100644 (file)
@@ -11,7 +11,7 @@ use bitcoin::blockdata::opcodes;
 
 use chain::chaininterface::{ChainError, ChainWatchInterface};
 use ln::features::{ChannelFeatures, NodeFeatures};
-use ln::msgs::{DecodeError,ErrorAction,LightningError,RoutingMessageHandler,NetAddress};
+use ln::msgs::{DecodeError, ErrorAction, LightningError, RoutingMessageHandler, NetAddress, OptionalField, MAX_VALUE_MSAT};
 use ln::msgs;
 use util::ser::{Writeable, Readable, Writer};
 use util::logger::Logger;
@@ -90,8 +90,8 @@ impl<C: Deref + Sync + Send, L: Deref + Sync + Send> RoutingMessageHandler for N
                        return Err(LightningError{err: "Channel announcement node had a channel with itself".to_owned(), action: ErrorAction::IgnoreError});
                }
 
-               let checked_utxo = match self.chain_monitor.get_chain_utxo(msg.contents.chain_hash, msg.contents.short_channel_id) {
-                       Ok((script_pubkey, _value)) => {
+               let utxo_value = match self.chain_monitor.get_chain_utxo(msg.contents.chain_hash, msg.contents.short_channel_id) {
+                       Ok((script_pubkey, value)) => {
                                let expected_script = Builder::new().push_opcode(opcodes::all::OP_PUSHNUM_2)
                                                                    .push_slice(&msg.contents.bitcoin_key_1.serialize())
                                                                    .push_slice(&msg.contents.bitcoin_key_2.serialize())
@@ -102,11 +102,11 @@ impl<C: Deref + Sync + Send, L: Deref + Sync + Send> RoutingMessageHandler for N
                                }
                                //TODO: Check if value is worth storing, use it to inform routing, and compare it
                                //to the new HTLC max field in channel_update
-                               true
+                               Some(value)
                        },
                        Err(ChainError::NotSupported) => {
                                // Tentatively accept, potentially exposing us to DoS attacks
-                               false
+                               None
                        },
                        Err(ChainError::NotWatched) => {
                                return Err(LightningError{err: format!("Channel announced on an unknown chain ({})", msg.contents.chain_hash.encode().to_hex()), action: ErrorAction::IgnoreError});
@@ -115,7 +115,7 @@ impl<C: Deref + Sync + Send, L: Deref + Sync + Send> RoutingMessageHandler for N
                                return Err(LightningError{err: "Channel announced without corresponding UTXO entry".to_owned(), action: ErrorAction::IgnoreError});
                        },
                };
-               let result = self.network_graph.write().unwrap().update_channel_from_announcement(msg, checked_utxo, Some(&self.secp_ctx));
+               let result = self.network_graph.write().unwrap().update_channel_from_announcement(msg, utxo_value, Some(&self.secp_ctx));
                log_trace!(self.logger, "Added channel_announcement for {}{}", msg.contents.short_channel_id, if !msg.contents.excess_data.is_empty() { " with excess uninterpreted data!" } else { "" });
                result
        }
@@ -215,6 +215,8 @@ pub struct DirectionalChannelInfo {
        pub cltv_expiry_delta: u16,
        /// The minimum value, which must be relayed to the next hop via the channel
        pub htlc_minimum_msat: u64,
+       /// The maximum value which may be relayed to the next hop via the channel.
+       pub htlc_maximum_msat: Option<u64>,
        /// Fees charged when the channel is used for routing
        pub fees: RoutingFees,
        /// Most recent update for the channel received from the network
@@ -236,6 +238,7 @@ impl_writeable!(DirectionalChannelInfo, 0, {
        enabled,
        cltv_expiry_delta,
        htlc_minimum_msat,
+       htlc_maximum_msat,
        fees,
        last_update_message
 });
@@ -254,6 +257,8 @@ pub struct ChannelInfo {
        pub node_two: PublicKey,
        /// Details about the second direction of a channel
        pub two_to_one: Option<DirectionalChannelInfo>,
+       /// The channel capacity as seen on-chain, if chain lookup is available.
+       pub capacity_sats: Option<u64>,
        /// An initial announcement of the channel
        /// Mostly redundant with the data we store in fields explicitly.
        /// Everything else is useful only for sending out for initial routing sync.
@@ -275,6 +280,7 @@ impl_writeable!(ChannelInfo, 0, {
        one_to_two,
        node_two,
        two_to_one,
+       capacity_sats,
        announcement_message
 });
 
@@ -552,7 +558,7 @@ impl NetworkGraph {
        /// which is probably result of a reorg. In that case, we update channel info only if the
        /// utxo was checked, otherwise stick to the existing update, to prevent DoS risks.
        /// Announcement signatures are checked here only if Secp256k1 object is provided.
-       fn update_channel_from_announcement(&mut self, msg: &msgs::ChannelAnnouncement, checked_utxo: bool, secp_ctx: Option<&Secp256k1<secp256k1::VerifyOnly>>) -> Result<bool, LightningError> {
+       fn update_channel_from_announcement(&mut self, msg: &msgs::ChannelAnnouncement, utxo_value: Option<u64>, secp_ctx: Option<&Secp256k1<secp256k1::VerifyOnly>>) -> Result<bool, LightningError> {
                if let Some(sig_verifier) = secp_ctx {
                        let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.contents.encode()[..])[..]);
                        secp_verify_sig!(sig_verifier, &msg_hash, &msg.node_signature_1, &msg.contents.node_id_1);
@@ -569,6 +575,7 @@ impl NetworkGraph {
                                one_to_two: None,
                                node_two: msg.contents.node_id_2.clone(),
                                two_to_one: None,
+                               capacity_sats: utxo_value,
                                announcement_message: if should_relay { Some(msg.clone()) } else { None },
                        };
 
@@ -577,7 +584,7 @@ impl NetworkGraph {
                                //TODO: because asking the blockchain if short_channel_id is valid is only optional
                                //in the blockchain API, we need to handle it smartly here, though it's unclear
                                //exactly how...
-                               if checked_utxo {
+                               if utxo_value.is_some() {
                                        // Either our UTXO provider is busted, there was a reorg, or the UTXO provider
                                        // only sometimes returns results. In any case remove the previous entry. Note
                                        // that the spec expects us to "blacklist" the node_ids involved, but we can't
@@ -659,6 +666,19 @@ impl NetworkGraph {
                match self.channels.get_mut(&msg.contents.short_channel_id) {
                        None => return Err(LightningError{err: "Couldn't find channel for update".to_owned(), action: ErrorAction::IgnoreError}),
                        Some(channel) => {
+                               if let OptionalField::Present(htlc_maximum_msat) = msg.contents.htlc_maximum_msat {
+                                       if htlc_maximum_msat > MAX_VALUE_MSAT {
+                                               return Err(LightningError{err: "htlc_maximum_msat is larger than maximum possible msats".to_owned(), action: ErrorAction::IgnoreError});
+                                       }
+
+                                       if let Some(capacity_sats) = channel.capacity_sats {
+                                               // It's possible channel capacity is available now, although it wasn't available at announcement (so the field is None).
+                                               // Don't query UTXO set here to reduce DoS risks.
+                                               if htlc_maximum_msat > capacity_sats * 1000 {
+                                                       return Err(LightningError{err: "htlc_maximum_msat is larger than channel capacity".to_owned(), action: ErrorAction::IgnoreError});
+                                               }
+                                       }
+                               }
                                macro_rules! maybe_update_channel_info {
                                        ( $target: expr, $src_node: expr) => {
                                                if let Some(existing_chan_info) = $target.as_ref() {
@@ -681,6 +701,7 @@ impl NetworkGraph {
                                                        last_update: msg.contents.timestamp,
                                                        cltv_expiry_delta: msg.contents.cltv_expiry_delta,
                                                        htlc_minimum_msat: msg.contents.htlc_minimum_msat,
+                                                       htlc_maximum_msat: if let OptionalField::Present(max_value) = msg.contents.htlc_maximum_msat { Some(max_value) } else { None },
                                                        fees: RoutingFees {
                                                                base_msat: msg.contents.fee_base_msat,
                                                                proportional_millionths: msg.contents.fee_proportional_millionths,
@@ -774,8 +795,9 @@ mod tests {
        use chain::chaininterface;
        use ln::features::{ChannelFeatures, NodeFeatures};
        use routing::network_graph::{NetGraphMsgHandler, NetworkGraph};
-       use ln::msgs::{RoutingMessageHandler, UnsignedNodeAnnouncement, NodeAnnouncement,
-               UnsignedChannelAnnouncement, ChannelAnnouncement, UnsignedChannelUpdate, ChannelUpdate, HTLCFailChannelUpdate};
+       use ln::msgs::{OptionalField, RoutingMessageHandler, UnsignedNodeAnnouncement, NodeAnnouncement,
+               UnsignedChannelAnnouncement, ChannelAnnouncement, UnsignedChannelUpdate, ChannelUpdate, HTLCFailChannelUpdate,
+               MAX_VALUE_MSAT};
        use util::test_utils;
        use util::logger::Logger;
        use util::ser::{Readable, Writeable};
@@ -1110,7 +1132,11 @@ mod tests {
 
        #[test]
        fn handling_channel_update() {
-               let (secp_ctx, net_graph_msg_handler) = create_net_graph_msg_handler();
+               let secp_ctx = Secp256k1::new();
+               let logger: Arc<Logger> = Arc::new(test_utils::TestLogger::new());
+               let chain_monitor = Arc::new(test_utils::TestChainWatcher::new());
+               let net_graph_msg_handler = NetGraphMsgHandler::new(chain_monitor.clone(), Arc::clone(&logger));
+
                let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap();
                let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap();
                let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_1_privkey);
@@ -1121,8 +1147,16 @@ mod tests {
                let zero_hash = Sha256dHash::hash(&[0; 32]);
                let short_channel_id = 0;
                let chain_hash = genesis_block(Network::Testnet).header.bitcoin_hash();
+               let amount_sats = 1000_000;
+
                {
                        // Announce a channel we will update
+                       let good_script = Builder::new().push_opcode(opcodes::all::OP_PUSHNUM_2)
+                          .push_slice(&PublicKey::from_secret_key(&secp_ctx, node_1_btckey).serialize())
+                          .push_slice(&PublicKey::from_secret_key(&secp_ctx, node_2_btckey).serialize())
+                          .push_opcode(opcodes::all::OP_PUSHNUM_2)
+                          .push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script().to_v0_p2wsh();
+                       *chain_monitor.utxo_ret.lock().unwrap() = Ok((good_script.clone(), amount_sats));
                        let unsigned_announcement = UnsignedChannelAnnouncement {
                                features: ChannelFeatures::empty(),
                                chain_hash,
@@ -1156,6 +1190,7 @@ mod tests {
                        flags: 0,
                        cltv_expiry_delta: 144,
                        htlc_minimum_msat: 1000000,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: 10000,
                        fee_proportional_millionths: 20,
                        excess_data: Vec::new()
@@ -1194,6 +1229,7 @@ mod tests {
                        Ok(res) => assert!(!res),
                        _ => panic!()
                };
+               unsigned_channel_update.timestamp += 10;
 
                unsigned_channel_update.short_channel_id += 1;
                let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_channel_update.encode()[..])[..]);
@@ -1208,6 +1244,31 @@ mod tests {
                };
                unsigned_channel_update.short_channel_id = short_channel_id;
 
+               unsigned_channel_update.htlc_maximum_msat = OptionalField::Present(MAX_VALUE_MSAT + 1);
+               let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_channel_update.encode()[..])[..]);
+               let valid_channel_update = ChannelUpdate {
+                       signature: secp_ctx.sign(&msghash, node_1_privkey),
+                       contents: unsigned_channel_update.clone()
+               };
+
+               match net_graph_msg_handler.handle_channel_update(&valid_channel_update) {
+                       Ok(_) => panic!(),
+                       Err(e) => assert_eq!(e.err, "htlc_maximum_msat is larger than maximum possible msats")
+               };
+               unsigned_channel_update.htlc_maximum_msat = OptionalField::Absent;
+
+               unsigned_channel_update.htlc_maximum_msat = OptionalField::Present(amount_sats * 1000 + 1);
+               let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_channel_update.encode()[..])[..]);
+               let valid_channel_update = ChannelUpdate {
+                       signature: secp_ctx.sign(&msghash, node_1_privkey),
+                       contents: unsigned_channel_update.clone()
+               };
+
+               match net_graph_msg_handler.handle_channel_update(&valid_channel_update) {
+                       Ok(_) => panic!(),
+                       Err(e) => assert_eq!(e.err, "htlc_maximum_msat is larger than channel capacity")
+               };
+               unsigned_channel_update.htlc_maximum_msat = OptionalField::Absent;
 
                // Even though previous update was not relayed further, we still accepted it,
                // so we now won't accept update before the previous one.
@@ -1289,6 +1350,7 @@ mod tests {
                                flags: 0,
                                cltv_expiry_delta: 144,
                                htlc_minimum_msat: 1000000,
+                               htlc_maximum_msat: OptionalField::Absent,
                                fee_base_msat: 10000,
                                fee_proportional_millionths: 20,
                                excess_data: Vec::new()
@@ -1416,6 +1478,7 @@ mod tests {
                                flags: 0,
                                cltv_expiry_delta: 144,
                                htlc_minimum_msat: 1000000,
+                               htlc_maximum_msat: OptionalField::Absent,
                                fee_base_msat: 10000,
                                fee_proportional_millionths: 20,
                                excess_data: Vec::new()
@@ -1452,6 +1515,7 @@ mod tests {
                                flags: 0,
                                cltv_expiry_delta: 144,
                                htlc_minimum_msat: 1000000,
+                               htlc_maximum_msat: OptionalField::Absent,
                                fee_base_msat: 10000,
                                fee_proportional_millionths: 20,
                                excess_data: [1; 3].to_vec()
index 883d0015fbb464a3d3ec994db838ead6930104aa..f0fa9ccc40595bd79487b998f26213b8b6692693 100644 (file)
@@ -7,7 +7,7 @@ use bitcoin::secp256k1::key::PublicKey;
 
 use ln::channelmanager;
 use ln::features::{ChannelFeatures, NodeFeatures};
-use ln::msgs::{DecodeError,ErrorAction,LightningError};
+use ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
 use routing::network_graph::{NetworkGraph, RoutingFees};
 use util::ser::{Writeable, Readable};
 use util::logger::Logger;
@@ -144,7 +144,7 @@ struct DummyDirectionalChannelInfo {
 }
 
 
-/// Gets a route from us (as specified in the provided NetworkGraph) to the given target node.
+/// Gets a route from us to the given target node.
 ///
 /// Extra routing hops between known nodes and the target will be used if they are included in
 /// last_hops.
@@ -168,7 +168,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, targ
                return Err(LightningError{err: "Cannot generate a route to ourselves".to_owned(), action: ErrorAction::IgnoreError});
        }
 
-       if final_value_msat > 21_000_000 * 1_0000_0000 * 1000 {
+       if final_value_msat > MAX_VALUE_MSAT {
                return Err(LightningError{err: "Cannot generate a route of more value than all existing satoshis".to_owned(), action: ErrorAction::IgnoreError});
        }
 
@@ -244,7 +244,8 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, targ
                                                                channel_features: $chan_features.clone(),
                                                                fee_msat: 0,
                                                                cltv_expiry_delta: 0,
-                                               })
+                                                       },
+                                               )
                                        });
                                        if $src_node_id != *our_node_id {
                                                // Ignore new_fee for channel-from-us as we assume all channels-from-us
@@ -404,7 +405,7 @@ mod tests {
        use routing::router::{get_route, RouteHint, RoutingFees};
        use routing::network_graph::NetGraphMsgHandler;
        use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
-       use ln::msgs::{ErrorAction, LightningError, UnsignedChannelAnnouncement, ChannelAnnouncement, RoutingMessageHandler,
+       use ln::msgs::{ErrorAction, LightningError, OptionalField, UnsignedChannelAnnouncement, ChannelAnnouncement, RoutingMessageHandler,
           NodeAnnouncement, UnsignedNodeAnnouncement, ChannelUpdate, UnsignedChannelUpdate};
        use ln::channelmanager;
        use util::test_utils;
@@ -603,6 +604,7 @@ mod tests {
                        flags: 1,
                        cltv_expiry_delta: 0,
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: 0,
                        fee_proportional_millionths: 0,
                        excess_data: Vec::new()
@@ -617,6 +619,7 @@ mod tests {
                        flags: 0,
                        cltv_expiry_delta: u16::max_value(),
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: u32::max_value(),
                        fee_proportional_millionths: u32::max_value(),
                        excess_data: Vec::new()
@@ -628,6 +631,7 @@ mod tests {
                        flags: 1,
                        cltv_expiry_delta: 0,
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: 0,
                        fee_proportional_millionths: 0,
                        excess_data: Vec::new()
@@ -643,6 +647,7 @@ mod tests {
                        flags: 0,
                        cltv_expiry_delta: u16::max_value(),
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: u32::max_value(),
                        fee_proportional_millionths: u32::max_value(),
                        excess_data: Vec::new()
@@ -654,6 +659,7 @@ mod tests {
                        flags: 1,
                        cltv_expiry_delta: 0,
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: 0,
                        fee_proportional_millionths: 0,
                        excess_data: Vec::new()
@@ -670,6 +676,7 @@ mod tests {
                        flags: 0,
                        cltv_expiry_delta: (3 << 8) | 1,
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: 0,
                        fee_proportional_millionths: 0,
                        excess_data: Vec::new()
@@ -681,6 +688,7 @@ mod tests {
                        flags: 1,
                        cltv_expiry_delta: (3 << 8) | 2,
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: 100,
                        fee_proportional_millionths: 0,
                        excess_data: Vec::new()
@@ -695,6 +703,7 @@ mod tests {
                        flags: 0,
                        cltv_expiry_delta: (4 << 8) | 1,
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: 0,
                        fee_proportional_millionths: 1000000,
                        excess_data: Vec::new()
@@ -706,6 +715,7 @@ mod tests {
                        flags: 1,
                        cltv_expiry_delta: (4 << 8) | 2,
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: 0,
                        fee_proportional_millionths: 0,
                        excess_data: Vec::new()
@@ -719,6 +729,7 @@ mod tests {
                        flags: 0,
                        cltv_expiry_delta: (13 << 8) | 1,
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: 0,
                        fee_proportional_millionths: 2000000,
                        excess_data: Vec::new()
@@ -730,6 +741,7 @@ mod tests {
                        flags: 1,
                        cltv_expiry_delta: (13 << 8) | 2,
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: 0,
                        fee_proportional_millionths: 0,
                        excess_data: Vec::new()
@@ -744,6 +756,7 @@ mod tests {
                        flags: 0,
                        cltv_expiry_delta: (6 << 8) | 1,
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: 0,
                        fee_proportional_millionths: 0,
                        excess_data: Vec::new()
@@ -755,6 +768,7 @@ mod tests {
                        flags: 1,
                        cltv_expiry_delta: (6 << 8) | 2,
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: 0,
                        fee_proportional_millionths: 0,
                        excess_data: Vec::new()
@@ -768,6 +782,7 @@ mod tests {
                        flags: 0,
                        cltv_expiry_delta: (11 << 8) | 1,
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: 0,
                        fee_proportional_millionths: 0,
                        excess_data: Vec::new()
@@ -779,6 +794,7 @@ mod tests {
                        flags: 1,
                        cltv_expiry_delta: (11 << 8) | 2,
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: 0,
                        fee_proportional_millionths: 0,
                        excess_data: Vec::new()
@@ -795,6 +811,7 @@ mod tests {
                        flags: 0,
                        cltv_expiry_delta: (7 << 8) | 1,
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: 0,
                        fee_proportional_millionths: 1000000,
                        excess_data: Vec::new()
@@ -806,6 +823,7 @@ mod tests {
                        flags: 1,
                        cltv_expiry_delta: (7 << 8) | 2,
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: 0,
                        fee_proportional_millionths: 0,
                        excess_data: Vec::new()
@@ -840,6 +858,7 @@ mod tests {
                        flags: 2, // to disable
                        cltv_expiry_delta: 0,
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: 0,
                        fee_proportional_millionths: 0,
                        excess_data: Vec::new()
@@ -851,6 +870,7 @@ mod tests {
                        flags: 2, // to disable
                        cltv_expiry_delta: 0,
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: 0,
                        fee_proportional_millionths: 0,
                        excess_data: Vec::new()
@@ -898,6 +918,7 @@ mod tests {
                        flags: 0, // to enable
                        cltv_expiry_delta: (4 << 8) | 1,
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: 0,
                        fee_proportional_millionths: 1000000,
                        excess_data: Vec::new()
@@ -909,6 +930,7 @@ mod tests {
                        flags: 0, // to enable
                        cltv_expiry_delta: u16::max_value(),
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: u32::max_value(),
                        fee_proportional_millionths: u32::max_value(),
                        excess_data: Vec::new()
index deaa4680cd18e13ea95d085b049226c920c29790..33b070ee89d60e3b880147d1ce6535707ab6d615 100644 (file)
@@ -5,6 +5,7 @@ use chain::keysinterface;
 use ln::channelmonitor;
 use ln::features::{ChannelFeatures, InitFeatures};
 use ln::msgs;
+use ln::msgs::OptionalField;
 use ln::channelmonitor::HTLCUpdate;
 use util::enforcing_trait_impls::EnforcingChannelKeys;
 use util::events;
@@ -216,6 +217,7 @@ fn get_dummy_channel_update(short_chan_id: u64) -> msgs::ChannelUpdate {
                        flags: 0,
                        cltv_expiry_delta: 0,
                        htlc_minimum_msat: 0,
+                       htlc_maximum_msat: OptionalField::Absent,
                        fee_base_msat: 0,
                        fee_proportional_millionths: 0,
                        excess_data: vec![],