@@ -43,6 +43,13 @@ use sync::Mutex;
4343use core:: ops:: Deref ;
4444use bitcoin:: hashes:: hex:: ToHex ;
4545
46+ #[ cfg( feature = "std" ) ]
47+ use std:: time:: { SystemTime , UNIX_EPOCH } ;
48+
49+ /// We remove stale channel directional info two weeks after the last update, per BOLT 7's
50+ /// suggestion.
51+ const STALE_CHANNEL_UPDATE_AGE_LIMIT_SECS : u64 = 60 * 60 * 24 * 14 ;
52+
4653/// The maximum number of extra bytes which we do not understand in a gossip message before we will
4754/// refuse to relay the message.
4855const MAX_EXCESS_BYTES_FOR_RELAY : usize = 1024 ;
@@ -628,6 +635,10 @@ pub struct ChannelInfo {
628635 /// Everything else is useful only for sending out for initial routing sync.
629636 /// Not stored if contains excess data to prevent DoS.
630637 pub announcement_message : Option < ChannelAnnouncement > ,
638+ /// The timestamp when we received the announcement, if we are running with feature = "std"
639+ /// (which we can probably assume we are - no-std environments probably won't have a full
640+ /// network graph in memory!).
641+ announcement_received_time : u64 ,
631642}
632643
633644impl fmt:: Display for ChannelInfo {
@@ -640,6 +651,7 @@ impl fmt::Display for ChannelInfo {
640651
641652impl_writeable_tlv_based ! ( ChannelInfo , {
642653 ( 0 , features, required) ,
654+ ( 1 , announcement_received_time, ( default_value, 0 ) ) ,
643655 ( 2 , node_one, required) ,
644656 ( 4 , one_to_two, required) ,
645657 ( 6 , node_two, required) ,
@@ -947,6 +959,13 @@ impl NetworkGraph {
947959 } ,
948960 } ;
949961
962+ #[ allow( unused_mut, unused_assignments) ]
963+ let mut announcement_received_time = 0 ;
964+ #[ cfg( feature = "std" ) ]
965+ {
966+ announcement_received_time = SystemTime :: now ( ) . duration_since ( UNIX_EPOCH ) . expect ( "Time must be > 1970" ) . as_secs ( ) ;
967+ }
968+
950969 let chan_info = ChannelInfo {
951970 features : msg. features . clone ( ) ,
952971 node_one : NodeId :: from_pubkey ( & msg. node_id_1 ) ,
@@ -956,6 +975,7 @@ impl NetworkGraph {
956975 capacity_sats : utxo_value,
957976 announcement_message : if msg. excess_data . len ( ) <= MAX_EXCESS_BYTES_FOR_RELAY
958977 { full_msg. cloned ( ) } else { None } ,
978+ announcement_received_time,
959979 } ;
960980
961981 let mut channels = self . channels . write ( ) . unwrap ( ) ;
@@ -1040,6 +1060,67 @@ impl NetworkGraph {
10401060 }
10411061 }
10421062
1063+ #[ cfg( feature = "std" ) ]
1064+ /// Removes information about channels that we haven't heard any updates about in some time.
1065+ /// This can be used regularly to prune the network graph of channels that likely no longer
1066+ /// exist.
1067+ ///
1068+ /// While there is no formal requirement that nodes regularly re-broadcast their channel
1069+ /// updates every two weeks, the non-normative section of BOLT 7 currently suggests that
1070+ /// pruning occur for updates which are at least two weeks old, which we implement here.
1071+ ///
1072+ ///
1073+ /// This method is only available with the `std` feature. See
1074+ /// [`NetworkGraph::remove_stale_channels_with_time`] for `no-std` use.
1075+ pub fn remove_stale_channels ( & self ) {
1076+ let time = SystemTime :: now ( ) . duration_since ( UNIX_EPOCH ) . expect ( "Time must be > 1970" ) . as_secs ( ) ;
1077+ self . remove_stale_channels_with_time ( time) ;
1078+ }
1079+
1080+ /// Removes information about channels that we haven't heard any updates about in some time.
1081+ /// This can be used regularly to prune the network graph of channels that likely no longer
1082+ /// exist.
1083+ ///
1084+ /// While there is no formal requirement that nodes regularly re-broadcast their channel
1085+ /// updates every two weeks, the non-normative section of BOLT 7 currently suggests that
1086+ /// pruning occur for updates which are at least two weeks old, which we implement here.
1087+ ///
1088+ /// This function takes the current unix time as an argument. For users with the `std` feature
1089+ /// enabled, [`NetworkGraph::remove_stale_channels`] may be preferable.
1090+ pub fn remove_stale_channels_with_time ( & self , current_time_unix : u64 ) {
1091+ let mut channels = self . channels . write ( ) . unwrap ( ) ;
1092+ // Time out if we haven't received an update in at least 14 days.
1093+ if current_time_unix > u32:: max_value ( ) as u64 { return ; } // Remove by 2106
1094+ if current_time_unix < STALE_CHANNEL_UPDATE_AGE_LIMIT_SECS { return ; }
1095+ let min_time_unix: u32 = ( current_time_unix - STALE_CHANNEL_UPDATE_AGE_LIMIT_SECS ) as u32 ;
1096+ // Sadly BTreeMap::retain was only stabilized in 1.53 so we can't switch to it for some
1097+ // time.
1098+ let mut scids_to_remove = Vec :: new ( ) ;
1099+ for ( scid, info) in channels. iter_mut ( ) {
1100+ if info. one_to_two . is_some ( ) && info. one_to_two . as_ref ( ) . unwrap ( ) . last_update < min_time_unix {
1101+ info. one_to_two = None ;
1102+ }
1103+ if info. two_to_one . is_some ( ) && info. two_to_one . as_ref ( ) . unwrap ( ) . last_update < min_time_unix {
1104+ info. two_to_one = None ;
1105+ }
1106+ if info. one_to_two . is_none ( ) && info. two_to_one . is_none ( ) {
1107+ // We check the announcement_received_time here to ensure we don't drop
1108+ // announcements that we just received and are just waiting for our peer to send a
1109+ // channel_update for.
1110+ if info. announcement_received_time < min_time_unix as u64 {
1111+ scids_to_remove. push ( * scid) ;
1112+ }
1113+ }
1114+ }
1115+ if !scids_to_remove. is_empty ( ) {
1116+ let mut nodes = self . nodes . write ( ) . unwrap ( ) ;
1117+ for scid in scids_to_remove {
1118+ let info = channels. remove ( & scid) . expect ( "We just accessed this scid, it should be present" ) ;
1119+ Self :: remove_channel_in_nodes ( & mut nodes, & info, scid) ;
1120+ }
1121+ }
1122+ }
1123+
10431124 /// For an already known (from announcement) channel, update info about one of the directions
10441125 /// of the channel.
10451126 ///
@@ -1237,6 +1318,8 @@ mod tests {
12371318 use util:: events:: { Event , EventHandler , MessageSendEvent , MessageSendEventsProvider } ;
12381319 use util:: scid_utils:: scid_from_parts;
12391320
1321+ use super :: STALE_CHANNEL_UPDATE_AGE_LIMIT_SECS ;
1322+
12401323 use bitcoin:: hashes:: sha256d:: Hash as Sha256dHash ;
12411324 use bitcoin:: hashes:: Hash ;
12421325 use bitcoin:: network:: constants:: Network ;
@@ -1720,28 +1803,73 @@ mod tests {
17201803 }
17211804
17221805 // Permanent closing deletes a channel
1806+ net_graph_msg_handler. handle_event ( & Event :: PaymentPathFailed {
1807+ payment_id : None ,
1808+ payment_hash : PaymentHash ( [ 0 ; 32 ] ) ,
1809+ rejected_by_dest : false ,
1810+ all_paths_failed : true ,
1811+ path : vec ! [ ] ,
1812+ network_update : Some ( NetworkUpdate :: ChannelClosed {
1813+ short_channel_id,
1814+ is_permanent : true ,
1815+ } ) ,
1816+ short_channel_id : None ,
1817+ retry : None ,
1818+ error_code : None ,
1819+ error_data : None ,
1820+ } ) ;
1821+
1822+ assert_eq ! ( network_graph. read_only( ) . channels( ) . len( ) , 0 ) ;
1823+ // Nodes are also deleted because there are no associated channels anymore
1824+ assert_eq ! ( network_graph. read_only( ) . nodes( ) . len( ) , 0 ) ;
1825+ // TODO: Test NetworkUpdate::NodeFailure, which is not implemented yet.
1826+ }
1827+
1828+ #[ test]
1829+ fn test_channel_timeouts ( ) {
1830+ // Test the removal of channels with `remove_stale_channels`.
1831+ let logger = test_utils:: TestLogger :: new ( ) ;
1832+ let chain_source = Arc :: new ( test_utils:: TestChainSource :: new ( Network :: Testnet ) ) ;
1833+ let genesis_hash = genesis_block ( Network :: Testnet ) . header . block_hash ( ) ;
1834+ let network_graph = NetworkGraph :: new ( genesis_hash) ;
1835+ let net_graph_msg_handler = NetGraphMsgHandler :: new ( & network_graph, Some ( chain_source. clone ( ) ) , & logger) ;
1836+ let secp_ctx = Secp256k1 :: new ( ) ;
1837+
1838+ let node_1_privkey = & SecretKey :: from_slice ( & [ 42 ; 32 ] ) . unwrap ( ) ;
1839+ let node_2_privkey = & SecretKey :: from_slice ( & [ 41 ; 32 ] ) . unwrap ( ) ;
1840+
1841+ let valid_channel_announcement = get_signed_channel_announcement ( |_| { } , node_1_privkey, node_2_privkey, & secp_ctx) ;
1842+ let short_channel_id = valid_channel_announcement. contents . short_channel_id ;
1843+ let chain_source: Option < & test_utils:: TestChainSource > = None ;
1844+ assert ! ( network_graph. update_channel_from_announcement( & valid_channel_announcement, & chain_source, & secp_ctx) . is_ok( ) ) ;
1845+ assert ! ( network_graph. read_only( ) . channels( ) . get( & short_channel_id) . is_some( ) ) ;
1846+
1847+ let valid_channel_update = get_signed_channel_update ( |_| { } , node_1_privkey, & secp_ctx) ;
1848+ assert ! ( net_graph_msg_handler. handle_channel_update( & valid_channel_update) . is_ok( ) ) ;
1849+ assert ! ( network_graph. read_only( ) . channels( ) . get( & short_channel_id) . unwrap( ) . one_to_two. is_some( ) ) ;
1850+
1851+ network_graph. remove_stale_channels_with_time ( 100 + STALE_CHANNEL_UPDATE_AGE_LIMIT_SECS ) ;
1852+ assert_eq ! ( network_graph. read_only( ) . channels( ) . len( ) , 1 ) ;
1853+ assert_eq ! ( network_graph. read_only( ) . nodes( ) . len( ) , 2 ) ;
1854+
1855+ network_graph. remove_stale_channels_with_time ( 101 + STALE_CHANNEL_UPDATE_AGE_LIMIT_SECS ) ;
1856+ #[ cfg( feature = "std" ) ]
17231857 {
1724- net_graph_msg_handler. handle_event ( & Event :: PaymentPathFailed {
1725- payment_id : None ,
1726- payment_hash : PaymentHash ( [ 0 ; 32 ] ) ,
1727- rejected_by_dest : false ,
1728- all_paths_failed : true ,
1729- path : vec ! [ ] ,
1730- network_update : Some ( NetworkUpdate :: ChannelClosed {
1731- short_channel_id,
1732- is_permanent : true ,
1733- } ) ,
1734- short_channel_id : None ,
1735- retry : None ,
1736- error_code : None ,
1737- error_data : None ,
1738- } ) ;
1858+ // In std mode, a further check is performed before fully removing the channel -
1859+ // the channel_announcement must have been received at least two weeks ago. We
1860+ // fudge that here by indicating the time has jumped two weeks. Note that the
1861+ // directional channel information will have been removed already..
1862+ assert_eq ! ( network_graph. read_only( ) . channels( ) . len( ) , 1 ) ;
1863+ assert_eq ! ( network_graph. read_only( ) . nodes( ) . len( ) , 2 ) ;
1864+ assert ! ( network_graph. read_only( ) . channels( ) . get( & short_channel_id) . unwrap( ) . one_to_two. is_none( ) ) ;
17391865
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 ) ;
1866+ use std :: time :: { SystemTime , UNIX_EPOCH } ;
1867+ let announcement_time = SystemTime :: now ( ) . duration_since ( UNIX_EPOCH ) . expect ( "Time must be > 1970" ) . as_secs ( ) ;
1868+ network_graph. remove_stale_channels_with_time ( announcement_time + 1 + STALE_CHANNEL_UPDATE_AGE_LIMIT_SECS ) ;
17431869 }
1744- // TODO: Test NetworkUpdate::NodeFailure, which is not implemented yet.
1870+
1871+ assert_eq ! ( network_graph. read_only( ) . channels( ) . len( ) , 0 ) ;
1872+ assert_eq ! ( network_graph. read_only( ) . nodes( ) . len( ) , 0 ) ;
17451873 }
17461874
17471875 #[ test]
0 commit comments