Skip to content

Commit 468814f

Browse files
committed
f enable tracking for no-std too, revert to system clock
1 parent ffc6553 commit 468814f

File tree

1 file changed

+77
-55
lines changed

1 file changed

+77
-55
lines changed

lightning/src/routing/gossip.rs

Lines changed: 77 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,6 @@ use bitcoin::hashes::hex::ToHex;
4545

4646
#[cfg(feature = "std")]
4747
use std::time::{SystemTime, UNIX_EPOCH};
48-
#[cfg(all(not(test), feature = "std"))]
49-
use std::time::Instant;
50-
#[cfg(all(test, feature = "std"))]
51-
use util::time::{Time, tests::SinceEpoch as Instant};
5248

5349
/// We remove stale channel directional info two weeks after the last update, per BOLT 7's
5450
/// suggestion.
@@ -138,18 +134,27 @@ pub struct NetworkGraph<L: Deref> where L::Target: Logger {
138134
channels: RwLock<BTreeMap<u64, ChannelInfo>>,
139135
nodes: RwLock<BTreeMap<NodeId, NodeInfo>>,
140136
// Lock order: removed_channels -> removed_nodes
141-
#[cfg(feature = "std")]
137+
//
138+
// NOTE: In the following `removed_*` maps, we use seconds since UNIX epoch to track time instead
139+
// of `std::time::Instant`s for a few reasons:
140+
// * We want it to be possible to do tracking in no-std environments where we can compare
141+
// a provided current UNIX timestamp with the time at which we started tracking.
142+
// * In the future, if we decide to persist these maps, they will already be serializable.
143+
// * Although we lose out on the platform's monotonic clock, the system clock in a std
144+
// environment should be practical over the time period we are considering (on the order of a
145+
// week).
146+
//
142147
/// Keeps track of short channel IDs for channels we have explicitly removed due to permanent
143-
/// failure so that we don't resync them from gossip. Each SCID is mapped to the instant when we
144-
/// removed it from the network graph so that we can forget we removed it after some time (by
145-
/// removing it from this map) and it can be resynced from gossip if it appears again.
146-
removed_channels: Mutex<HashMap<u64, Instant>>,
147-
#[cfg(feature = "std")]
148+
/// failure so that we don't resync them from gossip. Each SCID is mapped to the time in seconds
149+
/// since the UNIX epoch when we removed it from the network graph so that we can forget we
150+
/// removed it after some time (by removing it from this map) and it can be resynced from gossip
151+
/// if it appears again.
152+
removed_channels: Mutex<HashMap<u64, u64>>,
148153
/// Keeps track of [`NodeId`]s we have explicitly removed due to permanent failure so that we
149-
/// don't resync them from gossip. Each [`NodeId`] is mapped to the instant when we removed it
150-
/// from the network graph so that we can forget we removed it after some time (by removing it
151-
/// from this map) and it can be resynced from gossip if it appears again.
152-
removed_nodes: Mutex<HashMap<NodeId, Instant>>,
154+
/// don't resync them from gossip. Each [`NodeId`] is mapped to the time in seconds since the UNIX
155+
/// epoch when we removed it from the network graph so that we can forget we removed it after some
156+
/// time (by removing it from this map) and it can be resynced from gossip if it appears again.
157+
removed_nodes: Mutex<HashMap<NodeId, u64>>,
153158
}
154159

155160
/// A read-only view of [`NetworkGraph`].
@@ -1225,9 +1230,7 @@ impl<L: Deref> ReadableArgs<L> for NetworkGraph<L> where L::Target: Logger {
12251230
channels: RwLock::new(channels),
12261231
nodes: RwLock::new(nodes),
12271232
last_rapid_gossip_sync_timestamp: Mutex::new(last_rapid_gossip_sync_timestamp),
1228-
#[cfg(feature = "std")]
12291233
removed_nodes: Mutex::new(HashMap::new()),
1230-
#[cfg(feature = "std")]
12311234
removed_channels: Mutex::new(HashMap::new()),
12321235
})
12331236
}
@@ -1265,10 +1268,8 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
12651268
channels: RwLock::new(BTreeMap::new()),
12661269
nodes: RwLock::new(BTreeMap::new()),
12671270
last_rapid_gossip_sync_timestamp: Mutex::new(None),
1268-
#[cfg(feature = "std")]
1269-
removed_nodes: Mutex::new(HashMap::new()),
1270-
#[cfg(feature = "std")]
12711271
removed_channels: Mutex::new(HashMap::new()),
1272+
removed_nodes: Mutex::new(HashMap::new()),
12721273
}
12731274
}
12741275

@@ -1516,7 +1517,6 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
15161517
}
15171518
}
15181519

1519-
#[cfg(feature = "std")]
15201520
{
15211521
let removed_channels = self.removed_channels.lock().unwrap();
15221522
let removed_nodes = self.removed_nodes.lock().unwrap();
@@ -1578,17 +1578,33 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
15781578
self.add_channel_between_nodes(msg.short_channel_id, chan_info, utxo_value)
15791579
}
15801580

1581+
#[cfg(feature = "std")]
15811582
/// Marks a channel in the graph as failed if a corresponding HTLC fail was sent.
15821583
/// If permanent, removes a channel from the local storage.
15831584
/// May cause the removal of nodes too, if this was their last channel.
15841585
/// If not permanent, makes channels unavailable for routing.
1586+
///
1587+
/// This method is only available with the `std` feature. See
1588+
/// [`NetworkGraph::channel_failed_with_time`] for `no-std` use.
15851589
pub fn channel_failed(&self, short_channel_id: u64, is_permanent: bool) {
1590+
let current_time_unix = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time must be > 1970").as_secs();
1591+
self.channel_failed_with_time(short_channel_id, is_permanent, current_time_unix);
1592+
}
1593+
1594+
/// Marks a channel in the graph as failed if a corresponding HTLC fail was sent.
1595+
/// If permanent, removes a channel from the local storage.
1596+
/// May cause the removal of nodes too, if this was their last channel.
1597+
/// If not permanent, makes channels unavailable for routing.
1598+
///
1599+
/// This function takes the current unix time as an argument. For users with the `std` feature
1600+
/// enabled, [`NetworkGraph::channel_failed`] may be preferable.
1601+
pub fn channel_failed_with_time(&self, short_channel_id: u64, is_permanent: bool,
1602+
current_time_unix: u64) {
15861603
let mut channels = self.channels.write().unwrap();
15871604
if is_permanent {
15881605
if let Some(chan) = channels.remove(&short_channel_id) {
15891606
let mut nodes = self.nodes.write().unwrap();
1590-
#[cfg(feature = "std")]
1591-
self.removed_channels.lock().unwrap().insert(short_channel_id, Instant::now());
1607+
self.removed_channels.lock().unwrap().insert(short_channel_id, current_time_unix);
15921608
Self::remove_channel_in_nodes(&mut nodes, &chan, short_channel_id);
15931609
}
15941610
} else {
@@ -1603,40 +1619,45 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
16031619
}
16041620
}
16051621

1622+
#[cfg(feature = "std")]
16061623
/// Marks a node in the graph as permanently failed, effectively removing it and its channels
16071624
/// from local storage.
16081625
///
1609-
/// If called with the "std" feature enabled, the removed node and channels will also be tracked
1610-
/// as removed so they're not re-added on gossip re-broadcast.
1626+
/// This method is only available with the `std` feature. See
1627+
/// [`NetworkGraph::node_failed_permanent_with_time`] for `no-std` use.
16111628
pub fn node_failed_permanent(&self, node_id: &PublicKey) {
1629+
let current_time_unix = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time must be > 1970").as_secs();
1630+
self.node_failed_permanent_with_time(node_id, current_time_unix);
1631+
}
1632+
1633+
/// Marks a node in the graph as permanently failed, effectively removing it and its channels
1634+
/// from local storage.
1635+
///
1636+
/// This function takes the current unix time as an argument. For users with the `std` feature
1637+
/// enabled, [`NetworkGraph::node_failed_permanent`] may be preferable.
1638+
pub fn node_failed_permanent_with_time(&self, node_id: &PublicKey, current_time_unix: u64) {
16121639
let node_id = NodeId::from_pubkey(node_id);
16131640
let mut channels = self.channels.write().unwrap();
16141641
let mut nodes = self.nodes.write().unwrap();
1615-
#[cfg(feature = "std")]
16161642
let mut removed_channels = self.removed_channels.lock().unwrap();
1617-
#[cfg(feature = "std")]
16181643
let mut removed_nodes = self.removed_nodes.lock().unwrap();
1619-
#[cfg(feature = "std")]
1620-
let time = Instant::now();
16211644

16221645
if let Some(node) = nodes.remove(&node_id) {
16231646
for scid in node.channels.iter() {
16241647
if let Some(chan_info) = channels.remove(scid) {
16251648
let other_node_id = if node_id == chan_info.node_one { chan_info.node_two } else { chan_info.node_one };
1626-
if let BtreeEntry::Occupied(mut node_entry) = nodes.entry(other_node_id) {
1627-
node_entry.get_mut().channels.retain(|chan_id| {
1649+
if let BtreeEntry::Occupied(mut other_node_entry) = nodes.entry(other_node_id) {
1650+
other_node_entry.get_mut().channels.retain(|chan_id| {
16281651
*scid != *chan_id
16291652
});
1630-
if node_entry.get().channels.is_empty() {
1631-
node_entry.remove_entry();
1653+
if other_node_entry.get().channels.is_empty() {
1654+
other_node_entry.remove_entry();
16321655
}
16331656
}
1634-
#[cfg(feature = "std")]
1635-
removed_channels.insert(*scid, time);
1657+
removed_channels.insert(*scid, current_time_unix);
16361658
}
16371659
}
1638-
#[cfg(feature = "std")]
1639-
removed_nodes.insert(node_id, time);
1660+
removed_nodes.insert(node_id, current_time_unix);
16401661
}
16411662
}
16421663

@@ -1670,6 +1691,9 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
16701691
/// updates every two weeks, the non-normative section of BOLT 7 currently suggests that
16711692
/// pruning occur for updates which are at least two weeks old, which we implement here.
16721693
///
1694+
/// This method will also cause us to stop tracking removed nodes and channels if they have been
1695+
/// in the map for a while so that these can be resynced from gossip in the future.
1696+
///
16731697
/// This function takes the current unix time as an argument. For users with the `std` feature
16741698
/// enabled, [`NetworkGraph::remove_stale_channels_and_tracking`] may be preferable.
16751699
pub fn remove_stale_channels_and_tracking_with_time(&self, current_time_unix: u64) {
@@ -1705,11 +1729,10 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
17051729
}
17061730
}
17071731

1708-
#[cfg(feature = "std")]
1709-
{
1710-
self.removed_channels.lock().unwrap().retain(|_, time| time.elapsed().as_secs() < REMOVED_ENTRIES_TRACKING_AGE_LIMIT_SECS);
1711-
self.removed_nodes.lock().unwrap().retain(|_, time| time.elapsed().as_secs() < REMOVED_ENTRIES_TRACKING_AGE_LIMIT_SECS);
1712-
}
1732+
self.removed_channels.lock().unwrap().retain(|_, time| {
1733+
current_time_unix.saturating_sub(*time) < REMOVED_ENTRIES_TRACKING_AGE_LIMIT_SECS });
1734+
self.removed_nodes.lock().unwrap().retain(|_, time| {
1735+
current_time_unix.saturating_sub(*time) < REMOVED_ENTRIES_TRACKING_AGE_LIMIT_SECS });
17131736
}
17141737

17151738
/// For an already known (from announcement) channel, update info about one of the directions
@@ -2228,12 +2251,12 @@ mod tests {
22282251

22292252
#[cfg(feature = "std")]
22302253
{
2231-
use std::time::Duration;
2232-
use util::time::tests::SinceEpoch;
2254+
use std::time::{SystemTime, UNIX_EPOCH};
22332255

2256+
let tracking_time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time must be > 1970").as_secs();
22342257
// Mark a node as permanently failed so it's tracked as removed.
2235-
gossip_sync.network_graph().node_failed_permanent(
2236-
&PublicKey::from_secret_key(&secp_ctx, node_1_privkey));
2258+
gossip_sync.network_graph().node_failed_permanent_with_time(
2259+
&PublicKey::from_secret_key(&secp_ctx, node_1_privkey), tracking_time);
22372260

22382261
// Return error and ignore valid channel announcement if one of the nodes has been tracked as removed.
22392262
let valid_announcement = get_signed_channel_announcement(|unsigned_announcement| {
@@ -2244,9 +2267,7 @@ mod tests {
22442267
Err(e) => assert_eq!(e.err, "Channel with SCID 3 or one of its nodes was removed from our network graph recently")
22452268
}
22462269

2247-
// Advance mock time so node being tracked is stale, and then remove stale tracking.
2248-
SinceEpoch::advance(Duration::from_secs(REMOVED_ENTRIES_TRACKING_AGE_LIMIT_SECS));
2249-
gossip_sync.network_graph().remove_stale_channels_and_tracking();
2270+
gossip_sync.network_graph().remove_stale_channels_and_tracking_with_time(tracking_time + REMOVED_ENTRIES_TRACKING_AGE_LIMIT_SECS);
22502271

22512272
// The above channel announcement should be handled as per normal now.
22522273
match gossip_sync.handle_channel_announcement(&valid_announcement) {
@@ -2585,8 +2606,9 @@ mod tests {
25852606

25862607
#[cfg(feature = "std")]
25872608
{
2588-
use std::time::Duration;
2589-
use util::time::tests::SinceEpoch;
2609+
use std::time::{SystemTime, UNIX_EPOCH};
2610+
2611+
let tracking_time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time must be > 1970").as_secs();
25902612

25912613
// Clear tracked nodes and channels for clean slate
25922614
network_graph.removed_channels.lock().unwrap().clear();
@@ -2599,16 +2621,16 @@ mod tests {
25992621

26002622
// Mark the channel as permanently failed. This will also remove the two nodes
26012623
// and all of the entries will be tracked as removed.
2602-
network_graph.channel_failed(short_channel_id, true);
2624+
network_graph.channel_failed_with_time(short_channel_id, true, tracking_time);
26032625

26042626
// Should not remove from tracking if insufficient time has passed
2605-
SinceEpoch::advance(Duration::from_secs(REMOVED_ENTRIES_TRACKING_AGE_LIMIT_SECS - 1));
2606-
network_graph.remove_stale_channels_and_tracking();
2627+
network_graph.remove_stale_channels_and_tracking_with_time(
2628+
tracking_time + REMOVED_ENTRIES_TRACKING_AGE_LIMIT_SECS - 1);
26072629
assert_eq!(network_graph.removed_channels.lock().unwrap().len(), 1);
26082630

26092631
// Advance mock time so the entries are removed on next call
2610-
SinceEpoch::advance(Duration::from_secs(1));
2611-
network_graph.remove_stale_channels_and_tracking();
2632+
network_graph.remove_stale_channels_and_tracking_with_time(
2633+
tracking_time + REMOVED_ENTRIES_TRACKING_AGE_LIMIT_SECS);
26122634
assert!(network_graph.removed_channels.lock().unwrap().is_empty());
26132635
assert!(network_graph.removed_nodes.lock().unwrap().is_empty());
26142636
}

0 commit comments

Comments
 (0)