@@ -730,6 +730,11 @@ const MIN_SERIALIZATION_VERSION: u8 = 1;
730730
731731impl Writeable for NetworkGraph {
732732 fn write < W : Writer > ( & self , writer : & mut W ) -> Result < ( ) , io:: Error > {
733+ #[ cfg( feature = "std" ) ]
734+ {
735+ self . remove_stale_channels ( ) ;
736+ }
737+
733738 write_ver_prefix ! ( writer, SERIALIZATION_VERSION , MIN_SERIALIZATION_VERSION ) ;
734739
735740 self . genesis_hash . write ( writer) ?;
@@ -1040,6 +1045,63 @@ impl NetworkGraph {
10401045 }
10411046 }
10421047
1048+ #[ cfg( feature = "std" ) ]
1049+ /// Removes information about channels which we haven't heard any updates about in some time.
1050+ /// This can be used regularly to prune the network graph from channels which likely no longer
1051+ /// exist.
1052+ ///
1053+ /// While there is no formal requirement that nodes regularly re-broadcast their channel
1054+ /// updates every two weeks, the non-normative section of BOLT 7 currently suggests that
1055+ /// pruning occurrs for updates which are at least two weeks old, which we implement here.
1056+ ///
1057+ /// This method is automatically called immediately before writing the network graph via
1058+ /// [`Writeable::write`].
1059+ ///
1060+ /// This method is only available with the `std` feature. See
1061+ /// [`NetworkGraph::remove_stale_channels_with_time`] for `no-std` use.
1062+ pub fn remove_stale_channels ( & self ) {
1063+ use std:: time:: { SystemTime , UNIX_EPOCH } ;
1064+ let time = SystemTime :: now ( ) . duration_since ( UNIX_EPOCH ) . expect ( "Time must be > 1970" ) . as_secs ( ) ;
1065+ self . remove_stale_channels_with_time ( time) ;
1066+ }
1067+
1068+ /// Removes information about channels which we haven't heard any updates about in some time.
1069+ /// This can be used regularly to prune the network graph from channels which likely no longer
1070+ /// exist.
1071+ ///
1072+ /// While there is no formal requirement that nodes regularly re-broadcast their channel
1073+ /// updates every two weeks, the non-normative section of BOLT 7 currently suggests that
1074+ /// pruning occurrs for updates which are at least two weeks old, which we implement here.
1075+ ///
1076+ /// This function takes the current unix time as an argument. For users with the `std` feature
1077+ /// enabled, [`NetworkGraph::remove_stale_channels`] may be preferrable.
1078+ pub fn remove_stale_channels_with_time ( & self , current_time_unix : u64 ) {
1079+ let mut channels = self . channels . write ( ) . unwrap ( ) ;
1080+ // Time out if we haven't received an update in at least 14 days.
1081+ let min_time_unix: u32 = ( current_time_unix - 60 * 60 * 14 ) as u32 ;
1082+ // Sadly BTreeMap::retain was only stabilized in 1.53 so we can't switch to it for some
1083+ // time.
1084+ let mut scids_to_remove = Vec :: new ( ) ;
1085+ for ( scid, info) in channels. iter_mut ( ) {
1086+ if info. one_to_two . is_some ( ) && info. one_to_two . as_ref ( ) . unwrap ( ) . last_update < min_time_unix {
1087+ info. one_to_two = None ;
1088+ }
1089+ if info. two_to_one . is_some ( ) && info. two_to_one . as_ref ( ) . unwrap ( ) . last_update < min_time_unix {
1090+ info. two_to_one = None ;
1091+ }
1092+ if info. one_to_two . is_none ( ) && info. two_to_one . is_none ( ) {
1093+ scids_to_remove. push ( * scid) ;
1094+ }
1095+ }
1096+ if !scids_to_remove. is_empty ( ) {
1097+ let mut nodes = self . nodes . write ( ) . unwrap ( ) ;
1098+ for scid in scids_to_remove {
1099+ let info = channels. remove ( & scid) . expect ( "We just accessed this scid, it should be present" ) ;
1100+ Self :: remove_channel_in_nodes ( & mut nodes, & info, scid) ;
1101+ }
1102+ }
1103+ }
1104+
10431105 /// For an already known (from announcement) channel, update info about one of the directions
10441106 /// of the channel.
10451107 ///
@@ -1737,8 +1799,7 @@ mod tests {
17371799
17381800 }
17391801
1740- #[ test]
1741- fn handling_network_update ( ) {
1802+ fn do_handling_network_update ( remove_by_timeout : bool ) {
17421803 let logger = test_utils:: TestLogger :: new ( ) ;
17431804 let chain_source = Arc :: new ( test_utils:: TestChainSource :: new ( Network :: Testnet ) ) ;
17441805 let genesis_hash = genesis_block ( Network :: Testnet ) . header . block_hash ( ) ;
@@ -1857,8 +1918,13 @@ mod tests {
18571918 } ;
18581919 }
18591920
1860- // Permanent closing deletes a channel
1861- {
1921+ if remove_by_timeout {
1922+ network_graph. remove_stale_channels_with_time ( 100 + 60 * 60 * 14 ) ;
1923+ assert_eq ! ( network_graph. read_only( ) . channels( ) . len( ) , 1 ) ;
1924+ assert_eq ! ( network_graph. read_only( ) . nodes( ) . len( ) , 1 ) ;
1925+ network_graph. remove_stale_channels_with_time ( 101 + 60 * 60 * 14 ) ;
1926+ } else {
1927+ // Permanent closing deletes a channel
18621928 net_graph_msg_handler. handle_event ( & Event :: PaymentPathFailed {
18631929 payment_id : None ,
18641930 payment_hash : PaymentHash ( [ 0 ; 32 ] ) ,
@@ -1874,14 +1940,20 @@ mod tests {
18741940 error_code : None ,
18751941 error_data : None ,
18761942 } ) ;
1877-
1878- assert_eq ! ( network_graph. read_only( ) . channels( ) . len( ) , 0 ) ;
1879- // Nodes are also deleted because there are no associated channels anymore
1880- assert_eq ! ( network_graph. read_only( ) . nodes( ) . len( ) , 0 ) ;
18811943 }
1944+
1945+ assert_eq ! ( network_graph. read_only( ) . channels( ) . len( ) , 0 ) ;
1946+ // Nodes are also deleted because there are no associated channels anymore
1947+ assert_eq ! ( network_graph. read_only( ) . nodes( ) . len( ) , 0 ) ;
18821948 // TODO: Test NetworkUpdate::NodeFailure, which is not implemented yet.
18831949 }
18841950
1951+ #[ test]
1952+ fn handling_network_update ( ) {
1953+ do_handling_network_update ( true ) ;
1954+ do_handling_network_update ( false ) ;
1955+ }
1956+
18851957 #[ test]
18861958 fn getting_next_channel_announcements ( ) {
18871959 let network_graph = create_network_graph ( ) ;
0 commit comments