Move UTXO-lookup into pub utility function from RoutingMsgHandler
[rust-lightning] / lightning / src / routing / network_graph.rs
index cafe4fc10a38f5c51259256c3c87fe6e1248a90b..5003ca512f520df073c1b94e612ced49bb6b5ca1 100644 (file)
@@ -125,40 +125,7 @@ impl<C: Deref + Sync + Send, L: Deref + Sync + Send> RoutingMessageHandler for N
        }
 
        fn handle_channel_announcement(&self, msg: &msgs::ChannelAnnouncement) -> Result<bool, LightningError> {
-               if msg.contents.node_id_1 == msg.contents.node_id_2 || msg.contents.bitcoin_key_1 == msg.contents.bitcoin_key_2 {
-                       return Err(LightningError{err: "Channel announcement node had a channel with itself".to_owned(), action: ErrorAction::IgnoreError});
-               }
-
-               let utxo_value = match &self.chain_access {
-                       &None => {
-                               // Tentatively accept, potentially exposing us to DoS attacks
-                               None
-                       },
-                       &Some(ref chain_access) => {
-                               match chain_access.get_utxo(&msg.contents.chain_hash, msg.contents.short_channel_id) {
-                                       Ok(TxOut { value, script_pubkey }) => {
-                                               let expected_script = Builder::new().push_opcode(opcodes::all::OP_PUSHNUM_2)
-                                                                                   .push_slice(&msg.contents.bitcoin_key_1.serialize())
-                                                                                   .push_slice(&msg.contents.bitcoin_key_2.serialize())
-                                                                                   .push_opcode(opcodes::all::OP_PUSHNUM_2)
-                                                                                   .push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script().to_v0_p2wsh();
-                                               if script_pubkey != expected_script {
-                                                       return Err(LightningError{err: format!("Channel announcement key ({}) didn't match on-chain script ({})", script_pubkey.to_hex(), expected_script.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(chain::AccessError::UnknownChain) => {
-                                               return Err(LightningError{err: format!("Channel announced on an unknown chain ({})", msg.contents.chain_hash.encode().to_hex()), action: ErrorAction::IgnoreError});
-                                       },
-                                       Err(chain::AccessError::UnknownTx) => {
-                                               return Err(LightningError{err: "Channel announced without corresponding UTXO entry".to_owned(), action: ErrorAction::IgnoreError});
-                                       },
-                               }
-                       },
-               };
-               let result = self.network_graph.write().unwrap().update_channel_from_announcement(msg, utxo_value, Some(&self.secp_ctx));
+               let result = self.network_graph.write().unwrap().update_channel_from_announcement(msg, &self.chain_access, Some(&self.secp_ctx));
                log_trace!(self.logger, "Added channel_announcement for {}{}", msg.contents.short_channel_id, if !msg.contents.excess_data.is_empty() { " with excess uninterpreted data!" } else { "" });
                result
        }
@@ -518,13 +485,13 @@ impl Readable for NetworkGraph {
 
 impl fmt::Display for NetworkGraph {
        fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
-               write!(f, "Network map\n[Channels]\n")?;
+               writeln!(f, "Network map\n[Channels]")?;
                for (key, val) in self.channels.iter() {
-                       write!(f, " {}: {}\n", key, val)?;
+                       writeln!(f, " {}: {}", key, val)?;
                }
-               write!(f, "[Nodes]\n")?;
+               writeln!(f, "[Nodes]")?;
                for (key, val) in self.nodes.iter() {
-                       write!(f, " {}: {}\n", log_pubkey!(key), val)?;
+                       writeln!(f, " {}: {}", log_pubkey!(key), val)?;
                }
                Ok(())
        }
@@ -562,9 +529,14 @@ impl NetworkGraph {
                }
        }
 
-       /// For an already known node (from channel announcements), update its stored properties from a given node announcement
+       /// For an already known node (from channel announcements), update its stored properties from a given node announcement.
+       ///
+       /// You probably don't want to call this directly, instead relying on a NetGraphMsgHandler's
+       /// RoutingMessageHandler implementation to call it indirectly. This may be useful to accept
+       /// routing messages without checking their signatures.
+       ///
        /// Announcement signatures are checked here only if Secp256k1 object is provided.
-       fn update_node_from_announcement(&mut self, msg: &msgs::NodeAnnouncement, secp_ctx: Option<&Secp256k1<secp256k1::VerifyOnly>>) -> Result<bool, LightningError> {
+       pub fn update_node_from_announcement<T: secp256k1::Verification>(&mut self, msg: &msgs::NodeAnnouncement, secp_ctx: Option<&Secp256k1<T>>) -> Result<bool, LightningError> {
                if let Some(sig_verifier) = secp_ctx {
                        let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.contents.encode()[..])[..]);
                        secp_verify_sig!(sig_verifier, &msg_hash, &msg.signature, &msg.contents.node_id);
@@ -594,13 +566,24 @@ impl NetworkGraph {
                }
        }
 
-       /// For a new or already known (from previous announcement) channel, store or update channel info.
-       /// Also store nodes (if not stored yet) the channel is between, and make node aware of this channel.
-       /// Checking utxo on-chain is useful if we receive an update for already known channel id,
-       /// which is probably result of a reorg. In that case, we update channel info only if the
-       /// utxo was checked, otherwise stick to the existing update, to prevent DoS risks.
+       /// Store or update channel info from a channel announcement.
+       ///
+       /// You probably don't want to call this directly, instead relying on a NetGraphMsgHandler's
+       /// RoutingMessageHandler implementation to call it indirectly. This may be useful to accept
+       /// routing messages without checking their signatures.
+       ///
+       /// If a `chain::Access` object is provided via `chain_access`, it will be called to verify
+       /// the corresponding UTXO exists on chain and is correctly-formatted.
+       ///
        /// Announcement signatures are checked here only if Secp256k1 object is provided.
-       fn update_channel_from_announcement(&mut self, msg: &msgs::ChannelAnnouncement, utxo_value: Option<u64>, secp_ctx: Option<&Secp256k1<secp256k1::VerifyOnly>>) -> Result<bool, LightningError> {
+       pub fn update_channel_from_announcement<T: secp256k1::Verification, C: Deref>
+                       (&mut self, msg: &msgs::ChannelAnnouncement, chain_access: &Option<C>, secp_ctx: Option<&Secp256k1<T>>)
+                       -> Result<bool, LightningError>
+                       where C::Target: chain::Access {
+               if msg.contents.node_id_1 == msg.contents.node_id_2 || msg.contents.bitcoin_key_1 == msg.contents.bitcoin_key_2 {
+                       return Err(LightningError{err: "Channel announcement node had a channel with itself".to_owned(), action: ErrorAction::IgnoreError});
+               }
+
                if let Some(sig_verifier) = secp_ctx {
                        let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.contents.encode()[..])[..]);
                        secp_verify_sig!(sig_verifier, &msg_hash, &msg.node_signature_1, &msg.contents.node_id_1);
@@ -609,8 +592,37 @@ impl NetworkGraph {
                        secp_verify_sig!(sig_verifier, &msg_hash, &msg.bitcoin_signature_2, &msg.contents.bitcoin_key_2);
                }
 
-               let should_relay = msg.contents.excess_data.is_empty();
+               let utxo_value = match &chain_access {
+                       &None => {
+                               // Tentatively accept, potentially exposing us to DoS attacks
+                               None
+                       },
+                       &Some(ref chain_access) => {
+                               match chain_access.get_utxo(&msg.contents.chain_hash, msg.contents.short_channel_id) {
+                                       Ok(TxOut { value, script_pubkey }) => {
+                                               let expected_script = Builder::new().push_opcode(opcodes::all::OP_PUSHNUM_2)
+                                                                                   .push_slice(&msg.contents.bitcoin_key_1.serialize())
+                                                                                   .push_slice(&msg.contents.bitcoin_key_2.serialize())
+                                                                                   .push_opcode(opcodes::all::OP_PUSHNUM_2)
+                                                                                   .push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script().to_v0_p2wsh();
+                                               if script_pubkey != expected_script {
+                                                       return Err(LightningError{err: format!("Channel announcement key ({}) didn't match on-chain script ({})", script_pubkey.to_hex(), expected_script.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(chain::AccessError::UnknownChain) => {
+                                               return Err(LightningError{err: format!("Channel announced on an unknown chain ({})", msg.contents.chain_hash.encode().to_hex()), action: ErrorAction::IgnoreError});
+                                       },
+                                       Err(chain::AccessError::UnknownTx) => {
+                                               return Err(LightningError{err: "Channel announced without corresponding UTXO entry".to_owned(), action: ErrorAction::IgnoreError});
+                                       },
+                               }
+                       },
+               };
 
+               let should_relay = msg.contents.excess_data.is_empty();
                let chan_info = ChannelInfo {
                                features: msg.contents.features.clone(),
                                node_one: msg.contents.node_id_1.clone(),
@@ -699,8 +711,13 @@ impl NetworkGraph {
        }
 
        /// For an already known (from announcement) channel, update info about one of the directions of a channel.
+       ///
+       /// You probably don't want to call this directly, instead relying on a NetGraphMsgHandler's
+       /// RoutingMessageHandler implementation to call it indirectly. This may be useful to accept
+       /// routing messages without checking their signatures.
+       ///
        /// Announcement signatures are checked here only if Secp256k1 object is provided.
-       fn update_channel(&mut self, msg: &msgs::ChannelUpdate, secp_ctx: Option<&Secp256k1<secp256k1::VerifyOnly>>) -> Result<bool, LightningError> {
+       pub fn update_channel(&mut self, msg: &msgs::ChannelUpdate, secp_ctx: Option<&Secp256k1<secp256k1::VerifyOnly>>) -> Result<bool, LightningError> {
                let dest_node_id;
                let chan_enabled = msg.contents.flags & (1 << 1) != (1 << 1);
                let chan_was_enabled;
@@ -716,8 +733,8 @@ impl NetworkGraph {
                                        if let Some(capacity_sats) = channel.capacity_sats {
                                                // It's possible channel capacity is available now, although it wasn't available at announcement (so the field is None).
                                                // Don't query UTXO set here to reduce DoS risks.
-                                               if htlc_maximum_msat > capacity_sats * 1000 {
-                                                       return Err(LightningError{err: "htlc_maximum_msat is larger than channel capacity".to_owned(), action: ErrorAction::IgnoreError});
+                                               if capacity_sats > MAX_VALUE_MSAT / 1000 || htlc_maximum_msat > capacity_sats * 1000 {
+                                                       return Err(LightningError{err: "htlc_maximum_msat is larger than channel capacity or capacity is bogus".to_owned(), action: ErrorAction::IgnoreError});
                                                }
                                        }
                                }
@@ -1302,7 +1319,7 @@ mod tests {
 
                match net_graph_msg_handler.handle_channel_update(&valid_channel_update) {
                        Ok(_) => panic!(),
-                       Err(e) => assert_eq!(e.err, "htlc_maximum_msat is larger than channel capacity")
+                       Err(e) => assert_eq!(e.err, "htlc_maximum_msat is larger than channel capacity or capacity is bogus")
                };
                unsigned_channel_update.htlc_maximum_msat = OptionalField::Absent;