@@ -45,10 +45,6 @@ use bitcoin::hashes::hex::ToHex;
45
45
46
46
#[ cfg( feature = "std" ) ]
47
47
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 } ;
52
48
53
49
/// We remove stale channel directional info two weeks after the last update, per BOLT 7's
54
50
/// suggestion.
@@ -138,18 +134,27 @@ pub struct NetworkGraph<L: Deref> where L::Target: Logger {
138
134
channels : RwLock < BTreeMap < u64 , ChannelInfo > > ,
139
135
nodes : RwLock < BTreeMap < NodeId , NodeInfo > > ,
140
136
// 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
+ //
142
147
/// 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 > > ,
148
153
/// 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 > > ,
153
158
}
154
159
155
160
/// A read-only view of [`NetworkGraph`].
@@ -1225,9 +1230,7 @@ impl<L: Deref> ReadableArgs<L> for NetworkGraph<L> where L::Target: Logger {
1225
1230
channels : RwLock :: new ( channels) ,
1226
1231
nodes : RwLock :: new ( nodes) ,
1227
1232
last_rapid_gossip_sync_timestamp : Mutex :: new ( last_rapid_gossip_sync_timestamp) ,
1228
- #[ cfg( feature = "std" ) ]
1229
1233
removed_nodes : Mutex :: new ( HashMap :: new ( ) ) ,
1230
- #[ cfg( feature = "std" ) ]
1231
1234
removed_channels : Mutex :: new ( HashMap :: new ( ) ) ,
1232
1235
} )
1233
1236
}
@@ -1265,10 +1268,8 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
1265
1268
channels : RwLock :: new ( BTreeMap :: new ( ) ) ,
1266
1269
nodes : RwLock :: new ( BTreeMap :: new ( ) ) ,
1267
1270
last_rapid_gossip_sync_timestamp : Mutex :: new ( None ) ,
1268
- #[ cfg( feature = "std" ) ]
1269
- removed_nodes : Mutex :: new ( HashMap :: new ( ) ) ,
1270
- #[ cfg( feature = "std" ) ]
1271
1271
removed_channels : Mutex :: new ( HashMap :: new ( ) ) ,
1272
+ removed_nodes : Mutex :: new ( HashMap :: new ( ) ) ,
1272
1273
}
1273
1274
}
1274
1275
@@ -1516,7 +1517,6 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
1516
1517
}
1517
1518
}
1518
1519
1519
- #[ cfg( feature = "std" ) ]
1520
1520
{
1521
1521
let removed_channels = self . removed_channels . lock ( ) . unwrap ( ) ;
1522
1522
let removed_nodes = self . removed_nodes . lock ( ) . unwrap ( ) ;
@@ -1578,17 +1578,33 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
1578
1578
self . add_channel_between_nodes ( msg. short_channel_id , chan_info, utxo_value)
1579
1579
}
1580
1580
1581
+ #[ cfg( feature = "std" ) ]
1581
1582
/// Marks a channel in the graph as failed if a corresponding HTLC fail was sent.
1582
1583
/// If permanent, removes a channel from the local storage.
1583
1584
/// May cause the removal of nodes too, if this was their last channel.
1584
1585
/// 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.
1585
1589
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 ) {
1586
1603
let mut channels = self . channels . write ( ) . unwrap ( ) ;
1587
1604
if is_permanent {
1588
1605
if let Some ( chan) = channels. remove ( & short_channel_id) {
1589
1606
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) ;
1592
1608
Self :: remove_channel_in_nodes ( & mut nodes, & chan, short_channel_id) ;
1593
1609
}
1594
1610
} else {
@@ -1603,40 +1619,45 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
1603
1619
}
1604
1620
}
1605
1621
1622
+ #[ cfg( feature = "std" ) ]
1606
1623
/// Marks a node in the graph as permanently failed, effectively removing it and its channels
1607
1624
/// from local storage.
1608
1625
///
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 .
1611
1628
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 ) {
1612
1639
let node_id = NodeId :: from_pubkey ( node_id) ;
1613
1640
let mut channels = self . channels . write ( ) . unwrap ( ) ;
1614
1641
let mut nodes = self . nodes . write ( ) . unwrap ( ) ;
1615
- #[ cfg( feature = "std" ) ]
1616
1642
let mut removed_channels = self . removed_channels . lock ( ) . unwrap ( ) ;
1617
- #[ cfg( feature = "std" ) ]
1618
1643
let mut removed_nodes = self . removed_nodes . lock ( ) . unwrap ( ) ;
1619
- #[ cfg( feature = "std" ) ]
1620
- let time = Instant :: now ( ) ;
1621
1644
1622
1645
if let Some ( node) = nodes. remove ( & node_id) {
1623
1646
for scid in node. channels . iter ( ) {
1624
1647
if let Some ( chan_info) = channels. remove ( scid) {
1625
1648
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| {
1628
1651
* scid != * chan_id
1629
1652
} ) ;
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 ( ) ;
1632
1655
}
1633
1656
}
1634
- #[ cfg( feature = "std" ) ]
1635
- removed_channels. insert ( * scid, time) ;
1657
+ removed_channels. insert ( * scid, current_time_unix) ;
1636
1658
}
1637
1659
}
1638
- #[ cfg( feature = "std" ) ]
1639
- removed_nodes. insert ( node_id, time) ;
1660
+ removed_nodes. insert ( node_id, current_time_unix) ;
1640
1661
}
1641
1662
}
1642
1663
@@ -1670,6 +1691,9 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
1670
1691
/// updates every two weeks, the non-normative section of BOLT 7 currently suggests that
1671
1692
/// pruning occur for updates which are at least two weeks old, which we implement here.
1672
1693
///
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
+ ///
1673
1697
/// This function takes the current unix time as an argument. For users with the `std` feature
1674
1698
/// enabled, [`NetworkGraph::remove_stale_channels_and_tracking`] may be preferable.
1675
1699
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 {
1705
1729
}
1706
1730
}
1707
1731
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 } ) ;
1713
1736
}
1714
1737
1715
1738
/// For an already known (from announcement) channel, update info about one of the directions
@@ -2228,12 +2251,12 @@ mod tests {
2228
2251
2229
2252
#[ cfg( feature = "std" ) ]
2230
2253
{
2231
- use std:: time:: Duration ;
2232
- use util:: time:: tests:: SinceEpoch ;
2254
+ use std:: time:: { SystemTime , UNIX_EPOCH } ;
2233
2255
2256
+ let tracking_time = SystemTime :: now ( ) . duration_since ( UNIX_EPOCH ) . expect ( "Time must be > 1970" ) . as_secs ( ) ;
2234
2257
// 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 ) ;
2237
2260
2238
2261
// Return error and ignore valid channel announcement if one of the nodes has been tracked as removed.
2239
2262
let valid_announcement = get_signed_channel_announcement ( |unsigned_announcement| {
@@ -2244,9 +2267,7 @@ mod tests {
2244
2267
Err ( e) => assert_eq ! ( e. err, "Channel with SCID 3 or one of its nodes was removed from our network graph recently" )
2245
2268
}
2246
2269
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 ) ;
2250
2271
2251
2272
// The above channel announcement should be handled as per normal now.
2252
2273
match gossip_sync. handle_channel_announcement ( & valid_announcement) {
@@ -2585,8 +2606,9 @@ mod tests {
2585
2606
2586
2607
#[ cfg( feature = "std" ) ]
2587
2608
{
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 ( ) ;
2590
2612
2591
2613
// Clear tracked nodes and channels for clean slate
2592
2614
network_graph. removed_channels . lock ( ) . unwrap ( ) . clear ( ) ;
@@ -2599,16 +2621,16 @@ mod tests {
2599
2621
2600
2622
// Mark the channel as permanently failed. This will also remove the two nodes
2601
2623
// 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 ) ;
2603
2625
2604
2626
// 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 ) ;
2607
2629
assert_eq ! ( network_graph. removed_channels. lock( ) . unwrap( ) . len( ) , 1 ) ;
2608
2630
2609
2631
// 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 ) ;
2612
2634
assert ! ( network_graph. removed_channels. lock( ) . unwrap( ) . is_empty( ) ) ;
2613
2635
assert ! ( network_graph. removed_nodes. lock( ) . unwrap( ) . is_empty( ) ) ;
2614
2636
}
0 commit comments