@@ -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) ,
@@ -952,6 +964,13 @@ impl NetworkGraph {
952964 } ,
953965 } ;
954966
967+ #[ allow( unused_mut, unused_assignments) ]
968+ let mut announcement_received_time = 0 ;
969+ #[ cfg( feature = "std" ) ]
970+ {
971+ announcement_received_time = SystemTime :: now ( ) . duration_since ( UNIX_EPOCH ) . expect ( "Time must be > 1970" ) . as_secs ( ) ;
972+ }
973+
955974 let chan_info = ChannelInfo {
956975 features : msg. features . clone ( ) ,
957976 node_one : NodeId :: from_pubkey ( & msg. node_id_1 ) ,
@@ -961,6 +980,7 @@ impl NetworkGraph {
961980 capacity_sats : utxo_value,
962981 announcement_message : if msg. excess_data . len ( ) <= MAX_EXCESS_BYTES_FOR_RELAY
963982 { full_msg. cloned ( ) } else { None } ,
983+ announcement_received_time,
964984 } ;
965985
966986 let mut channels = self . channels . write ( ) . unwrap ( ) ;
@@ -1045,6 +1065,67 @@ impl NetworkGraph {
10451065 }
10461066 }
10471067
1068+ #[ cfg( feature = "std" ) ]
1069+ /// Removes information about channels that we haven't heard any updates about in some time.
1070+ /// This can be used regularly to prune the network graph of channels that likely no longer
1071+ /// exist.
1072+ ///
1073+ /// While there is no formal requirement that nodes regularly re-broadcast their channel
1074+ /// updates every two weeks, the non-normative section of BOLT 7 currently suggests that
1075+ /// pruning occur for updates which are at least two weeks old, which we implement here.
1076+ ///
1077+ ///
1078+ /// This method is only available with the `std` feature. See
1079+ /// [`NetworkGraph::remove_stale_channels_with_time`] for `no-std` use.
1080+ pub fn remove_stale_channels ( & self ) {
1081+ let time = SystemTime :: now ( ) . duration_since ( UNIX_EPOCH ) . expect ( "Time must be > 1970" ) . as_secs ( ) ;
1082+ self . remove_stale_channels_with_time ( time) ;
1083+ }
1084+
1085+ /// Removes information about channels that we haven't heard any updates about in some time.
1086+ /// This can be used regularly to prune the network graph of channels that likely no longer
1087+ /// exist.
1088+ ///
1089+ /// While there is no formal requirement that nodes regularly re-broadcast their channel
1090+ /// updates every two weeks, the non-normative section of BOLT 7 currently suggests that
1091+ /// pruning occur for updates which are at least two weeks old, which we implement here.
1092+ ///
1093+ /// This function takes the current unix time as an argument. For users with the `std` feature
1094+ /// enabled, [`NetworkGraph::remove_stale_channels`] may be preferable.
1095+ pub fn remove_stale_channels_with_time ( & self , current_time_unix : u64 ) {
1096+ let mut channels = self . channels . write ( ) . unwrap ( ) ;
1097+ // Time out if we haven't received an update in at least 14 days.
1098+ if current_time_unix > u32:: max_value ( ) as u64 { return ; } // Remove by 2106
1099+ if current_time_unix < STALE_CHANNEL_UPDATE_AGE_LIMIT_SECS { return ; }
1100+ let min_time_unix: u32 = ( current_time_unix - STALE_CHANNEL_UPDATE_AGE_LIMIT_SECS ) as u32 ;
1101+ // Sadly BTreeMap::retain was only stabilized in 1.53 so we can't switch to it for some
1102+ // time.
1103+ let mut scids_to_remove = Vec :: new ( ) ;
1104+ for ( scid, info) in channels. iter_mut ( ) {
1105+ if info. one_to_two . is_some ( ) && info. one_to_two . as_ref ( ) . unwrap ( ) . last_update < min_time_unix {
1106+ info. one_to_two = None ;
1107+ }
1108+ if info. two_to_one . is_some ( ) && info. two_to_one . as_ref ( ) . unwrap ( ) . last_update < min_time_unix {
1109+ info. two_to_one = None ;
1110+ }
1111+ if info. one_to_two . is_none ( ) && info. two_to_one . is_none ( ) {
1112+ // We check the announcement_received_time here to ensure we don't drop
1113+ // announcements that we just received and are just waiting for our peer to send a
1114+ // channel_update for.
1115+ if info. announcement_received_time < min_time_unix as u64 {
1116+ scids_to_remove. push ( * scid) ;
1117+ }
1118+ }
1119+ }
1120+ if !scids_to_remove. is_empty ( ) {
1121+ let mut nodes = self . nodes . write ( ) . unwrap ( ) ;
1122+ for scid in scids_to_remove {
1123+ let info = channels. remove ( & scid) . expect ( "We just accessed this scid, it should be present" ) ;
1124+ Self :: remove_channel_in_nodes ( & mut nodes, & info, scid) ;
1125+ }
1126+ }
1127+ }
1128+
10481129 /// For an already known (from announcement) channel, update info about one of the directions
10491130 /// of the channel.
10501131 ///
@@ -1250,6 +1331,8 @@ mod tests {
12501331 use util:: events:: { Event , EventHandler , MessageSendEvent , MessageSendEventsProvider } ;
12511332 use util:: scid_utils:: scid_from_parts;
12521333
1334+ use super :: STALE_CHANNEL_UPDATE_AGE_LIMIT_SECS ;
1335+
12531336 use bitcoin:: hashes:: sha256d:: Hash as Sha256dHash ;
12541337 use bitcoin:: hashes:: Hash ;
12551338 use bitcoin:: network:: constants:: Network ;
@@ -1733,28 +1816,73 @@ mod tests {
17331816 }
17341817
17351818 // Permanent closing deletes a channel
1819+ net_graph_msg_handler. handle_event ( & Event :: PaymentPathFailed {
1820+ payment_id : None ,
1821+ payment_hash : PaymentHash ( [ 0 ; 32 ] ) ,
1822+ rejected_by_dest : false ,
1823+ all_paths_failed : true ,
1824+ path : vec ! [ ] ,
1825+ network_update : Some ( NetworkUpdate :: ChannelClosed {
1826+ short_channel_id,
1827+ is_permanent : true ,
1828+ } ) ,
1829+ short_channel_id : None ,
1830+ retry : None ,
1831+ error_code : None ,
1832+ error_data : None ,
1833+ } ) ;
1834+
1835+ assert_eq ! ( network_graph. read_only( ) . channels( ) . len( ) , 0 ) ;
1836+ // Nodes are also deleted because there are no associated channels anymore
1837+ assert_eq ! ( network_graph. read_only( ) . nodes( ) . len( ) , 0 ) ;
1838+ // TODO: Test NetworkUpdate::NodeFailure, which is not implemented yet.
1839+ }
1840+
1841+ #[ test]
1842+ fn test_channel_timeouts ( ) {
1843+ // Test the removal of channels with `remove_stale_channels`.
1844+ let logger = test_utils:: TestLogger :: new ( ) ;
1845+ let chain_source = Arc :: new ( test_utils:: TestChainSource :: new ( Network :: Testnet ) ) ;
1846+ let genesis_hash = genesis_block ( Network :: Testnet ) . header . block_hash ( ) ;
1847+ let network_graph = NetworkGraph :: new ( genesis_hash) ;
1848+ let net_graph_msg_handler = NetGraphMsgHandler :: new ( & network_graph, Some ( chain_source. clone ( ) ) , & logger) ;
1849+ let secp_ctx = Secp256k1 :: new ( ) ;
1850+
1851+ let node_1_privkey = & SecretKey :: from_slice ( & [ 42 ; 32 ] ) . unwrap ( ) ;
1852+ let node_2_privkey = & SecretKey :: from_slice ( & [ 41 ; 32 ] ) . unwrap ( ) ;
1853+
1854+ let valid_channel_announcement = get_signed_channel_announcement ( |_| { } , node_1_privkey, node_2_privkey, & secp_ctx) ;
1855+ let short_channel_id = valid_channel_announcement. contents . short_channel_id ;
1856+ let chain_source: Option < & test_utils:: TestChainSource > = None ;
1857+ assert ! ( network_graph. update_channel_from_announcement( & valid_channel_announcement, & chain_source, & secp_ctx) . is_ok( ) ) ;
1858+ assert ! ( network_graph. read_only( ) . channels( ) . get( & short_channel_id) . is_some( ) ) ;
1859+
1860+ let valid_channel_update = get_signed_channel_update ( |_| { } , node_1_privkey, & secp_ctx) ;
1861+ assert ! ( net_graph_msg_handler. handle_channel_update( & valid_channel_update) . is_ok( ) ) ;
1862+ assert ! ( network_graph. read_only( ) . channels( ) . get( & short_channel_id) . unwrap( ) . one_to_two. is_some( ) ) ;
1863+
1864+ network_graph. remove_stale_channels_with_time ( 100 + STALE_CHANNEL_UPDATE_AGE_LIMIT_SECS ) ;
1865+ assert_eq ! ( network_graph. read_only( ) . channels( ) . len( ) , 1 ) ;
1866+ assert_eq ! ( network_graph. read_only( ) . nodes( ) . len( ) , 2 ) ;
1867+
1868+ network_graph. remove_stale_channels_with_time ( 101 + STALE_CHANNEL_UPDATE_AGE_LIMIT_SECS ) ;
1869+ #[ cfg( feature = "std" ) ]
17361870 {
1737- net_graph_msg_handler. handle_event ( & Event :: PaymentPathFailed {
1738- payment_id : None ,
1739- payment_hash : PaymentHash ( [ 0 ; 32 ] ) ,
1740- rejected_by_dest : false ,
1741- all_paths_failed : true ,
1742- path : vec ! [ ] ,
1743- network_update : Some ( NetworkUpdate :: ChannelClosed {
1744- short_channel_id,
1745- is_permanent : true ,
1746- } ) ,
1747- short_channel_id : None ,
1748- retry : None ,
1749- error_code : None ,
1750- error_data : None ,
1751- } ) ;
1871+ // In std mode, a further check is performed before fully removing the channel -
1872+ // the channel_announcement must have been received at least two weeks ago. We
1873+ // fudge that here by indicating the time has jumped two weeks. Note that the
1874+ // directional channel information will have been removed already..
1875+ assert_eq ! ( network_graph. read_only( ) . channels( ) . len( ) , 1 ) ;
1876+ assert_eq ! ( network_graph. read_only( ) . nodes( ) . len( ) , 2 ) ;
1877+ assert ! ( network_graph. read_only( ) . channels( ) . get( & short_channel_id) . unwrap( ) . one_to_two. is_none( ) ) ;
17521878
1753- assert_eq ! ( network_graph . read_only ( ) . channels ( ) . len ( ) , 0 ) ;
1754- // Nodes are also deleted because there are no associated channels anymore
1755- assert_eq ! ( network_graph. read_only ( ) . nodes ( ) . len ( ) , 0 ) ;
1879+ use std :: time :: { SystemTime , UNIX_EPOCH } ;
1880+ let announcement_time = SystemTime :: now ( ) . duration_since ( UNIX_EPOCH ) . expect ( "Time must be > 1970" ) . as_secs ( ) ;
1881+ network_graph. remove_stale_channels_with_time ( announcement_time + 1 + STALE_CHANNEL_UPDATE_AGE_LIMIT_SECS ) ;
17561882 }
1757- // TODO: Test NetworkUpdate::NodeFailure, which is not implemented yet.
1883+
1884+ assert_eq ! ( network_graph. read_only( ) . channels( ) . len( ) , 0 ) ;
1885+ assert_eq ! ( network_graph. read_only( ) . nodes( ) . len( ) , 0 ) ;
17581886 }
17591887
17601888 #[ test]
0 commit comments