@@ -1720,6 +1720,15 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
1720
1720
/// RoutingMessageHandler implementation to call it indirectly. This may be useful to accept
1721
1721
/// routing messages from a source using a protocol other than the lightning P2P protocol.
1722
1722
pub fn update_node_from_announcement ( & self , msg : & msgs:: NodeAnnouncement ) -> Result < ( ) , LightningError > {
1723
+ // First check if we have the announcement already to avoid the CPU cost of validating a
1724
+ // redundant announcement.
1725
+ if let Some ( node) = self . nodes . read ( ) . unwrap ( ) . get ( & msg. contents . node_id ) {
1726
+ if let Some ( node_info) = node. announcement_info . as_ref ( ) {
1727
+ if node_info. last_update ( ) == msg. contents . timestamp {
1728
+ return Err ( LightningError { err : "Update had the same timestamp as last processed update" . to_owned ( ) , action : ErrorAction :: IgnoreDuplicateGossip } ) ;
1729
+ }
1730
+ }
1731
+ }
1723
1732
verify_node_announcement ( msg, & self . secp_ctx ) ?;
1724
1733
self . update_node_from_announcement_intern ( & msg. contents , Some ( & msg) )
1725
1734
}
@@ -1788,6 +1797,7 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
1788
1797
where
1789
1798
U :: Target : UtxoLookup ,
1790
1799
{
1800
+ self . pre_channel_announcement_validation_check ( & msg. contents , utxo_lookup) ?;
1791
1801
verify_channel_announcement ( msg, & self . secp_ctx ) ?;
1792
1802
self . update_channel_from_unsigned_announcement_intern ( & msg. contents , Some ( msg) , utxo_lookup)
1793
1803
}
@@ -1817,6 +1827,7 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
1817
1827
where
1818
1828
U :: Target : UtxoLookup ,
1819
1829
{
1830
+ self . pre_channel_announcement_validation_check ( & msg, utxo_lookup) ?;
1820
1831
self . update_channel_from_unsigned_announcement_intern ( msg, None , utxo_lookup)
1821
1832
}
1822
1833
@@ -1911,6 +1922,52 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
1911
1922
Ok ( ( ) )
1912
1923
}
1913
1924
1925
+ /// If we already have all the information for a channel that we're gonna get, there's no
1926
+ /// reason to redundantly process it.
1927
+ ///
1928
+ /// In those cases, this will return an `Err` that we can return immediately. Otherwise it will
1929
+ /// return an `Ok(())`.
1930
+ fn pre_channel_announcement_validation_check < U : Deref > (
1931
+ & self , msg : & msgs:: UnsignedChannelAnnouncement , utxo_lookup : & Option < U > ,
1932
+ ) -> Result < ( ) , LightningError > where U :: Target : UtxoLookup {
1933
+ let channels = self . channels . read ( ) . unwrap ( ) ;
1934
+
1935
+ if let Some ( chan) = channels. get ( & msg. short_channel_id ) {
1936
+ if chan. capacity_sats . is_some ( ) {
1937
+ // If we'd previously looked up the channel on-chain and checked the script
1938
+ // against what appears on-chain, ignore the duplicate announcement.
1939
+ //
1940
+ // Because a reorg could replace one channel with another at the same SCID, if
1941
+ // the channel appears to be different, we re-validate. This doesn't expose us
1942
+ // to any more DoS risk than not, as a peer can always flood us with
1943
+ // randomly-generated SCID values anyway.
1944
+ //
1945
+ // We use the Node IDs rather than the bitcoin_keys to check for "equivalence"
1946
+ // as we didn't (necessarily) store the bitcoin keys, and we only really care
1947
+ // if the peers on the channel changed anyway.
1948
+ if msg. node_id_1 == chan. node_one && msg. node_id_2 == chan. node_two {
1949
+ return Err ( LightningError {
1950
+ err : "Already have chain-validated channel" . to_owned ( ) ,
1951
+ action : ErrorAction :: IgnoreDuplicateGossip
1952
+ } ) ;
1953
+ }
1954
+ } else if utxo_lookup. is_none ( ) {
1955
+ // Similarly, if we can't check the chain right now anyway, ignore the
1956
+ // duplicate announcement without bothering to take the channels write lock.
1957
+ return Err ( LightningError {
1958
+ err : "Already have non-chain-validated channel" . to_owned ( ) ,
1959
+ action : ErrorAction :: IgnoreDuplicateGossip
1960
+ } ) ;
1961
+ }
1962
+ }
1963
+
1964
+ Ok ( ( ) )
1965
+ }
1966
+
1967
+ /// Update channel information from a received announcement.
1968
+ ///
1969
+ /// Generally [`Self::pre_channel_announcement_validation_check`] should have been called
1970
+ /// first.
1914
1971
fn update_channel_from_unsigned_announcement_intern < U : Deref > (
1915
1972
& self , msg : & msgs:: UnsignedChannelAnnouncement , full_msg : Option < & msgs:: ChannelAnnouncement > , utxo_lookup : & Option < U >
1916
1973
) -> Result < ( ) , LightningError >
@@ -1928,39 +1985,6 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
1928
1985
} ) ;
1929
1986
}
1930
1987
1931
- {
1932
- let channels = self . channels . read ( ) . unwrap ( ) ;
1933
-
1934
- if let Some ( chan) = channels. get ( & msg. short_channel_id ) {
1935
- if chan. capacity_sats . is_some ( ) {
1936
- // If we'd previously looked up the channel on-chain and checked the script
1937
- // against what appears on-chain, ignore the duplicate announcement.
1938
- //
1939
- // Because a reorg could replace one channel with another at the same SCID, if
1940
- // the channel appears to be different, we re-validate. This doesn't expose us
1941
- // to any more DoS risk than not, as a peer can always flood us with
1942
- // randomly-generated SCID values anyway.
1943
- //
1944
- // We use the Node IDs rather than the bitcoin_keys to check for "equivalence"
1945
- // as we didn't (necessarily) store the bitcoin keys, and we only really care
1946
- // if the peers on the channel changed anyway.
1947
- if msg. node_id_1 == chan. node_one && msg. node_id_2 == chan. node_two {
1948
- return Err ( LightningError {
1949
- err : "Already have chain-validated channel" . to_owned ( ) ,
1950
- action : ErrorAction :: IgnoreDuplicateGossip
1951
- } ) ;
1952
- }
1953
- } else if utxo_lookup. is_none ( ) {
1954
- // Similarly, if we can't check the chain right now anyway, ignore the
1955
- // duplicate announcement without bothering to take the channels write lock.
1956
- return Err ( LightningError {
1957
- err : "Already have non-chain-validated channel" . to_owned ( ) ,
1958
- action : ErrorAction :: IgnoreDuplicateGossip
1959
- } ) ;
1960
- }
1961
- }
1962
- }
1963
-
1964
1988
{
1965
1989
let removed_channels = self . removed_channels . lock ( ) . unwrap ( ) ;
1966
1990
let removed_nodes = self . removed_nodes . lock ( ) . unwrap ( ) ;
@@ -2563,11 +2587,6 @@ pub(crate) mod tests {
2563
2587
} ;
2564
2588
}
2565
2589
2566
- match gossip_sync. handle_node_announcement ( Some ( & node_1_pubkey) , & valid_announcement) {
2567
- Ok ( res) => assert ! ( res) ,
2568
- Err ( _) => panic ! ( )
2569
- } ;
2570
-
2571
2590
let fake_msghash = hash_to_message ! ( zero_hash. as_byte_array( ) ) ;
2572
2591
match gossip_sync. handle_node_announcement (
2573
2592
Some ( & node_1_pubkey) ,
@@ -2579,6 +2598,11 @@ pub(crate) mod tests {
2579
2598
Err ( e) => assert_eq ! ( e. err, "Invalid signature on node_announcement message" )
2580
2599
} ;
2581
2600
2601
+ match gossip_sync. handle_node_announcement ( Some ( & node_1_pubkey) , & valid_announcement) {
2602
+ Ok ( res) => assert ! ( res) ,
2603
+ Err ( _) => panic ! ( )
2604
+ } ;
2605
+
2582
2606
let announcement_with_data = get_signed_node_announcement ( |unsigned_announcement| {
2583
2607
unsigned_announcement. timestamp += 1000 ;
2584
2608
unsigned_announcement. excess_data . resize ( MAX_EXCESS_BYTES_FOR_RELAY + 1 , 0 ) ;
@@ -2701,23 +2725,23 @@ pub(crate) mod tests {
2701
2725
}
2702
2726
}
2703
2727
2704
- // Don't relay valid channels with excess data
2705
- let valid_announcement = get_signed_channel_announcement ( |unsigned_announcement| {
2728
+ let valid_excess_data_announcement = get_signed_channel_announcement ( |unsigned_announcement| {
2706
2729
unsigned_announcement. short_channel_id += 4 ;
2707
2730
unsigned_announcement. excess_data . resize ( MAX_EXCESS_BYTES_FOR_RELAY + 1 , 0 ) ;
2708
2731
} , node_1_privkey, node_2_privkey, & secp_ctx) ;
2709
- match gossip_sync. handle_channel_announcement ( Some ( & node_1_pubkey) , & valid_announcement) {
2710
- Ok ( res) => assert ! ( !res) ,
2711
- _ => panic ! ( )
2712
- } ;
2713
2732
2714
- let mut invalid_sig_announcement = valid_announcement . clone ( ) ;
2733
+ let mut invalid_sig_announcement = valid_excess_data_announcement . clone ( ) ;
2715
2734
invalid_sig_announcement. contents . excess_data = Vec :: new ( ) ;
2716
2735
match gossip_sync. handle_channel_announcement ( Some ( & node_1_pubkey) , & invalid_sig_announcement) {
2717
2736
Ok ( _) => panic ! ( ) ,
2718
2737
Err ( e) => assert_eq ! ( e. err, "Invalid signature on channel_announcement message" )
2719
2738
} ;
2720
2739
2740
+ // Don't relay valid channels with excess data
2741
+ match gossip_sync. handle_channel_announcement ( Some ( & node_1_pubkey) , & valid_excess_data_announcement) {
2742
+ Ok ( res) => assert ! ( !res) ,
2743
+ _ => panic ! ( )
2744
+ } ;
2721
2745
2722
2746
let channel_to_itself_announcement = get_signed_channel_announcement ( |_| { } , node_1_privkey, node_1_privkey, & secp_ctx) ;
2723
2747
match gossip_sync. handle_channel_announcement ( Some ( & node_1_pubkey) , & channel_to_itself_announcement) {
0 commit comments