@@ -16,6 +16,9 @@ use lightning::io;
1616use crate :: error:: GraphSyncError ;
1717use crate :: RapidGossipSync ;
1818
19+ #[ cfg( all( feature = "std" , not( test) ) ) ]
20+ use std:: time:: { SystemTime , UNIX_EPOCH } ;
21+
1922#[ cfg( not( feature = "std" ) ) ]
2023use alloc:: { vec:: Vec , borrow:: ToOwned } ;
2124
@@ -29,10 +32,30 @@ const GOSSIP_PREFIX: [u8; 4] = [76, 68, 75, 1];
2932/// avoid malicious updates being able to trigger excessive memory allocation.
3033const MAX_INITIAL_NODE_ID_VECTOR_CAPACITY : u32 = 50_000 ;
3134
35+ /// We disallow gossip data that's more than two weeks old, per BOLT 7's
36+ /// suggestion.
37+ const STALE_RGS_UPDATE_AGE_LIMIT_SECS : u64 = 60 * 60 * 24 * 14 ;
38+
3239impl < NG : Deref < Target =NetworkGraph < L > > , L : Deref > RapidGossipSync < NG , L > where L :: Target : Logger {
3340 pub ( crate ) fn update_network_graph_from_byte_stream < R : io:: Read > (
41+ & self ,
42+ read_cursor : & mut R ,
43+ ) -> Result < u32 , GraphSyncError > {
44+ #[ allow( unused_mut) ]
45+ let mut current_time_unix = None ;
46+ #[ cfg( all( feature = "std" , not( test) ) ) ]
47+ {
48+ // Note that many tests rely on being able to set arbitrarily old timestamps, thus we
49+ // disable this check during tests!
50+ current_time_unix = Some ( SystemTime :: now ( ) . duration_since ( UNIX_EPOCH ) . expect ( "Time must be > 1970" ) . as_secs ( ) ) ;
51+ }
52+ self . update_network_graph_from_byte_stream_no_std ( read_cursor, current_time_unix)
53+ }
54+
55+ pub ( crate ) fn update_network_graph_from_byte_stream_no_std < R : io:: Read > (
3456 & self ,
3557 mut read_cursor : & mut R ,
58+ current_time_unix : Option < u64 >
3659 ) -> Result < u32 , GraphSyncError > {
3760 let mut prefix = [ 0u8 ; 4 ] ;
3861 read_cursor. read_exact ( & mut prefix) ?;
@@ -43,6 +66,13 @@ impl<NG: Deref<Target=NetworkGraph<L>>, L: Deref> RapidGossipSync<NG, L> where L
4366
4467 let chain_hash: BlockHash = Readable :: read ( read_cursor) ?;
4568 let latest_seen_timestamp: u32 = Readable :: read ( read_cursor) ?;
69+
70+ if let Some ( time) = current_time_unix {
71+ if ( latest_seen_timestamp as u64 ) < time. saturating_sub ( STALE_RGS_UPDATE_AGE_LIMIT_SECS ) {
72+ return Err ( LightningError { err : "Rapid Gossip Sync data is more than two weeks old" . to_owned ( ) , action : ErrorAction :: IgnoreError } . into ( ) ) ;
73+ }
74+ }
75+
4676 // backdate the applied timestamp by a week
4777 let backdated_timestamp = latest_seen_timestamp. saturating_sub ( 24 * 3600 * 7 ) ;
4878
@@ -215,8 +245,28 @@ mod tests {
215245 use lightning:: util:: test_utils:: TestLogger ;
216246
217247 use crate :: error:: GraphSyncError ;
248+ use crate :: processing:: STALE_RGS_UPDATE_AGE_LIMIT_SECS ;
218249 use crate :: RapidGossipSync ;
219250
251+ const VALID_RGS_BINARY : [ u8 ; 300 ] = [
252+ 76 , 68 , 75 , 1 , 111 , 226 , 140 , 10 , 182 , 241 , 179 , 114 , 193 , 166 , 162 , 70 , 174 , 99 , 247 ,
253+ 79 , 147 , 30 , 131 , 101 , 225 , 90 , 8 , 156 , 104 , 214 , 25 , 0 , 0 , 0 , 0 , 0 , 97 , 227 , 98 , 218 ,
254+ 0 , 0 , 0 , 4 , 2 , 22 , 7 , 207 , 206 , 25 , 164 , 197 , 231 , 230 , 231 , 56 , 102 , 61 , 250 , 251 ,
255+ 187 , 172 , 38 , 46 , 79 , 247 , 108 , 44 , 155 , 48 , 219 , 238 , 252 , 53 , 192 , 6 , 67 , 2 , 36 , 125 ,
256+ 157 , 176 , 223 , 175 , 234 , 116 , 94 , 248 , 201 , 225 , 97 , 235 , 50 , 47 , 115 , 172 , 63 , 136 ,
257+ 88 , 216 , 115 , 11 , 111 , 217 , 114 , 84 , 116 , 124 , 231 , 107 , 2 , 158 , 1 , 242 , 121 , 152 , 106 ,
258+ 204 , 131 , 186 , 35 , 93 , 70 , 216 , 10 , 237 , 224 , 183 , 89 , 95 , 65 , 3 , 83 , 185 , 58 , 138 ,
259+ 181 , 64 , 187 , 103 , 127 , 68 , 50 , 2 , 201 , 19 , 17 , 138 , 136 , 149 , 185 , 226 , 156 , 137 , 175 ,
260+ 110 , 32 , 237 , 0 , 217 , 90 , 31 , 100 , 228 , 149 , 46 , 219 , 175 , 168 , 77 , 4 , 143 , 38 , 128 ,
261+ 76 , 97 , 0 , 0 , 0 , 2 , 0 , 0 , 255 , 8 , 153 , 192 , 0 , 2 , 27 , 0 , 0 , 0 , 1 , 0 , 0 , 255 , 2 , 68 ,
262+ 226 , 0 , 6 , 11 , 0 , 1 , 2 , 3 , 0 , 0 , 0 , 4 , 0 , 40 , 0 , 0 , 0 , 0 , 0 , 0 , 3 , 232 , 0 , 0 , 3 , 232 ,
263+ 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 29 , 129 , 25 , 192 , 255 , 8 , 153 , 192 , 0 , 2 , 27 , 0 , 0 , 60 , 0 , 0 ,
264+ 0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 100 , 0 , 0 , 2 , 224 , 0 , 0 , 0 , 0 , 58 , 85 , 116 , 216 , 0 , 29 , 0 ,
265+ 0 , 0 , 1 , 0 , 0 , 0 , 125 , 0 , 0 , 0 , 0 , 58 , 85 , 116 , 216 , 255 , 2 , 68 , 226 , 0 , 6 , 11 , 0 , 1 ,
266+ 0 , 0 , 1 ,
267+ ] ;
268+ const VALID_BINARY_TIMESTAMP : u64 = 1642291930 ;
269+
220270 #[ test]
221271 fn network_graph_fails_to_update_from_clipped_input ( ) {
222272 let block_hash = genesis_block ( Network :: Bitcoin ) . block_hash ( ) ;
@@ -478,32 +528,14 @@ mod tests {
478528
479529 #[ test]
480530 fn full_update_succeeds ( ) {
481- let valid_input = vec ! [
482- 76 , 68 , 75 , 1 , 111 , 226 , 140 , 10 , 182 , 241 , 179 , 114 , 193 , 166 , 162 , 70 , 174 , 99 , 247 ,
483- 79 , 147 , 30 , 131 , 101 , 225 , 90 , 8 , 156 , 104 , 214 , 25 , 0 , 0 , 0 , 0 , 0 , 97 , 227 , 98 , 218 ,
484- 0 , 0 , 0 , 4 , 2 , 22 , 7 , 207 , 206 , 25 , 164 , 197 , 231 , 230 , 231 , 56 , 102 , 61 , 250 , 251 ,
485- 187 , 172 , 38 , 46 , 79 , 247 , 108 , 44 , 155 , 48 , 219 , 238 , 252 , 53 , 192 , 6 , 67 , 2 , 36 , 125 ,
486- 157 , 176 , 223 , 175 , 234 , 116 , 94 , 248 , 201 , 225 , 97 , 235 , 50 , 47 , 115 , 172 , 63 , 136 ,
487- 88 , 216 , 115 , 11 , 111 , 217 , 114 , 84 , 116 , 124 , 231 , 107 , 2 , 158 , 1 , 242 , 121 , 152 , 106 ,
488- 204 , 131 , 186 , 35 , 93 , 70 , 216 , 10 , 237 , 224 , 183 , 89 , 95 , 65 , 3 , 83 , 185 , 58 , 138 ,
489- 181 , 64 , 187 , 103 , 127 , 68 , 50 , 2 , 201 , 19 , 17 , 138 , 136 , 149 , 185 , 226 , 156 , 137 , 175 ,
490- 110 , 32 , 237 , 0 , 217 , 90 , 31 , 100 , 228 , 149 , 46 , 219 , 175 , 168 , 77 , 4 , 143 , 38 , 128 ,
491- 76 , 97 , 0 , 0 , 0 , 2 , 0 , 0 , 255 , 8 , 153 , 192 , 0 , 2 , 27 , 0 , 0 , 0 , 1 , 0 , 0 , 255 , 2 , 68 ,
492- 226 , 0 , 6 , 11 , 0 , 1 , 2 , 3 , 0 , 0 , 0 , 4 , 0 , 40 , 0 , 0 , 0 , 0 , 0 , 0 , 3 , 232 , 0 , 0 , 3 , 232 ,
493- 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 29 , 129 , 25 , 192 , 255 , 8 , 153 , 192 , 0 , 2 , 27 , 0 , 0 , 60 , 0 , 0 ,
494- 0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 100 , 0 , 0 , 2 , 224 , 0 , 0 , 0 , 0 , 58 , 85 , 116 , 216 , 0 , 29 , 0 ,
495- 0 , 0 , 1 , 0 , 0 , 0 , 125 , 0 , 0 , 0 , 0 , 58 , 85 , 116 , 216 , 255 , 2 , 68 , 226 , 0 , 6 , 11 , 0 , 1 ,
496- 0 , 0 , 1 ,
497- ] ;
498-
499531 let block_hash = genesis_block ( Network :: Bitcoin ) . block_hash ( ) ;
500532 let logger = TestLogger :: new ( ) ;
501533 let network_graph = NetworkGraph :: new ( block_hash, & logger) ;
502534
503535 assert_eq ! ( network_graph. read_only( ) . channels( ) . len( ) , 0 ) ;
504536
505537 let rapid_sync = RapidGossipSync :: new ( & network_graph) ;
506- let update_result = rapid_sync. update_network_graph ( & valid_input [ .. ] ) ;
538+ let update_result = rapid_sync. update_network_graph ( & VALID_RGS_BINARY ) ;
507539 if update_result. is_err ( ) {
508540 panic ! ( "Unexpected update result: {:?}" , update_result)
509541 }
@@ -526,6 +558,58 @@ mod tests {
526558 assert ! ( after. contains( "783241506229452801" ) ) ;
527559 }
528560
561+ #[ test]
562+ fn full_update_succeeds_at_the_beginning_of_the_unix_era ( ) {
563+ let block_hash = genesis_block ( Network :: Bitcoin ) . block_hash ( ) ;
564+ let logger = TestLogger :: new ( ) ;
565+ let network_graph = NetworkGraph :: new ( block_hash, & logger) ;
566+
567+ assert_eq ! ( network_graph. read_only( ) . channels( ) . len( ) , 0 ) ;
568+
569+ let rapid_sync = RapidGossipSync :: new ( & network_graph) ;
570+ // this is mostly for checking uint underflow issues before the fuzzer does
571+ let update_result = rapid_sync. update_network_graph_no_std ( & VALID_RGS_BINARY , Some ( 0 ) ) ;
572+ assert ! ( update_result. is_ok( ) ) ;
573+ assert_eq ! ( network_graph. read_only( ) . channels( ) . len( ) , 2 ) ;
574+ }
575+
576+ #[ test]
577+ fn timestamp_edge_cases_are_handled_correctly ( ) {
578+ // this is the timestamp encoded in the binary data of valid_input below
579+ let block_hash = genesis_block ( Network :: Bitcoin ) . block_hash ( ) ;
580+ let logger = TestLogger :: new ( ) ;
581+
582+ let latest_succeeding_time = VALID_BINARY_TIMESTAMP + STALE_RGS_UPDATE_AGE_LIMIT_SECS ;
583+ let earliest_failing_time = latest_succeeding_time + 1 ;
584+
585+ {
586+ let network_graph = NetworkGraph :: new ( block_hash, & logger) ;
587+ assert_eq ! ( network_graph. read_only( ) . channels( ) . len( ) , 0 ) ;
588+
589+ let rapid_sync = RapidGossipSync :: new ( & network_graph) ;
590+ let update_result = rapid_sync. update_network_graph_no_std ( & VALID_RGS_BINARY , Some ( latest_succeeding_time) ) ;
591+ assert ! ( update_result. is_ok( ) ) ;
592+ assert_eq ! ( network_graph. read_only( ) . channels( ) . len( ) , 2 ) ;
593+ }
594+
595+ {
596+ let network_graph = NetworkGraph :: new ( block_hash, & logger) ;
597+ assert_eq ! ( network_graph. read_only( ) . channels( ) . len( ) , 0 ) ;
598+
599+ let rapid_sync = RapidGossipSync :: new ( & network_graph) ;
600+ let update_result = rapid_sync. update_network_graph_no_std ( & VALID_RGS_BINARY , Some ( earliest_failing_time) ) ;
601+ assert ! ( update_result. is_err( ) ) ;
602+ if let Err ( GraphSyncError :: LightningError ( lightning_error) ) = update_result {
603+ assert_eq ! (
604+ lightning_error. err,
605+ "Rapid Gossip Sync data is more than two weeks old"
606+ ) ;
607+ } else {
608+ panic ! ( "Unexpected update result: {:?}" , update_result)
609+ }
610+ }
611+ }
612+
529613 #[ test]
530614 pub fn update_fails_with_unknown_version ( ) {
531615 let unknown_version_input = vec ! [
0 commit comments