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;
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,
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 {
},
}
}
+
+ /// Gets the genesis hash for this network graph.
+ pub fn get_genesis_hash(&self) -> BlockHash {
+ self.genesis_hash
+ }
}
macro_rules! secp_verify_sig {
}
/// 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,
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)
}
}
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();
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);
}
}
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
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]
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]
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!(),
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);
}
};
}
// 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(
// 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);
}
}
-#[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()
+ ));
}
}