X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Frouting%2Fgossip.rs;h=90b7bb0eddc6b00977f7c087f1935ab42d636e85;hb=b315856e686dd9e72a7ff93616f314ca6a037b56;hp=0ee9d4cf8b9cc5bf06ecea53e98ca435cb7a6c97;hpb=b0bf50fa2493e5364511eb781db9772a06e140f9;p=rust-lightning diff --git a/lightning/src/routing/gossip.rs b/lightning/src/routing/gossip.rs index 0ee9d4cf..90b7bb0e 100644 --- a/lightning/src/routing/gossip.rs +++ b/lightning/src/routing/gossip.rs @@ -10,26 +10,27 @@ //! 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; use bitcoin::blockdata::constants::genesis_block; +use crate::events::{MessageSendEvent, MessageSendEventsProvider}; use crate::ln::features::{ChannelFeatures, NodeFeatures, InitFeatures}; use crate::ln::msgs::{DecodeError, ErrorAction, Init, LightningError, RoutingMessageHandler, NetAddress, MAX_VALUE_MSAT}; 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::events::{MessageSendEvent, MessageSendEventsProvider}; use crate::util::scid_utils::{block_from_scid, scid_from_parts, MAX_SCID_BLOCK}; use crate::util::string::PrintableString; use crate::util::indexed_map::{IndexedMap, Entry as IndexedMapEntry}; @@ -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::from_slice(&self.0) + } } impl fmt::Debug for NodeId { @@ -130,6 +138,29 @@ impl Readable for NodeId { } } +impl From for NodeId { + fn from(pubkey: PublicKey) -> Self { + Self::from_pubkey(&pubkey) + } +} + +impl TryFrom for PublicKey { + type Error = secp256k1::Error; + + fn try_from(node_id: NodeId) -> Result { + node_id.as_pubkey() + } +} + +impl FromStr for NodeId { + type Err = bitcoin::hashes::hex::Error; + + fn from_str(s: &str) -> Result { + 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 where L::Target: Logger { secp_ctx: Secp256k1, @@ -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>, U: Deref, L: Deref> where U::Target: UtxoLookup, L::Target: Logger { network_graph: G, - utxo_lookup: Option, + utxo_lookup: RwLock>, #[cfg(feature = "std")] full_syncs_requested: AtomicUsize, pending_events: Mutex>, @@ -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) { - self.utxo_lookup = utxo_lookup; + pub fn add_utxo_lookup(&self, utxo_lookup: Option) { + *self.utxo_lookup.write().unwrap() = utxo_lookup; } /// Gets a reference to the underlying [`NetworkGraph`] which was provided in @@ -310,7 +341,7 @@ where U::Target: UtxoLookup, L::Target: Logger impl NetworkGraph where L::Target: Logger { /// Handles any network updates originating from [`Event`]s. /// - /// [`Event`]: crate::util::events::Event + /// [`Event`]: crate::events::Event pub fn handle_network_update(&self, network_update: &NetworkUpdate) { match *network_update { NetworkUpdate::ChannelUpdateMessage { ref msg } => { @@ -321,9 +352,10 @@ impl NetworkGraph 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 NetworkGraph 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(msg: &NodeAnnouncement, secp_ctx: &Secp256k1) -> 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(msg: &ChannelAnnouncement, secp_ctx: &Secp256k1) -> 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>, U: Deref, L: Deref> RoutingMessageHandler for P2PGossipSync 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 { - 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(reader: &mut R) -> Result { + fn read(reader: &mut R) -> Result { _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> = _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 = None; - let mut announcement_info_wrap: Option = 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 = _lowest_inbound_channel_fees; + let announcement_info_wrap: Option = 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 fmt::Display for NetworkGraph where L::Target: Logger { impl Eq for NetworkGraph where L::Target: Logger {} impl PartialEq for NetworkGraph 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 NetworkGraph 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 NetworkGraph 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 NetworkGraph 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 NetworkGraph 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 NetworkGraph 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 NetworkGraph 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) { + /// 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) { 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 NetworkGraph 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 @@ -1957,6 +2035,7 @@ impl ReadOnlyNetworkGraph<'_> { #[cfg(test)] pub(crate) mod tests { + use crate::events::{MessageSendEvent, MessageSendEventsProvider}; use crate::ln::channelmanager; use crate::ln::chan_utils::make_funding_redeemscript; #[cfg(feature = "std")] @@ -1969,7 +2048,6 @@ pub(crate) mod tests { use crate::util::config::UserConfig; use crate::util::test_utils; use crate::util::ser::{ReadableArgs, Readable, Writeable}; - use crate::util::events::{MessageSendEvent, MessageSendEventsProvider}; use crate::util::scid_utils::scid_from_parts; use crate::routing::gossip::REMOVED_ENTRIES_TRACKING_AGE_LIMIT_SECS; @@ -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() + )); } }