@@ -1720,6 +1720,15 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
17201720 /// RoutingMessageHandler implementation to call it indirectly. This may be useful to accept
17211721 /// routing messages from a source using a protocol other than the lightning P2P protocol.
17221722 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+ }
17231732 verify_node_announcement ( msg, & self . secp_ctx ) ?;
17241733 self . update_node_from_announcement_intern ( & msg. contents , Some ( & msg) )
17251734 }
@@ -1788,6 +1797,7 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
17881797 where
17891798 U :: Target : UtxoLookup ,
17901799 {
1800+ self . pre_channel_announcement_validation_check ( & msg. contents , utxo_lookup) ?;
17911801 verify_channel_announcement ( msg, & self . secp_ctx ) ?;
17921802 self . update_channel_from_unsigned_announcement_intern ( & msg. contents , Some ( msg) , utxo_lookup)
17931803 }
@@ -1817,6 +1827,7 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
18171827 where
18181828 U :: Target : UtxoLookup ,
18191829 {
1830+ self . pre_channel_announcement_validation_check ( & msg, utxo_lookup) ?;
18201831 self . update_channel_from_unsigned_announcement_intern ( msg, None , utxo_lookup)
18211832 }
18221833
@@ -1911,6 +1922,52 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
19111922 Ok ( ( ) )
19121923 }
19131924
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.
19141971 fn update_channel_from_unsigned_announcement_intern < U : Deref > (
19151972 & self , msg : & msgs:: UnsignedChannelAnnouncement , full_msg : Option < & msgs:: ChannelAnnouncement > , utxo_lookup : & Option < U >
19161973 ) -> Result < ( ) , LightningError >
@@ -1928,39 +1985,6 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
19281985 } ) ;
19291986 }
19301987
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-
19641988 {
19651989 let removed_channels = self . removed_channels . lock ( ) . unwrap ( ) ;
19661990 let removed_nodes = self . removed_nodes . lock ( ) . unwrap ( ) ;
@@ -2562,11 +2586,6 @@ pub(crate) mod tests {
25622586 } ;
25632587 }
25642588
2565- match gossip_sync. handle_node_announcement ( & valid_announcement) {
2566- Ok ( res) => assert ! ( res) ,
2567- Err ( _) => panic ! ( )
2568- } ;
2569-
25702589 let fake_msghash = hash_to_message ! ( zero_hash. as_byte_array( ) ) ;
25712590 match gossip_sync. handle_node_announcement (
25722591 & NodeAnnouncement {
@@ -2577,6 +2596,11 @@ pub(crate) mod tests {
25772596 Err ( e) => assert_eq ! ( e. err, "Invalid signature on node_announcement message" )
25782597 } ;
25792598
2599+ match gossip_sync. handle_node_announcement ( & valid_announcement) {
2600+ Ok ( res) => assert ! ( res) ,
2601+ Err ( _) => panic ! ( )
2602+ } ;
2603+
25802604 let announcement_with_data = get_signed_node_announcement ( |unsigned_announcement| {
25812605 unsigned_announcement. timestamp += 1000 ;
25822606 unsigned_announcement. excess_data . resize ( MAX_EXCESS_BYTES_FOR_RELAY + 1 , 0 ) ;
@@ -2698,23 +2722,24 @@ pub(crate) mod tests {
26982722 }
26992723 }
27002724
2701- // Don't relay valid channels with excess data
2702- let valid_announcement = get_signed_channel_announcement ( |unsigned_announcement| {
2725+ let valid_excess_data_announcement = get_signed_channel_announcement ( |unsigned_announcement| {
27032726 unsigned_announcement. short_channel_id += 4 ;
27042727 unsigned_announcement. excess_data . resize ( MAX_EXCESS_BYTES_FOR_RELAY + 1 , 0 ) ;
27052728 } , node_1_privkey, node_2_privkey, & secp_ctx) ;
2706- match gossip_sync. handle_channel_announcement ( & valid_announcement) {
2707- Ok ( res) => assert ! ( !res) ,
2708- _ => panic ! ( )
2709- } ;
27102729
2711- let mut invalid_sig_announcement = valid_announcement . clone ( ) ;
2730+ let mut invalid_sig_announcement = valid_excess_data_announcement . clone ( ) ;
27122731 invalid_sig_announcement. contents . excess_data = Vec :: new ( ) ;
27132732 match gossip_sync. handle_channel_announcement ( & invalid_sig_announcement) {
27142733 Ok ( _) => panic ! ( ) ,
27152734 Err ( e) => assert_eq ! ( e. err, "Invalid signature on channel_announcement message" )
27162735 } ;
27172736
2737+ // Don't relay valid channels with excess data
2738+ match gossip_sync. handle_channel_announcement ( & valid_excess_data_announcement) {
2739+ Ok ( res) => assert ! ( !res) ,
2740+ _ => panic ! ( )
2741+ } ;
2742+
27182743 let channel_to_itself_announcement = get_signed_channel_announcement ( |_| { } , node_1_privkey, node_1_privkey, & secp_ctx) ;
27192744 match gossip_sync. handle_channel_announcement ( & channel_to_itself_announcement) {
27202745 Ok ( _) => panic ! ( ) ,
0 commit comments