@@ -730,6 +730,11 @@ const MIN_SERIALIZATION_VERSION: u8 = 1;
730
730
731
731
impl Writeable for NetworkGraph {
732
732
fn write < W : Writer > ( & self , writer : & mut W ) -> Result < ( ) , io:: Error > {
733
+ #[ cfg( feature = "std" ) ]
734
+ {
735
+ self . remove_stale_channels ( ) ;
736
+ }
737
+
733
738
write_ver_prefix ! ( writer, SERIALIZATION_VERSION , MIN_SERIALIZATION_VERSION ) ;
734
739
735
740
self . genesis_hash . write ( writer) ?;
@@ -1040,6 +1045,63 @@ impl NetworkGraph {
1040
1045
}
1041
1046
}
1042
1047
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
+
1043
1105
/// For an already known (from announcement) channel, update info about one of the directions
1044
1106
/// of the channel.
1045
1107
///
@@ -1639,8 +1701,7 @@ mod tests {
1639
1701
} ;
1640
1702
}
1641
1703
1642
- #[ test]
1643
- fn handling_network_update ( ) {
1704
+ fn do_handling_network_update ( remove_by_timeout : bool ) {
1644
1705
let logger = test_utils:: TestLogger :: new ( ) ;
1645
1706
let chain_source = Arc :: new ( test_utils:: TestChainSource :: new ( Network :: Testnet ) ) ;
1646
1707
let genesis_hash = genesis_block ( Network :: Testnet ) . header . block_hash ( ) ;
@@ -1719,8 +1780,13 @@ mod tests {
1719
1780
} ;
1720
1781
}
1721
1782
1722
- // Permanent closing deletes a channel
1723
- {
1783
+ if remove_by_timeout {
1784
+ network_graph. remove_stale_channels_with_time ( 100 + 60 * 60 * 14 ) ;
1785
+ assert_eq ! ( network_graph. read_only( ) . channels( ) . len( ) , 1 ) ;
1786
+ assert_eq ! ( network_graph. read_only( ) . nodes( ) . len( ) , 2 ) ;
1787
+ network_graph. remove_stale_channels_with_time ( 101 + 60 * 60 * 14 ) ;
1788
+ } else {
1789
+ // Permanent closing deletes a channel
1724
1790
net_graph_msg_handler. handle_event ( & Event :: PaymentPathFailed {
1725
1791
payment_id : None ,
1726
1792
payment_hash : PaymentHash ( [ 0 ; 32 ] ) ,
@@ -1736,14 +1802,20 @@ mod tests {
1736
1802
error_code : None ,
1737
1803
error_data : None ,
1738
1804
} ) ;
1739
-
1740
- assert_eq ! ( network_graph. read_only( ) . channels( ) . len( ) , 0 ) ;
1741
- // Nodes are also deleted because there are no associated channels anymore
1742
- assert_eq ! ( network_graph. read_only( ) . nodes( ) . len( ) , 0 ) ;
1743
1805
}
1806
+
1807
+ assert_eq ! ( network_graph. read_only( ) . channels( ) . len( ) , 0 ) ;
1808
+ // Nodes are also deleted because there are no associated channels anymore
1809
+ assert_eq ! ( network_graph. read_only( ) . nodes( ) . len( ) , 0 ) ;
1744
1810
// TODO: Test NetworkUpdate::NodeFailure, which is not implemented yet.
1745
1811
}
1746
1812
1813
+ #[ test]
1814
+ fn handling_network_update ( ) {
1815
+ do_handling_network_update ( true ) ;
1816
+ do_handling_network_update ( false ) ;
1817
+ }
1818
+
1747
1819
#[ test]
1748
1820
fn getting_next_channel_announcements ( ) {
1749
1821
let network_graph = create_network_graph ( ) ;
0 commit comments