Don't apply gossip backpressure to non-channel-announcing peers
[rust-lightning] / lightning / src / routing / gossip.rs
index bbb8732f27713d83d4410a45c9c61b84e286f2e5..e2c59b4f4340ceba68b1a62e881af214598074f3 100644 (file)
@@ -7,7 +7,7 @@
 // You may not use this file except in accordance with one or both of these
 // licenses.
 
-//! The top-level network map tracking logic lives here.
+//! The [`NetworkGraph`] stores the network gossip and [`P2PGossipSync`] fetches it from peers
 
 use bitcoin::secp256k1::constants::PUBLIC_KEY_SIZE;
 use bitcoin::secp256k1::PublicKey;
@@ -16,16 +16,14 @@ use bitcoin::secp256k1;
 
 use bitcoin::hashes::sha256d::Hash as Sha256dHash;
 use bitcoin::hashes::Hash;
-use bitcoin::blockdata::transaction::TxOut;
 use bitcoin::hash_types::BlockHash;
 
-use crate::ln::chan_utils::make_funding_redeemscript_from_slices;
 use crate::ln::features::{ChannelFeatures, NodeFeatures, InitFeatures};
 use crate::ln::msgs::{DecodeError, ErrorAction, Init, LightningError, RoutingMessageHandler, NetAddress, MAX_VALUE_MSAT};
 use crate::ln::msgs::{ChannelAnnouncement, ChannelUpdate, NodeAnnouncement, GossipTimestampFilter};
 use crate::ln::msgs::{QueryChannelRange, ReplyChannelRange, QueryShortChannelIds, ReplyShortChannelIdsEnd};
 use crate::ln::msgs;
-use crate::routing::utxo::{UtxoLookup, UtxoLookupError};
+use crate::routing::utxo::{self, UtxoLookup};
 use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer, MaybeReadable};
 use crate::util::logger::{Logger, Level};
 use crate::util::events::{MessageSendEvent, MessageSendEventsProvider};
@@ -42,7 +40,6 @@ use crate::sync::{RwLock, RwLockReadGuard};
 use core::sync::atomic::{AtomicUsize, Ordering};
 use crate::sync::Mutex;
 use core::ops::{Bound, Deref};
-use bitcoin::hashes::hex::ToHex;
 
 #[cfg(feature = "std")]
 use std::time::{SystemTime, UNIX_EPOCH};
@@ -158,6 +155,8 @@ pub struct NetworkGraph<L: Deref> where L::Target: Logger {
        /// resync them from gossip. Each `NodeId` is mapped to the time (in seconds) it was removed so
        /// that once some time passes, we can potentially resync it from gossip again.
        removed_nodes: Mutex<HashMap<NodeId, Option<u64>>>,
+       /// Announcement messages which are awaiting an on-chain lookup to be processed.
+       pub(super) pending_checks: utxo::PendingChecks,
 }
 
 /// A read-only view of [`NetworkGraph`].
@@ -273,6 +272,36 @@ where U::Target: UtxoLookup, L::Target: Logger
                        false
                }
        }
+
+       /// Used to broadcast forward gossip messages which were validated async.
+       ///
+       /// Note that this will ignore events other than `Broadcast*` or messages with too much excess
+       /// data.
+       pub(super) fn forward_gossip_msg(&self, mut ev: MessageSendEvent) {
+               match &mut ev {
+                       MessageSendEvent::BroadcastChannelAnnouncement { msg, ref mut update_msg } => {
+                               if msg.contents.excess_data.len() > MAX_EXCESS_BYTES_FOR_RELAY { return; }
+                               if update_msg.as_ref()
+                                       .map(|msg| msg.contents.excess_data.len()).unwrap_or(0) > MAX_EXCESS_BYTES_FOR_RELAY
+                               {
+                                       *update_msg = None;
+                               }
+                       },
+                       MessageSendEvent::BroadcastChannelUpdate { msg } => {
+                               if msg.contents.excess_data.len() > MAX_EXCESS_BYTES_FOR_RELAY { return; }
+                       },
+                       MessageSendEvent::BroadcastNodeAnnouncement { msg } => {
+                               if msg.contents.excess_data.len() >  MAX_EXCESS_BYTES_FOR_RELAY ||
+                                  msg.contents.excess_address_data.len() > MAX_EXCESS_BYTES_FOR_RELAY ||
+                                  msg.contents.excess_data.len() + msg.contents.excess_address_data.len() > MAX_EXCESS_BYTES_FOR_RELAY
+                               {
+                                       return;
+                               }
+                       },
+                       _ => return,
+               }
+               self.pending_events.lock().unwrap().push(ev);
+       }
 }
 
 impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
@@ -628,6 +657,10 @@ where U::Target: UtxoLookup, L::Target: Logger
                features.set_gossip_queries_optional();
                features
        }
+
+       fn processing_queue_high(&self) -> bool {
+               self.network_graph.pending_checks.too_many_checks_pending()
+       }
 }
 
 impl<G: Deref<Target=NetworkGraph<L>>, U: Deref, L: Deref> MessageSendEventsProvider for P2PGossipSync<G, U, L>
@@ -1203,6 +1236,7 @@ impl<L: Deref> ReadableArgs<L> for NetworkGraph<L> where L::Target: Logger {
                        last_rapid_gossip_sync_timestamp: Mutex::new(last_rapid_gossip_sync_timestamp),
                        removed_nodes: Mutex::new(HashMap::new()),
                        removed_channels: Mutex::new(HashMap::new()),
+                       pending_checks: utxo::PendingChecks::new(),
                })
        }
 }
@@ -1242,6 +1276,7 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
                        last_rapid_gossip_sync_timestamp: Mutex::new(None),
                        removed_channels: Mutex::new(HashMap::new()),
                        removed_nodes: Mutex::new(HashMap::new()),
+                       pending_checks: utxo::PendingChecks::new(),
                }
        }
 
@@ -1297,8 +1332,13 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
        }
 
        fn update_node_from_announcement_intern(&self, msg: &msgs::UnsignedNodeAnnouncement, full_msg: Option<&msgs::NodeAnnouncement>) -> Result<(), LightningError> {
-               match self.nodes.write().unwrap().get_mut(&msg.node_id) {
-                       None => Err(LightningError{err: "No existing channels for node_announcement".to_owned(), action: ErrorAction::IgnoreError}),
+               let mut nodes = self.nodes.write().unwrap();
+               match nodes.get_mut(&msg.node_id) {
+                       None => {
+                               core::mem::drop(nodes);
+                               self.pending_checks.check_hold_pending_node_announcement(msg, full_msg)?;
+                               Err(LightningError{err: "No existing channels for node_announcement".to_owned(), action: ErrorAction::IgnoreError})
+                       },
                        Some(node) => {
                                if let Some(node_info) = node.announcement_info.as_ref() {
                                        // The timestamp field is somewhat of a misnomer - the BOLTs use it to order
@@ -1497,32 +1537,8 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
                        }
                }
 
-               let utxo_value = match &utxo_lookup {
-                       &None => {
-                               // Tentatively accept, potentially exposing us to DoS attacks
-                               None
-                       },
-                       &Some(ref utxo_lookup) => {
-                               match utxo_lookup.get_utxo(&msg.chain_hash, msg.short_channel_id) {
-                                       Ok(TxOut { value, script_pubkey }) => {
-                                               let expected_script =
-                                                       make_funding_redeemscript_from_slices(msg.bitcoin_key_1.as_slice(), msg.bitcoin_key_2.as_slice()).to_v0_p2wsh();
-                                               if script_pubkey != expected_script {
-                                                       return Err(LightningError{err: format!("Channel announcement key ({}) didn't match on-chain script ({})", expected_script.to_hex(), script_pubkey.to_hex()), action: ErrorAction::IgnoreError});
-                                               }
-                                               //TODO: Check if value is worth storing, use it to inform routing, and compare it
-                                               //to the new HTLC max field in channel_update
-                                               Some(value)
-                                       },
-                                       Err(UtxoLookupError::UnknownChain) => {
-                                               return Err(LightningError{err: format!("Channel announced on an unknown chain ({})", msg.chain_hash.encode().to_hex()), action: ErrorAction::IgnoreError});
-                                       },
-                                       Err(UtxoLookupError::UnknownTx) => {
-                                               return Err(LightningError{err: "Channel announced without corresponding UTXO entry".to_owned(), action: ErrorAction::IgnoreError});
-                                       },
-                               }
-                       },
-               };
+               let utxo_value = self.pending_checks.check_channel_announcement(
+                       utxo_lookup, msg, full_msg)?;
 
                #[allow(unused_mut, unused_assignments)]
                let mut announcement_received_time = 0;
@@ -1747,7 +1763,11 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
 
                let mut channels = self.channels.write().unwrap();
                match channels.get_mut(&msg.short_channel_id) {
-                       None => return Err(LightningError{err: "Couldn't find channel for update".to_owned(), action: ErrorAction::IgnoreError}),
+                       None => {
+                               core::mem::drop(channels);
+                               self.pending_checks.check_hold_pending_channel_update(msg, full_msg)?;
+                               return Err(LightningError{err: "Couldn't find channel for update".to_owned(), action: ErrorAction::IgnoreError});
+                       },
                        Some(channel) => {
                                if msg.htlc_maximum_msat > MAX_VALUE_MSAT {
                                        return Err(LightningError{err: