Make the `P2PGossipSync` `UtxoLookup` exchangable without &mut self
[rust-lightning] / lightning / src / routing / gossip.rs
index 892d1b40a962ca9f2f6d80fd840eb4fd164637cc..90b7bb0eddc6b00977f7c087f1935ab42d636e85 100644 (file)
 //! The [`NetworkGraph`] stores the network gossip and [`P2PGossipSync`] fetches it from peers
 
 use bitcoin::secp256k1::constants::PUBLIC_KEY_SIZE;
-use bitcoin::secp256k1::PublicKey;
+use bitcoin::secp256k1::{PublicKey, Verification};
 use bitcoin::secp256k1::Secp256k1;
 use bitcoin::secp256k1;
 
 use bitcoin::hashes::sha256d::Hash as Sha256dHash;
 use bitcoin::hashes::Hash;
+use bitcoin::hashes::hex::FromHex;
 use bitcoin::hash_types::BlockHash;
 
 use bitcoin::network::constants::Network;
@@ -27,7 +28,7 @@ use crate::ln::msgs::{DecodeError, ErrorAction, Init, LightningError, RoutingMes
 use crate::ln::msgs::{ChannelAnnouncement, ChannelUpdate, NodeAnnouncement, GossipTimestampFilter};
 use crate::ln::msgs::{QueryChannelRange, ReplyChannelRange, QueryShortChannelIds, ReplyShortChannelIdsEnd};
 use crate::ln::msgs;
-use crate::routing::utxo::{self, UtxoLookup};
+use crate::routing::utxo::{self, UtxoLookup, UtxoResolver};
 use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer, MaybeReadable};
 use crate::util::logger::{Logger, Level};
 use crate::util::scid_utils::{block_from_scid, scid_from_parts, MAX_SCID_BLOCK};
@@ -38,11 +39,13 @@ use crate::io;
 use crate::io_extras::{copy, sink};
 use crate::prelude::*;
 use core::{cmp, fmt};
-use crate::sync::{RwLock, RwLockReadGuard};
+use core::convert::TryFrom;
+use crate::sync::{RwLock, RwLockReadGuard, LockTestExt};
 #[cfg(feature = "std")]
 use core::sync::atomic::{AtomicUsize, Ordering};
 use crate::sync::Mutex;
 use core::ops::{Bound, Deref};
+use core::str::FromStr;
 
 #[cfg(feature = "std")]
 use std::time::{SystemTime, UNIX_EPOCH};
@@ -76,6 +79,11 @@ impl NodeId {
        pub fn as_slice(&self) -> &[u8] {
                &self.0
        }
+
+       /// Get the public key from this NodeId
+       pub fn as_pubkey(&self) -> Result<PublicKey, secp256k1::Error> {
+               PublicKey::from_slice(&self.0)
+       }
 }
 
 impl fmt::Debug for NodeId {
@@ -130,6 +138,29 @@ impl Readable for NodeId {
        }
 }
 
+impl From<PublicKey> for NodeId {
+       fn from(pubkey: PublicKey) -> Self {
+               Self::from_pubkey(&pubkey)
+       }
+}
+
+impl TryFrom<NodeId> for PublicKey {
+       type Error = secp256k1::Error;
+
+       fn try_from(node_id: NodeId) -> Result<Self, Self::Error> {
+               node_id.as_pubkey()
+       }
+}
+
+impl FromStr for NodeId {
+       type Err = bitcoin::hashes::hex::Error;
+
+       fn from_str(s: &str) -> Result<Self, Self::Err> {
+               let data: [u8; PUBLIC_KEY_SIZE] = FromHex::from_hex(s)?;
+               Ok(NodeId(data))
+       }
+}
+
 /// Represents the network as nodes and channels between them
 pub struct NetworkGraph<L: Deref> where L::Target: Logger {
        secp_ctx: Secp256k1<secp256k1::VerifyOnly>,
@@ -181,7 +212,7 @@ pub enum NetworkUpdate {
                msg: ChannelUpdate,
        },
        /// An error indicating that a channel failed to route a payment, which should be applied via
-       /// [`NetworkGraph::channel_failed`].
+       /// [`NetworkGraph::channel_failed_permanent`] if permanent.
        ChannelFailure {
                /// The short channel id of the closed channel.
                short_channel_id: u64,
@@ -223,7 +254,7 @@ pub struct P2PGossipSync<G: Deref<Target=NetworkGraph<L>>, U: Deref, L: Deref>
 where U::Target: UtxoLookup, L::Target: Logger
 {
        network_graph: G,
-       utxo_lookup: Option<U>,
+       utxo_lookup: RwLock<Option<U>>,
        #[cfg(feature = "std")]
        full_syncs_requested: AtomicUsize,
        pending_events: Mutex<Vec<MessageSendEvent>>,
@@ -242,7 +273,7 @@ where U::Target: UtxoLookup, L::Target: Logger
                        network_graph,
                        #[cfg(feature = "std")]
                        full_syncs_requested: AtomicUsize::new(0),
-                       utxo_lookup,
+                       utxo_lookup: RwLock::new(utxo_lookup),
                        pending_events: Mutex::new(vec![]),
                        logger,
                }
@@ -251,8 +282,8 @@ where U::Target: UtxoLookup, L::Target: Logger
        /// Adds a provider used to check new announcements. Does not affect
        /// existing announcements unless they are updated.
        /// Add, update or remove the provider would replace the current one.
-       pub fn add_utxo_lookup(&mut self, utxo_lookup: Option<U>) {
-               self.utxo_lookup = utxo_lookup;
+       pub fn add_utxo_lookup(&self, utxo_lookup: Option<U>) {
+               *self.utxo_lookup.write().unwrap() = utxo_lookup;
        }
 
        /// Gets a reference to the underlying [`NetworkGraph`] which was provided in
@@ -321,9 +352,10 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
                                let _ = self.update_channel(msg);
                        },
                        NetworkUpdate::ChannelFailure { short_channel_id, is_permanent } => {
-                               let action = if is_permanent { "Removing" } else { "Disabling" };
-                               log_debug!(self.logger, "{} channel graph entry for {} due to a payment failure.", action, short_channel_id);
-                               self.channel_failed(short_channel_id, is_permanent);
+                               if is_permanent {
+                                       log_debug!(self.logger, "Removing channel graph entry for {} due to a payment failure.", short_channel_id);
+                                       self.channel_failed_permanent(short_channel_id);
+                               }
                        },
                        NetworkUpdate::NodeFailure { ref node_id, is_permanent } => {
                                if is_permanent {
@@ -334,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 {
@@ -372,6 +409,29 @@ macro_rules! get_pubkey_from_node_id {
        }
 }
 
+/// Verifies the signature of a [`NodeAnnouncement`].
+///
+/// Returns an error if it is invalid.
+pub fn verify_node_announcement<C: Verification>(msg: &NodeAnnouncement, secp_ctx: &Secp256k1<C>) -> Result<(), LightningError> {
+       let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.contents.encode()[..])[..]);
+       secp_verify_sig!(secp_ctx, &msg_hash, &msg.signature, &get_pubkey_from_node_id!(msg.contents.node_id, "node_announcement"), "node_announcement");
+
+       Ok(())
+}
+
+/// Verifies all signatures included in a [`ChannelAnnouncement`].
+///
+/// Returns an error if one of the signatures is invalid.
+pub fn verify_channel_announcement<C: Verification>(msg: &ChannelAnnouncement, secp_ctx: &Secp256k1<C>) -> Result<(), LightningError> {
+       let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.contents.encode()[..])[..]);
+       secp_verify_sig!(secp_ctx, &msg_hash, &msg.node_signature_1, &get_pubkey_from_node_id!(msg.contents.node_id_1, "channel_announcement"), "channel_announcement");
+       secp_verify_sig!(secp_ctx, &msg_hash, &msg.node_signature_2, &get_pubkey_from_node_id!(msg.contents.node_id_2, "channel_announcement"), "channel_announcement");
+       secp_verify_sig!(secp_ctx, &msg_hash, &msg.bitcoin_signature_1, &get_pubkey_from_node_id!(msg.contents.bitcoin_key_1, "channel_announcement"), "channel_announcement");
+       secp_verify_sig!(secp_ctx, &msg_hash, &msg.bitcoin_signature_2, &get_pubkey_from_node_id!(msg.contents.bitcoin_key_2, "channel_announcement"), "channel_announcement");
+
+       Ok(())
+}
+
 impl<G: Deref<Target=NetworkGraph<L>>, U: Deref, L: Deref> RoutingMessageHandler for P2PGossipSync<G, U, L>
 where U::Target: UtxoLookup, L::Target: Logger
 {
@@ -383,7 +443,7 @@ where U::Target: UtxoLookup, L::Target: Logger
        }
 
        fn handle_channel_announcement(&self, msg: &msgs::ChannelAnnouncement) -> Result<bool, LightningError> {
-               self.network_graph.update_channel_from_announcement(msg, &self.utxo_lookup)?;
+               self.network_graph.update_channel_from_announcement(msg, &*self.utxo_lookup.read().unwrap())?;
                Ok(msg.contents.excess_data.len() <= MAX_EXCESS_BYTES_FOR_RELAY)
        }
 
@@ -933,7 +993,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 {
@@ -987,7 +1047,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,
        },
@@ -1001,6 +1061,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,
@@ -1015,8 +1080,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,
                }
@@ -1024,7 +1090,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,
@@ -1077,33 +1143,33 @@ impl Writeable for NodeAnnouncementInfo {
                        (4, self.rgb, required),
                        (6, self.alias, required),
                        (8, self.announcement_message, option),
-                       (10, empty_addresses, vec_type), // Versions prior to 0.0.115 require this field
+                       (10, empty_addresses, required_vec), // Versions prior to 0.0.115 require this field
                });
                Ok(())
        }
 }
 
 impl Readable for NodeAnnouncementInfo {
-    fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
+       fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
                _init_and_read_tlv_fields!(reader, {
                        (0, features, required),
                        (2, last_update, required),
                        (4, rgb, required),
                        (6, alias, required),
                        (8, announcement_message, option),
-                       (10, _addresses, vec_type), // deprecated, not used anymore
+                       (10, _addresses, optional_vec), // deprecated, not used anymore
                });
                let _: Option<Vec<NetAddress>> = _addresses;
                Ok(Self { features: features.0.unwrap(), last_update: last_update.0.unwrap(), rgb: rgb.0.unwrap(),
                        alias: alias.0.unwrap(), announcement_message })
-    }
+       }
 }
 
 /// A user-defined name for a node, which may be used when displaying the node in a graph.
 ///
 /// Since node aliases are provided by third parties, they are a potential avenue for injection
 /// attacks. Care must be taken when processing.
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub struct NodeAlias(pub [u8; 32]);
 
 impl fmt::Display for NodeAlias {
@@ -1162,7 +1228,7 @@ impl Writeable for NodeInfo {
                write_tlv_fields!(writer, {
                        // Note that older versions of LDK wrote the lowest inbound fees here at type 0
                        (2, self.announcement_info, option),
-                       (4, self.channels, vec_type),
+                       (4, self.channels, required_vec),
                });
                Ok(())
        }
@@ -1193,19 +1259,17 @@ impl Readable for NodeInfo {
                // with zero inbound fees, causing that heuristic to provide little gain. Worse, because it
                // requires additional complexity and lookups during routing, it ends up being a
                // performance loss. Thus, we simply ignore the old field here and no longer track it.
-               let mut _lowest_inbound_channel_fees: Option<RoutingFees> = None;
-               let mut announcement_info_wrap: Option<NodeAnnouncementInfoDeserWrapper> = None;
-               _init_tlv_field_var!(channels, vec_type);
-
-               read_tlv_fields!(reader, {
+               _init_and_read_tlv_fields!(reader, {
                        (0, _lowest_inbound_channel_fees, option),
                        (2, announcement_info_wrap, upgradable_option),
-                       (4, channels, vec_type),
+                       (4, channels, required_vec),
                });
+               let _: Option<RoutingFees> = _lowest_inbound_channel_fees;
+               let announcement_info_wrap: Option<NodeAnnouncementInfoDeserWrapper> = announcement_info_wrap;
 
                Ok(NodeInfo {
                        announcement_info: announcement_info_wrap.map(|w| w.0),
-                       channels: _init_tlv_based_struct_field!(channels, vec_type),
+                       channels,
                })
        }
 }
@@ -1295,9 +1359,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)
        }
 }
 
@@ -1355,8 +1424,7 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
        /// RoutingMessageHandler implementation to call it indirectly. This may be useful to accept
        /// routing messages from a source using a protocol other than the lightning P2P protocol.
        pub fn update_node_from_announcement(&self, msg: &msgs::NodeAnnouncement) -> Result<(), LightningError> {
-               let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.contents.encode()[..])[..]);
-               secp_verify_sig!(self.secp_ctx, &msg_hash, &msg.signature, &get_pubkey_from_node_id!(msg.contents.node_id, "node_announcement"), "node_announcement");
+               verify_node_announcement(msg, &self.secp_ctx)?;
                self.update_node_from_announcement_intern(&msg.contents, Some(&msg))
        }
 
@@ -1396,7 +1464,7 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
                                        features: msg.features.clone(),
                                        last_update: msg.timestamp,
                                        rgb: msg.rgb,
-                                       alias: NodeAlias(msg.alias),
+                                       alias: msg.alias,
                                        announcement_message: if should_relay { full_msg.cloned() } else { None },
                                });
 
@@ -1407,8 +1475,8 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
 
        /// Store or update channel info from a channel announcement.
        ///
-       /// You probably don't want to call this directly, instead relying on a P2PGossipSync's
-       /// RoutingMessageHandler implementation to call it indirectly. This may be useful to accept
+       /// You probably don't want to call this directly, instead relying on a [`P2PGossipSync`]'s
+       /// [`RoutingMessageHandler`] implementation to call it indirectly. This may be useful to accept
        /// routing messages from a source using a protocol other than the lightning P2P protocol.
        ///
        /// If a [`UtxoLookup`] object is provided via `utxo_lookup`, it will be called to verify
@@ -1419,14 +1487,23 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
        where
                U::Target: UtxoLookup,
        {
-               let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.contents.encode()[..])[..]);
-               secp_verify_sig!(self.secp_ctx, &msg_hash, &msg.node_signature_1, &get_pubkey_from_node_id!(msg.contents.node_id_1, "channel_announcement"), "channel_announcement");
-               secp_verify_sig!(self.secp_ctx, &msg_hash, &msg.node_signature_2, &get_pubkey_from_node_id!(msg.contents.node_id_2, "channel_announcement"), "channel_announcement");
-               secp_verify_sig!(self.secp_ctx, &msg_hash, &msg.bitcoin_signature_1, &get_pubkey_from_node_id!(msg.contents.bitcoin_key_1, "channel_announcement"), "channel_announcement");
-               secp_verify_sig!(self.secp_ctx, &msg_hash, &msg.bitcoin_signature_2, &get_pubkey_from_node_id!(msg.contents.bitcoin_key_2, "channel_announcement"), "channel_announcement");
+               verify_channel_announcement(msg, &self.secp_ctx)?;
                self.update_channel_from_unsigned_announcement_intern(&msg.contents, Some(msg), utxo_lookup)
        }
 
+       /// Store or update channel info from a channel announcement.
+       ///
+       /// You probably don't want to call this directly, instead relying on a [`P2PGossipSync`]'s
+       /// [`RoutingMessageHandler`] implementation to call it indirectly. This may be useful to accept
+       /// routing messages from a source using a protocol other than the lightning P2P protocol.
+       ///
+       /// This will skip verification of if the channel is actually on-chain.
+       pub fn update_channel_from_announcement_no_lookup(
+               &self, msg: &ChannelAnnouncement
+       ) -> Result<(), LightningError> {
+               self.update_channel_from_announcement::<&UtxoResolver>(msg, &None)
+       }
+
        /// Store or update channel info from a channel announcement without verifying the associated
        /// signatures. Because we aren't given the associated signatures here we cannot relay the
        /// channel announcement to any of our peers.
@@ -1528,6 +1605,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();
 
@@ -1601,40 +1685,27 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
                Ok(())
        }
 
-       /// Marks a channel in the graph as failed if a corresponding HTLC fail was sent.
-       /// If permanent, removes a channel from the local storage.
-       /// May cause the removal of nodes too, if this was their last channel.
-       /// If not permanent, makes channels unavailable for routing.
-       pub fn channel_failed(&self, short_channel_id: u64, is_permanent: bool) {
+       /// Marks a channel in the graph as failed permanently.
+       ///
+       /// The channel and any node for which this was their last channel are removed from the graph.
+       pub fn channel_failed_permanent(&self, short_channel_id: u64) {
                #[cfg(feature = "std")]
                let current_time_unix = Some(SystemTime::now().duration_since(UNIX_EPOCH).expect("Time must be > 1970").as_secs());
                #[cfg(not(feature = "std"))]
                let current_time_unix = None;
 
-               self.channel_failed_with_time(short_channel_id, is_permanent, current_time_unix)
+               self.channel_failed_permanent_with_time(short_channel_id, current_time_unix)
        }
 
-       /// Marks a channel in the graph as failed if a corresponding HTLC fail was sent.
-       /// If permanent, removes a channel from the local storage.
-       /// May cause the removal of nodes too, if this was their last channel.
-       /// If not permanent, makes channels unavailable for routing.
-       fn channel_failed_with_time(&self, short_channel_id: u64, is_permanent: bool, current_time_unix: Option<u64>) {
+       /// Marks a channel in the graph as failed permanently.
+       ///
+       /// The channel and any node for which this was their last channel are removed from the graph.
+       fn channel_failed_permanent_with_time(&self, short_channel_id: u64, current_time_unix: Option<u64>) {
                let mut channels = self.channels.write().unwrap();
-               if is_permanent {
-                       if let Some(chan) = channels.remove(&short_channel_id) {
-                               let mut nodes = self.nodes.write().unwrap();
-                               self.removed_channels.lock().unwrap().insert(short_channel_id, current_time_unix);
-                               Self::remove_channel_in_nodes(&mut nodes, &chan, short_channel_id);
-                       }
-               } else {
-                       if let Some(chan) = channels.get_mut(&short_channel_id) {
-                               if let Some(one_to_two) = chan.one_to_two.as_mut() {
-                                       one_to_two.enabled = false;
-                               }
-                               if let Some(two_to_one) = chan.two_to_one.as_mut() {
-                                       two_to_one.enabled = false;
-                               }
-                       }
+               if let Some(chan) = channels.remove(&short_channel_id) {
+                       let mut nodes = self.nodes.write().unwrap();
+                       self.removed_channels.lock().unwrap().insert(short_channel_id, current_time_unix);
+                       Self::remove_channel_in_nodes(&mut nodes, &chan, short_channel_id);
                }
        }
 
@@ -1787,6 +1858,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
@@ -2029,7 +2107,7 @@ pub(crate) mod tests {
                        timestamp: 100,
                        node_id,
                        rgb: [0; 3],
-                       alias: [0; 32],
+                       alias: NodeAlias([0; 32]),
                        addresses: Vec::new(),
                        excess_address_data: Vec::new(),
                        excess_data: Vec::new(),
@@ -2279,6 +2357,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]
@@ -2383,6 +2471,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]
@@ -2419,7 +2518,7 @@ pub(crate) mod tests {
                        assert!(network_graph.read_only().channels().get(&short_channel_id).unwrap().one_to_two.is_some());
                }
 
-               // Non-permanent closing just disables a channel
+               // Non-permanent failure doesn't touch the channel at all
                {
                        match network_graph.read_only().channels().get(&short_channel_id) {
                                None => panic!(),
@@ -2436,7 +2535,7 @@ pub(crate) mod tests {
                        match network_graph.read_only().channels().get(&short_channel_id) {
                                None => panic!(),
                                Some(channel_info) => {
-                                       assert!(!channel_info.one_to_two.as_ref().unwrap().enabled);
+                                       assert!(channel_info.one_to_two.as_ref().unwrap().enabled);
                                }
                        };
                }
@@ -2570,7 +2669,7 @@ pub(crate) mod tests {
 
                        // Mark the channel as permanently failed. This will also remove the two nodes
                        // and all of the entries will be tracked as removed.
-                       network_graph.channel_failed_with_time(short_channel_id, true, Some(tracking_time));
+                       network_graph.channel_failed_permanent_with_time(short_channel_id, Some(tracking_time));
 
                        // Should not remove from tracking if insufficient time has passed
                        network_graph.remove_stale_channels_and_tracking_with_time(
@@ -2603,7 +2702,7 @@ pub(crate) mod tests {
 
                        // Mark the channel as permanently failed. This will also remove the two nodes
                        // and all of the entries will be tracked as removed.
-                       network_graph.channel_failed(short_channel_id, true);
+                       network_graph.channel_failed_permanent(short_channel_id);
 
                        // The first time we call the following, the channel will have a removal time assigned.
                        network_graph.remove_stale_channels_and_tracking_with_time(removal_time);
@@ -2818,7 +2917,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);
@@ -2828,7 +2927,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);
@@ -3321,31 +3420,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()
+               ));
        }
 }