Merge pull request #2156 from alecchendev/2023-04-mpp-keysend
[rust-lightning] / lightning / src / routing / gossip.rs
index 11bf532e90fc40388f0debf30968fa5c6cb813bd..40969b180af9d6904d79eb29d8cdf05b3c624bc9 100644 (file)
@@ -40,7 +40,7 @@ use crate::io_extras::{copy, sink};
 use crate::prelude::*;
 use core::{cmp, fmt};
 use core::convert::TryFrom;
-use crate::sync::{RwLock, RwLockReadGuard};
+use crate::sync::{RwLock, RwLockReadGuard, LockTestExt};
 #[cfg(feature = "std")]
 use core::sync::atomic::{AtomicUsize, Ordering};
 use crate::sync::Mutex;
@@ -366,6 +366,11 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
                        },
                }
        }
+
+       /// Gets the genesis hash for this network graph.
+       pub fn get_genesis_hash(&self) -> BlockHash {
+               self.genesis_hash
+       }
 }
 
 macro_rules! secp_verify_sig {
@@ -965,7 +970,7 @@ impl<'a> DirectedChannelInfo<'a> {
                                htlc_maximum_msat = cmp::min(htlc_maximum_msat, capacity_msat);
                                EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat: htlc_maximum_msat }
                        },
-                       None => EffectiveCapacity::MaximumHTLC { amount_msat: htlc_maximum_msat },
+                       None => EffectiveCapacity::AdvertisedMaxHTLC { amount_msat: htlc_maximum_msat },
                };
 
                Self {
@@ -1019,7 +1024,7 @@ pub enum EffectiveCapacity {
                liquidity_msat: u64,
        },
        /// The maximum HTLC amount in one direction as advertised on the gossip network.
-       MaximumHTLC {
+       AdvertisedMaxHTLC {
                /// The maximum HTLC amount denominated in millisatoshi.
                amount_msat: u64,
        },
@@ -1033,6 +1038,11 @@ pub enum EffectiveCapacity {
        /// A capacity sufficient to route any payment, typically used for private channels provided by
        /// an invoice.
        Infinite,
+       /// The maximum HTLC amount as provided by an invoice route hint.
+       HintMaxHTLC {
+               /// The maximum HTLC amount denominated in millisatoshi.
+               amount_msat: u64,
+       },
        /// A capacity that is unknown possibly because either the chain state is unavailable to know
        /// the total capacity or the `htlc_maximum_msat` was not advertised on the gossip network.
        Unknown,
@@ -1047,8 +1057,9 @@ impl EffectiveCapacity {
        pub fn as_msat(&self) -> u64 {
                match self {
                        EffectiveCapacity::ExactLiquidity { liquidity_msat } => *liquidity_msat,
-                       EffectiveCapacity::MaximumHTLC { amount_msat } => *amount_msat,
+                       EffectiveCapacity::AdvertisedMaxHTLC { amount_msat } => *amount_msat,
                        EffectiveCapacity::Total { capacity_msat, .. } => *capacity_msat,
+                       EffectiveCapacity::HintMaxHTLC { amount_msat } => *amount_msat,
                        EffectiveCapacity::Infinite => u64::max_value(),
                        EffectiveCapacity::Unknown => UNKNOWN_CHANNEL_CAPACITY_MSAT,
                }
@@ -1056,7 +1067,7 @@ impl EffectiveCapacity {
 }
 
 /// Fees for routing via a given channel or a node
-#[derive(Eq, PartialEq, Copy, Clone, Debug, Hash)]
+#[derive(Eq, PartialEq, Copy, Clone, Debug, Hash, Ord, PartialOrd)]
 pub struct RoutingFees {
        /// Flat routing fee in millisatoshis.
        pub base_msat: u32,
@@ -1327,9 +1338,14 @@ impl<L: Deref> fmt::Display for NetworkGraph<L> where L::Target: Logger {
 impl<L: Deref> Eq for NetworkGraph<L> where L::Target: Logger {}
 impl<L: Deref> PartialEq for NetworkGraph<L> where L::Target: Logger {
        fn eq(&self, other: &Self) -> bool {
-               self.genesis_hash == other.genesis_hash &&
-                       *self.channels.read().unwrap() == *other.channels.read().unwrap() &&
-                       *self.nodes.read().unwrap() == *other.nodes.read().unwrap()
+               // For a total lockorder, sort by position in memory and take the inner locks in that order.
+               // (Assumes that we can't move within memory while a lock is held).
+               let ord = ((self as *const _) as usize) < ((other as *const _) as usize);
+               let a = if ord { (&self.channels, &self.nodes) } else { (&other.channels, &other.nodes) };
+               let b = if ord { (&other.channels, &other.nodes) } else { (&self.channels, &self.nodes) };
+               let (channels_a, channels_b) = (a.0.unsafe_well_ordered_double_lock_self(), b.0.unsafe_well_ordered_double_lock_self());
+               let (nodes_a, nodes_b) = (a.1.unsafe_well_ordered_double_lock_self(), b.1.unsafe_well_ordered_double_lock_self());
+               self.genesis_hash.eq(&other.genesis_hash) && channels_a.eq(&channels_b) && nodes_a.eq(&nodes_b)
        }
 }
 
@@ -1573,6 +1589,13 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
                        return Err(LightningError{err: "Channel announcement node had a channel with itself".to_owned(), action: ErrorAction::IgnoreError});
                }
 
+               if msg.chain_hash != self.genesis_hash {
+                       return Err(LightningError {
+                               err: "Channel announcement chain hash does not match genesis hash".to_owned(),
+                               action: ErrorAction::IgnoreAndLog(Level::Debug),
+                       });
+               }
+
                {
                        let channels = self.channels.read().unwrap();
 
@@ -1819,6 +1842,13 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
        fn update_channel_intern(&self, msg: &msgs::UnsignedChannelUpdate, full_msg: Option<&msgs::ChannelUpdate>, sig: Option<&secp256k1::ecdsa::Signature>) -> Result<(), LightningError> {
                let chan_enabled = msg.flags & (1 << 1) != (1 << 1);
 
+               if msg.chain_hash != self.genesis_hash {
+                       return Err(LightningError {
+                               err: "Channel update chain hash does not match genesis hash".to_owned(),
+                               action: ErrorAction::IgnoreAndLog(Level::Debug),
+                       });
+               }
+
                #[cfg(all(feature = "std", not(test), not(feature = "_test_utils")))]
                {
                        // Note that many tests rely on being able to set arbitrarily old timestamps, thus we
@@ -2311,6 +2341,16 @@ pub(crate) mod tests {
                        Ok(_) => panic!(),
                        Err(e) => assert_eq!(e.err, "Channel announcement node had a channel with itself")
                };
+
+               // Test that channel announcements with the wrong chain hash are ignored (network graph is testnet,
+               // announcement is mainnet).
+               let incorrect_chain_announcement = get_signed_channel_announcement(|unsigned_announcement| {
+                       unsigned_announcement.chain_hash = genesis_block(Network::Bitcoin).header.block_hash();
+               }, node_1_privkey, node_2_privkey, &secp_ctx);
+               match gossip_sync.handle_channel_announcement(&incorrect_chain_announcement) {
+                       Ok(_) => panic!(),
+                       Err(e) => assert_eq!(e.err, "Channel announcement chain hash does not match genesis hash")
+               };
        }
 
        #[test]
@@ -2415,6 +2455,17 @@ pub(crate) mod tests {
                        Ok(_) => panic!(),
                        Err(e) => assert_eq!(e.err, "Invalid signature on channel_update message")
                };
+
+               // Test that channel updates with the wrong chain hash are ignored (network graph is testnet, channel
+               // update is mainet).
+               let incorrect_chain_update = get_signed_channel_update(|unsigned_channel_update| {
+                       unsigned_channel_update.chain_hash = genesis_block(Network::Bitcoin).header.block_hash();
+               }, node_1_privkey, &secp_ctx);
+
+               match gossip_sync.handle_channel_update(&incorrect_chain_update) {
+                       Ok(_) => panic!(),
+                       Err(e) => assert_eq!(e.err, "Channel update chain hash does not match genesis hash")
+               };
        }
 
        #[test]
@@ -2850,7 +2901,7 @@ pub(crate) mod tests {
 
                // It should ignore if gossip_queries feature is not enabled
                {
-                       let init_msg = Init { features: InitFeatures::empty(), remote_network_address: None };
+                       let init_msg = Init { features: InitFeatures::empty(), networks: None, remote_network_address: None };
                        gossip_sync.peer_connected(&node_id_1, &init_msg, true).unwrap();
                        let events = gossip_sync.get_and_clear_pending_msg_events();
                        assert_eq!(events.len(), 0);
@@ -2860,7 +2911,7 @@ pub(crate) mod tests {
                {
                        let mut features = InitFeatures::empty();
                        features.set_gossip_queries_optional();
-                       let init_msg = Init { features, remote_network_address: None };
+                       let init_msg = Init { features, networks: None, remote_network_address: None };
                        gossip_sync.peer_connected(&node_id_1, &init_msg, true).unwrap();
                        let events = gossip_sync.get_and_clear_pending_msg_events();
                        assert_eq!(events.len(), 1);
@@ -3353,31 +3404,28 @@ pub(crate) mod tests {
        }
 }
 
-#[cfg(all(test, feature = "_bench_unstable"))]
-mod benches {
+#[cfg(ldk_bench)]
+pub mod benches {
        use super::*;
-
-       use test::Bencher;
        use std::io::Read;
+       use criterion::{black_box, Criterion};
 
-       #[bench]
-       fn read_network_graph(bench: &mut Bencher) {
+       pub fn read_network_graph(bench: &mut Criterion) {
                let logger = crate::util::test_utils::TestLogger::new();
                let mut d = crate::routing::router::bench_utils::get_route_file().unwrap();
                let mut v = Vec::new();
                d.read_to_end(&mut v).unwrap();
-               bench.iter(|| {
-                       let _ = NetworkGraph::read(&mut std::io::Cursor::new(&v), &logger).unwrap();
-               });
+               bench.bench_function("read_network_graph", |b| b.iter(||
+                       NetworkGraph::read(&mut std::io::Cursor::new(black_box(&v)), &logger).unwrap()
+               ));
        }
 
-       #[bench]
-       fn write_network_graph(bench: &mut Bencher) {
+       pub fn write_network_graph(bench: &mut Criterion) {
                let logger = crate::util::test_utils::TestLogger::new();
                let mut d = crate::routing::router::bench_utils::get_route_file().unwrap();
                let net_graph = NetworkGraph::read(&mut d, &logger).unwrap();
-               bench.iter(|| {
-                       let _ = net_graph.encode();
-               });
+               bench.bench_function("write_network_graph", |b| b.iter(||
+                       black_box(&net_graph).encode()
+               ));
        }
 }