Merge pull request #1091 from TheBlueMatt/2021-09-997-winblowz
authorMatt Corallo <649246+TheBlueMatt@users.noreply.github.com>
Tue, 21 Sep 2021 22:34:00 +0000 (22:34 +0000)
committerGitHub <noreply@github.com>
Tue, 21 Sep 2021 22:34:00 +0000 (22:34 +0000)
Fix windows-only test failure added in #997

fuzz/src/chanmon_consistency.rs
lightning-block-sync/src/convert.rs
lightning-block-sync/src/http.rs
lightning/src/ln/chanmon_update_fail_tests.rs
lightning/src/ln/channelmanager.rs
lightning/src/ln/functional_test_utils.rs
lightning/src/ln/functional_tests.rs
lightning/src/ln/onion_route_tests.rs
lightning/src/routing/network_graph.rs
lightning/src/routing/router.rs
lightning/src/util/events.rs

index f85ac9b9def2839542eefa00b0777a2838612302..4177b50275931722565fc1468aa7e5b08f29d76d 100644 (file)
@@ -827,7 +827,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
                                                        }
                                                },
                                                events::Event::PaymentSent { .. } => {},
-                                               events::Event::PaymentFailed { .. } => {},
+                                               events::Event::PaymentPathFailed { .. } => {},
                                                events::Event::PaymentForwarded { .. } if $node == 1 => {},
                                                events::Event::PendingHTLCsForwardable { .. } => {
                                                        nodes[$node].process_pending_htlc_forwards();
index e8e1427bdb654ffa432d069faf37457fc5a74cea..358076f4c25a883f5e571b421f9d3ff1ebe20f27 100644 (file)
@@ -1,11 +1,12 @@
-use crate::{BlockHeaderData, BlockSourceError};
 use crate::http::{BinaryResponse, JsonResponse};
 use crate::utils::hex_to_uint256;
+use crate::{BlockHeaderData, BlockSourceError};
 
 use bitcoin::blockdata::block::{Block, BlockHeader};
 use bitcoin::consensus::encode;
 use bitcoin::hash_types::{BlockHash, TxMerkleNode, Txid};
-use bitcoin::hashes::hex::{ToHex, FromHex};
+use bitcoin::hashes::hex::{FromHex, ToHex};
+use bitcoin::Transaction;
 
 use serde::Deserialize;
 
@@ -104,7 +105,6 @@ impl TryFrom<GetHeaderResponse> for BlockHeaderData {
        }
 }
 
-
 /// Converts a JSON value into a block. Assumes the block is hex-encoded in a JSON string.
 impl TryInto<Block> for JsonResponse {
        type Error = std::io::Error;
@@ -181,13 +181,77 @@ impl TryInto<Txid> for JsonResponse {
        }
 }
 
+/// Converts a JSON value into a transaction. WATCH OUT! this cannot be used for zero-input transactions
+/// (e.g. createrawtransaction). See https://github.com/rust-bitcoin/rust-bitcoincore-rpc/issues/197
+impl TryInto<Transaction> for JsonResponse {
+       type Error = std::io::Error;
+       fn try_into(self) -> std::io::Result<Transaction> {
+               let hex_tx = if self.0.is_object() {
+                       // result is json encoded
+                       match &self.0["hex"] {
+                               // result has hex field
+                               serde_json::Value::String(hex_data) => match self.0["complete"] {
+                                       // result may or may not be signed (e.g. signrawtransactionwithwallet)
+                                       serde_json::Value::Bool(x) => {
+                                               if x == false {
+                                                       let reason = match &self.0["errors"][0]["error"] {
+                                                               serde_json::Value::String(x) => x.as_str(),
+                                                               _ => "Unknown error",
+                                                       };
+
+                                                       return Err(std::io::Error::new(
+                                                               std::io::ErrorKind::InvalidData,
+                                                               format!("transaction couldn't be signed. {}", reason),
+                                                       ));
+                                               } else {
+                                                       hex_data
+                                               }
+                                       }
+                                       // result is a complete transaction (e.g. getrawtranaction verbose)
+                                       _ => hex_data,
+                               },
+                               _ => return Err(std::io::Error::new(
+                                                       std::io::ErrorKind::InvalidData,
+                                                       "expected JSON string",
+                                       )),
+                       }
+               } else {
+                       // result is plain text (e.g. getrawtransaction no verbose)
+                       match self.0.as_str() {
+                               Some(hex_tx) => hex_tx,
+                               None => {
+                                       return Err(std::io::Error::new(
+                                               std::io::ErrorKind::InvalidData,
+                                               "expected JSON string",
+                                       ))
+                               }
+                       }
+               };
+
+               match Vec::<u8>::from_hex(hex_tx) {
+                       Err(_) => Err(std::io::Error::new(
+                               std::io::ErrorKind::InvalidData,
+                               "invalid hex data",
+                       )),
+                       Ok(tx_data) => match encode::deserialize(&tx_data) {
+                               Err(_) => Err(std::io::Error::new(
+                                       std::io::ErrorKind::InvalidData,
+                                       "invalid transaction",
+                               )),
+                               Ok(tx) => Ok(tx),
+                       },
+               }
+       }
+}
+
 #[cfg(test)]
 pub(crate) mod tests {
        use super::*;
        use bitcoin::blockdata::constants::genesis_block;
-       use bitcoin::consensus::encode;
        use bitcoin::hashes::Hash;
        use bitcoin::network::constants::Network;
+       use serde_json::value::Number;
+       use serde_json::Value;
 
        /// Converts from `BlockHeaderData` into a `GetHeaderResponse` JSON value.
        impl From<BlockHeaderData> for serde_json::Value {
@@ -541,4 +605,101 @@ pub(crate) mod tests {
                        Ok(txid) => assert_eq!(txid, target_txid),
                }
        }
+
+       // TryInto<Transaction> can be used in two ways, first with plain hex response where data is
+       // the hex encoded transaction (e.g. as a result of getrawtransaction) or as a JSON object
+       // where the hex encoded transaction can be found in the hex field of the object (if present)
+       // (e.g. as a result of signrawtransactionwithwallet).
+
+       // plain hex transaction
+
+       #[test]
+       fn into_tx_from_json_response_with_invalid_hex_data() {
+               let response = JsonResponse(serde_json::json!("foobar"));
+               match TryInto::<Transaction>::try_into(response) {
+                       Err(e) => {
+                               assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
+                               assert_eq!(e.get_ref().unwrap().to_string(), "invalid hex data");
+                       }
+                       Ok(_) => panic!("Expected error"),
+               }
+       }
+
+       #[test]
+       fn into_tx_from_json_response_with_invalid_data_type() {
+               let response = JsonResponse(Value::Number(Number::from_f64(1.0).unwrap()));
+               match TryInto::<Transaction>::try_into(response) {
+                       Err(e) => {
+                               assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
+                               assert_eq!(e.get_ref().unwrap().to_string(), "expected JSON string");
+                       }
+                       Ok(_) => panic!("Expected error"),
+               }
+       }
+
+       #[test]
+       fn into_tx_from_json_response_with_invalid_tx_data() {
+               let response = JsonResponse(serde_json::json!("abcd"));
+               match TryInto::<Transaction>::try_into(response) {
+                       Err(e) => {
+                               assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
+                               assert_eq!(e.get_ref().unwrap().to_string(), "invalid transaction");
+                       }
+                       Ok(_) => panic!("Expected error"),
+               }
+       }
+
+       #[test]
+       fn into_tx_from_json_response_with_valid_tx_data_plain() {
+               let genesis_block = genesis_block(Network::Bitcoin);
+               let target_tx = genesis_block.txdata.get(0).unwrap();
+               let response = JsonResponse(serde_json::json!(encode::serialize_hex(&target_tx)));
+               match TryInto::<Transaction>::try_into(response) {
+                       Err(e) => panic!("Unexpected error: {:?}", e),
+                       Ok(tx) => assert_eq!(&tx, target_tx),
+               }
+       }
+
+       #[test]
+       fn into_tx_from_json_response_with_valid_tx_data_hex_field() {
+               let genesis_block = genesis_block(Network::Bitcoin);
+               let target_tx = genesis_block.txdata.get(0).unwrap();
+               let response = JsonResponse(serde_json::json!({"hex": encode::serialize_hex(&target_tx)}));
+               match TryInto::<Transaction>::try_into(response) {
+                       Err(e) => panic!("Unexpected error: {:?}", e),
+                       Ok(tx) => assert_eq!(&tx, target_tx),
+               }
+       }
+
+       // transaction in hex field of JSON object
+
+       #[test]
+       fn into_tx_from_json_response_with_no_hex_field() {
+               let response = JsonResponse(serde_json::json!({ "error": "foo" }));
+               match TryInto::<Transaction>::try_into(response) {
+                       Err(e) => {
+                               assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
+                               assert_eq!(
+                                       e.get_ref().unwrap().to_string(),
+                                       "expected JSON string"
+                               );
+                       }
+                       Ok(_) => panic!("Expected error"),
+               }
+       }
+
+       #[test]
+       fn into_tx_from_json_response_not_signed() {
+               let response = JsonResponse(serde_json::json!({ "hex": "foo", "complete": false }));
+               match TryInto::<Transaction>::try_into(response) {
+                       Err(e) => {
+                               assert_eq!(e.kind(), std::io::ErrorKind::InvalidData);
+                               assert!(
+                                       e.get_ref().unwrap().to_string().contains(
+                                       "transaction couldn't be signed")
+                               );
+                       }
+                       Ok(_) => panic!("Expected error"),
+               }
+       }
 }
index 0721babfde3d1b626051ba1ccccb7240d1f5a5a7..a3935edf5cb6e9880217196112d8f4ead5bacd57 100644 (file)
@@ -27,9 +27,10 @@ const TCP_STREAM_TIMEOUT: Duration = Duration::from_secs(5);
 
 /// Timeout for reading the first byte of a response. This is separate from the general read
 /// timeout as it is not uncommon for Bitcoin Core to be blocked waiting on UTXO cache flushes for
-/// upwards of a minute or more. Note that we always retry once when we time out, so the maximum
-/// time we allow Bitcoin Core to block for is twice this value.
-const TCP_STREAM_RESPONSE_TIMEOUT: Duration = Duration::from_secs(120);
+/// upwards of 10 minutes on slow devices (e.g. RPis with SSDs over USB). Note that we always retry
+/// once when we time out, so the maximum time we allow Bitcoin Core to block for is twice this
+/// value.
+const TCP_STREAM_RESPONSE_TIMEOUT: Duration = Duration::from_secs(300);
 
 /// Maximum HTTP message header size in bytes.
 const MAX_HTTP_MESSAGE_HEADER_SIZE: usize = 8192;
index 36f226823945b3a4cc5f0e07485788198b7719d8..c3262f17374327b42bc40c987350a05a6d8441d9 100644 (file)
@@ -78,7 +78,7 @@ fn do_test_simple_monitor_permanent_update_fail(persister_fail: bool) {
        };
 
        // TODO: Once we hit the chain with the failure transaction we should check that we get a
-       // PaymentFailed event
+       // PaymentPathFailed event
 
        assert_eq!(nodes[0].node.list_channels().len(), 0);
        check_closed_event!(nodes[0], 1, ClosureReason::ProcessingError { err: "ChannelMonitor storage failure".to_string() });
@@ -267,7 +267,7 @@ fn do_test_simple_monitor_temporary_update_fail(disconnect: bool, persister_fail
        check_closed_broadcast!(nodes[0], true);
 
        // TODO: Once we hit the chain with the failure transaction we should check that we get a
-       // PaymentFailed event
+       // PaymentPathFailed event
 
        assert_eq!(nodes[0].node.list_channels().len(), 0);
        check_closed_event!(nodes[0], 1, ClosureReason::HolderForceClosed);
@@ -1819,7 +1819,7 @@ fn test_monitor_update_on_pending_forwards() {
 
        let events = nodes[0].node.get_and_clear_pending_events();
        assert_eq!(events.len(), 2);
-       if let Event::PaymentFailed { payment_hash, rejected_by_dest, .. } = events[0] {
+       if let Event::PaymentPathFailed { payment_hash, rejected_by_dest, .. } = events[0] {
                assert_eq!(payment_hash, payment_hash_1);
                assert!(rejected_by_dest);
        } else { panic!("Unexpected event!"); }
index 8ce19cc0c40d408e2ba2f42428034dc8789451b9..a4684bfe6cc2960438bfdc9e2e575ed2b7aa2f79 100644 (file)
@@ -489,7 +489,7 @@ pub struct ChannelManager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref,
        /// The session_priv bytes of outbound payments which are pending resolution.
        /// The authoritative state of these HTLCs resides either within Channels or ChannelMonitors
        /// (if the channel has been force-closed), however we track them here to prevent duplicative
-       /// PaymentSent/PaymentFailed events. Specifically, in the case of a duplicative
+       /// PaymentSent/PaymentPathFailed events. Specifically, in the case of a duplicative
        /// update_fulfill_htlc message after a reconnect, we may "claim" a payment twice.
        /// Additionally, because ChannelMonitors are often not re-serialized after connecting block(s)
        /// which may generate a claim event, we may receive similar duplicate claim/fail MonitorEvents
@@ -2875,18 +2875,19 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                        self.fail_htlc_backwards_internal(channel_state,
                                                htlc_src, &payment_hash, HTLCFailReason::Reason { failure_code, data: onion_failure_data});
                                },
-                               HTLCSource::OutboundRoute { session_priv, mpp_id, .. } => {
+                               HTLCSource::OutboundRoute { session_priv, mpp_id, path, .. } => {
                                        let mut session_priv_bytes = [0; 32];
                                        session_priv_bytes.copy_from_slice(&session_priv[..]);
                                        let mut outbounds = self.pending_outbound_payments.lock().unwrap();
                                        if let hash_map::Entry::Occupied(mut sessions) = outbounds.entry(mpp_id) {
                                                if sessions.get_mut().remove(&session_priv_bytes) {
                                                        self.pending_events.lock().unwrap().push(
-                                                               events::Event::PaymentFailed {
+                                                               events::Event::PaymentPathFailed {
                                                                        payment_hash,
                                                                        rejected_by_dest: false,
                                                                        network_update: None,
                                                                        all_paths_failed: sessions.get().len() == 0,
+                                                                       path: path.clone(),
                                                                        #[cfg(test)]
                                                                        error_code: None,
                                                                        #[cfg(test)]
@@ -2951,11 +2952,12 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                                // process_onion_failure we should close that channel as it implies our
                                                // next-hop is needlessly blaming us!
                                                self.pending_events.lock().unwrap().push(
-                                                       events::Event::PaymentFailed {
+                                                       events::Event::PaymentPathFailed {
                                                                payment_hash: payment_hash.clone(),
                                                                rejected_by_dest: !payment_retryable,
                                                                network_update,
                                                                all_paths_failed,
+                                                               path: path.clone(),
 #[cfg(test)]
                                                                error_code: onion_error_code,
 #[cfg(test)]
@@ -2977,11 +2979,12 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                                // TODO: For non-temporary failures, we really should be closing the
                                                // channel here as we apparently can't relay through them anyway.
                                                self.pending_events.lock().unwrap().push(
-                                                       events::Event::PaymentFailed {
+                                                       events::Event::PaymentPathFailed {
                                                                payment_hash: payment_hash.clone(),
                                                                rejected_by_dest: path.len() == 1,
                                                                network_update: None,
                                                                all_paths_failed,
+                                                               path: path.clone(),
 #[cfg(test)]
                                                                error_code: Some(*failure_code),
 #[cfg(test)]
index 5690c834e73860d37729934c38c507bb648b3299..9fbbcfff39c9701306f0c06e0dcc47c0fdd7728b 100644 (file)
@@ -1073,7 +1073,7 @@ macro_rules! expect_payment_failed_with_update {
                let events = $node.node.get_and_clear_pending_events();
                assert_eq!(events.len(), 1);
                match events[0] {
-                       Event::PaymentFailed { ref payment_hash, rejected_by_dest, ref network_update, ref error_code, ref error_data, .. } => {
+                       Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, ref network_update, ref error_code, ref error_data, .. } => {
                                assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash");
                                assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value");
                                assert!(error_code.is_some(), "expected error_code.is_some() = true");
@@ -1102,7 +1102,7 @@ macro_rules! expect_payment_failed {
                let events = $node.node.get_and_clear_pending_events();
                assert_eq!(events.len(), 1);
                match events[0] {
-                       Event::PaymentFailed { ref payment_hash, rejected_by_dest, network_update: _, ref error_code, ref error_data, .. } => {
+                       Event::PaymentPathFailed { ref payment_hash, rejected_by_dest, network_update: _, ref error_code, ref error_data, .. } => {
                                assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash");
                                assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value");
                                assert!(error_code.is_some(), "expected error_code.is_some() = true");
@@ -1399,10 +1399,13 @@ pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expe
                        let events = origin_node.node.get_and_clear_pending_events();
                        assert_eq!(events.len(), 1);
                        match events[0] {
-                               Event::PaymentFailed { payment_hash, rejected_by_dest, all_paths_failed, .. } => {
+                               Event::PaymentPathFailed { payment_hash, rejected_by_dest, all_paths_failed, ref path, .. } => {
                                        assert_eq!(payment_hash, our_payment_hash);
                                        assert!(rejected_by_dest);
                                        assert_eq!(all_paths_failed, i == expected_paths.len() - 1);
+                                       for (idx, hop) in expected_route.iter().enumerate() {
+                                               assert_eq!(hop.node.get_our_node_id(), path[idx].pubkey);
+                                       }
                                },
                                _ => panic!("Unexpected event"),
                        }
index a7714796af4f07b46fd47e7e355da1db26cb685a..03b62ac185f44052246c40e341d0b29fc7918e0e 100644 (file)
@@ -3020,7 +3020,7 @@ fn do_test_commitment_revoked_fail_backward_exhaustive(deliver_bs_raa: bool, use
                _ => panic!("Unexepected event"),
        }
        match events[1] {
-               Event::PaymentFailed { ref payment_hash, .. } => {
+               Event::PaymentPathFailed { ref payment_hash, .. } => {
                        assert_eq!(*payment_hash, fourth_payment_hash);
                },
                _ => panic!("Unexpected event"),
@@ -3076,7 +3076,7 @@ fn do_test_commitment_revoked_fail_backward_exhaustive(deliver_bs_raa: bool, use
                        let events = nodes[0].node.get_and_clear_pending_events();
                        assert_eq!(events.len(), 3);
                        match events[0] {
-                               Event::PaymentFailed { ref payment_hash, rejected_by_dest: _, ref network_update, .. } => {
+                               Event::PaymentPathFailed { ref payment_hash, rejected_by_dest: _, ref network_update, .. } => {
                                        assert!(failed_htlcs.insert(payment_hash.0));
                                        // If we delivered B's RAA we got an unknown preimage error, not something
                                        // that we should update our routing table for.
@@ -3087,14 +3087,14 @@ fn do_test_commitment_revoked_fail_backward_exhaustive(deliver_bs_raa: bool, use
                                _ => panic!("Unexpected event"),
                        }
                        match events[1] {
-                               Event::PaymentFailed { ref payment_hash, rejected_by_dest: _, ref network_update, .. } => {
+                               Event::PaymentPathFailed { ref payment_hash, rejected_by_dest: _, ref network_update, .. } => {
                                        assert!(failed_htlcs.insert(payment_hash.0));
                                        assert!(network_update.is_some());
                                },
                                _ => panic!("Unexpected event"),
                        }
                        match events[2] {
-                               Event::PaymentFailed { ref payment_hash, rejected_by_dest: _, ref network_update, .. } => {
+                               Event::PaymentPathFailed { ref payment_hash, rejected_by_dest: _, ref network_update, .. } => {
                                        assert!(failed_htlcs.insert(payment_hash.0));
                                        assert!(network_update.is_some());
                                },
@@ -3190,7 +3190,7 @@ fn fail_backward_pending_htlc_upon_channel_failure() {
        assert_eq!(events.len(), 2);
        // Check that Alice fails backward the pending HTLC from the second payment.
        match events[0] {
-               Event::PaymentFailed { payment_hash, .. } => {
+               Event::PaymentPathFailed { payment_hash, .. } => {
                        assert_eq!(payment_hash, failed_payment_hash);
                },
                _ => panic!("Unexpected event"),
@@ -3392,7 +3392,7 @@ fn test_simple_peer_disconnect() {
                        _ => panic!("Unexpected event"),
                }
                match events[1] {
-                       Event::PaymentFailed { payment_hash, rejected_by_dest, .. } => {
+                       Event::PaymentPathFailed { payment_hash, rejected_by_dest, .. } => {
                                assert_eq!(payment_hash, payment_hash_5);
                                assert!(rejected_by_dest);
                        },
@@ -4192,7 +4192,7 @@ fn test_dup_htlc_onchain_fails_on_reload() {
        //
        // If, due to an on-chain event, an HTLC is failed/claimed, and then we serialize the
        // ChannelManager, we generally expect there not to be a duplicate HTLC fail/claim (eg via a
-       // PaymentFailed event appearing). However, because we may not serialize the relevant
+       // PaymentPathFailed event appearing). However, because we may not serialize the relevant
        // ChannelMonitor at the same time, this isn't strictly guaranteed. In order to provide this
        // consistency, the ChannelManager explicitly tracks pending-onchain-resolution outbound HTLCs
        // and de-duplicates ChannelMonitor events.
@@ -5518,7 +5518,7 @@ fn do_test_fail_backwards_unrevoked_remote_announce(deliver_last_raa: bool, anno
        let mut as_failds = HashSet::new();
        let mut as_updates = 0;
        for event in as_events.iter() {
-               if let &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref network_update, .. } = event {
+               if let &Event::PaymentPathFailed { ref payment_hash, ref rejected_by_dest, ref network_update, .. } = event {
                        assert!(as_failds.insert(*payment_hash));
                        if *payment_hash != payment_hash_2 {
                                assert_eq!(*rejected_by_dest, deliver_last_raa);
@@ -5543,7 +5543,7 @@ fn do_test_fail_backwards_unrevoked_remote_announce(deliver_last_raa: bool, anno
        let mut bs_failds = HashSet::new();
        let mut bs_updates = 0;
        for event in bs_events.iter() {
-               if let &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref network_update, .. } = event {
+               if let &Event::PaymentPathFailed { ref payment_hash, ref rejected_by_dest, ref network_update, .. } = event {
                        assert!(bs_failds.insert(*payment_hash));
                        if *payment_hash != payment_hash_1 && *payment_hash != payment_hash_5 {
                                assert_eq!(*rejected_by_dest, deliver_last_raa);
@@ -6068,7 +6068,7 @@ fn test_fail_holding_cell_htlc_upon_free() {
        let events = nodes[0].node.get_and_clear_pending_events();
        assert_eq!(events.len(), 1);
        match &events[0] {
-               &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref error_code, ref error_data, ref all_paths_failed } => {
+               &Event::PaymentPathFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref error_code, ref error_data, ref all_paths_failed, path: _ } => {
                        assert_eq!(our_payment_hash.clone(), *payment_hash);
                        assert_eq!(*rejected_by_dest, false);
                        assert_eq!(*all_paths_failed, true);
@@ -6155,7 +6155,7 @@ fn test_free_and_fail_holding_cell_htlcs() {
        let events = nodes[0].node.get_and_clear_pending_events();
        assert_eq!(events.len(), 1);
        match &events[0] {
-               &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref error_code, ref error_data, ref all_paths_failed } => {
+               &Event::PaymentPathFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref error_code, ref error_data, ref all_paths_failed, path: _ } => {
                        assert_eq!(payment_hash_2.clone(), *payment_hash);
                        assert_eq!(*rejected_by_dest, false);
                        assert_eq!(*all_paths_failed, true);
@@ -7107,12 +7107,12 @@ fn do_test_failure_delay_dust_htlc_local_commitment(announce_latest: bool) {
        assert_eq!(nodes[0].node.get_and_clear_pending_events().len(), 0);
        connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
        let events = nodes[0].node.get_and_clear_pending_events();
-       // Only 2 PaymentFailed events should show up, over-dust HTLC has to be failed by timeout tx
+       // Only 2 PaymentPathFailed events should show up, over-dust HTLC has to be failed by timeout tx
        assert_eq!(events.len(), 2);
        let mut first_failed = false;
        for event in events {
                match event {
-                       Event::PaymentFailed { payment_hash, .. } => {
+                       Event::PaymentPathFailed { payment_hash, .. } => {
                                if payment_hash == payment_hash_1 {
                                        assert!(!first_failed);
                                        first_failed = true;
@@ -7202,14 +7202,14 @@ fn do_test_sweep_outbound_htlc_failure_update(revoked: bool, local: bool) {
                        assert_eq!(events.len(), 2);
                        let first;
                        match events[0] {
-                               Event::PaymentFailed { payment_hash, .. } => {
+                               Event::PaymentPathFailed { payment_hash, .. } => {
                                        if payment_hash == dust_hash { first = true; }
                                        else { first = false; }
                                },
                                _ => panic!("Unexpected event"),
                        }
                        match events[1] {
-                               Event::PaymentFailed { payment_hash, .. } => {
+                               Event::PaymentPathFailed { payment_hash, .. } => {
                                        if first { assert_eq!(payment_hash, non_dust_hash); }
                                        else { assert_eq!(payment_hash, dust_hash); }
                                },
index 70d5a0c0bdfe30362668824ed6e77e0e267755a0..0e2d081c83bf862c7a3a80165c9f78b8e87ba1ec 100644 (file)
@@ -163,7 +163,7 @@ fn run_onion_failure_test_with_fail_intercept<F1,F2,F3>(_name: &str, test_case:
 
        let events = nodes[0].node.get_and_clear_pending_events();
        assert_eq!(events.len(), 1);
-       if let &Event::PaymentFailed { payment_hash:_, ref rejected_by_dest, ref network_update, ref error_code, error_data: _, ref all_paths_failed } = &events[0] {
+       if let &Event::PaymentPathFailed { payment_hash:_, ref rejected_by_dest, ref network_update, ref error_code, error_data: _, ref all_paths_failed, path: _ } = &events[0] {
                assert_eq!(*rejected_by_dest, !expected_retryable);
                assert_eq!(*all_paths_failed, true);
                assert_eq!(*error_code, expected_error_code);
index 16ff80189e96b7dd249d8d96b5112b305e2f272b..29bbc580a40384de0dda1fd9d1b29bfcff2620d6 100644 (file)
@@ -113,7 +113,7 @@ impl_writeable_tlv_based_enum_upgradable!(NetworkUpdate,
 impl<C: Deref, L: Deref> EventHandler for NetGraphMsgHandler<C, L>
 where C::Target: chain::Access, L::Target: Logger {
        fn handle_event(&self, event: &Event) {
-               if let Event::PaymentFailed { payment_hash: _, rejected_by_dest: _, network_update, .. } = event {
+               if let Event::PaymentPathFailed { payment_hash: _, rejected_by_dest: _, network_update, .. } = event {
                        if let Some(network_update) = network_update {
                                self.handle_network_update(network_update);
                        }
@@ -127,7 +127,7 @@ where C::Target: chain::Access, L::Target: Logger {
 /// Provides interface to help with initial routing sync by
 /// serving historical announcements.
 ///
-/// Serves as an [`EventHandler`] for applying updates from [`Event::PaymentFailed`] to the
+/// Serves as an [`EventHandler`] for applying updates from [`Event::PaymentPathFailed`] to the
 /// [`NetworkGraph`].
 pub struct NetGraphMsgHandler<C: Deref, L: Deref>
 where C::Target: chain::Access, L::Target: Logger
@@ -1725,10 +1725,11 @@ mod tests {
 
                        assert!(network_graph.read_only().channels().get(&short_channel_id).unwrap().one_to_two.is_none());
 
-                       net_graph_msg_handler.handle_event(&Event::PaymentFailed {
+                       net_graph_msg_handler.handle_event(&Event::PaymentPathFailed {
                                payment_hash: PaymentHash([0; 32]),
                                rejected_by_dest: false,
                                all_paths_failed: true,
+                               path: vec![],
                                network_update: Some(NetworkUpdate::ChannelUpdateMessage {
                                        msg: valid_channel_update,
                                }),
@@ -1748,10 +1749,11 @@ mod tests {
                                }
                        };
 
-                       net_graph_msg_handler.handle_event(&Event::PaymentFailed {
+                       net_graph_msg_handler.handle_event(&Event::PaymentPathFailed {
                                payment_hash: PaymentHash([0; 32]),
                                rejected_by_dest: false,
                                all_paths_failed: true,
+                               path: vec![],
                                network_update: Some(NetworkUpdate::ChannelClosed {
                                        short_channel_id,
                                        is_permanent: false,
@@ -1770,10 +1772,11 @@ mod tests {
 
                // Permanent closing deletes a channel
                {
-                       net_graph_msg_handler.handle_event(&Event::PaymentFailed {
+                       net_graph_msg_handler.handle_event(&Event::PaymentPathFailed {
                                payment_hash: PaymentHash([0; 32]),
                                rejected_by_dest: false,
                                all_paths_failed: true,
+                               path: vec![],
                                network_update: Some(NetworkUpdate::ChannelClosed {
                                        short_channel_id,
                                        is_permanent: true,
index 98b3d8f52453b939d129426d32117bbb6cedb1cb..325920b778151bafbfa98c8d610af77b7cb7ca41 100644 (file)
@@ -28,7 +28,7 @@ use core::cmp;
 use core::ops::Deref;
 
 /// A hop in a route
-#[derive(Clone, Hash, PartialEq, Eq)]
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
 pub struct RouteHop {
        /// The node_id of the node at this hop.
        pub pubkey: PublicKey,
@@ -72,21 +72,22 @@ pub struct Route {
 }
 
 impl Route {
-       /// Returns the total amount of fees paid on this Route.
-       /// This doesn't include any extra payment made to the recipient,
-       /// which can happen in excess of the amount passed to `get_route`'s `final_value_msat`.
+       /// Returns the total amount of fees paid on this [`Route`].
+       ///
+       /// This doesn't include any extra payment made to the recipient, which can happen in excess of
+       /// the amount passed to [`get_route`]'s `final_value_msat`.
        pub fn get_total_fees(&self) -> u64 {
                // Do not count last hop of each path since that's the full value of the payment
                return self.paths.iter()
-                       .flat_map(|path| path.split_last().unwrap().1)
+                       .flat_map(|path| path.split_last().map(|(_, path_prefix)| path_prefix).unwrap_or(&[]))
                        .map(|hop| &hop.fee_msat)
                        .sum();
        }
-       /// Returns the total amount paid on this Route, excluding the fees.
+
+       /// Returns the total amount paid on this [`Route`], excluding the fees.
        pub fn get_total_amount(&self) -> u64 {
                return self.paths.iter()
-                       .map(|path| path.split_last().unwrap().0)
-                       .map(|hop| &hop.fee_msat)
+                       .map(|path| path.split_last().map(|(hop, _)| hop.fee_msat).unwrap_or(0))
                        .sum();
        }
 }
@@ -4226,17 +4227,17 @@ mod tests {
                                RouteHop {
                                        pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(),
                                        channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
-                                       short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
+                                       short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0
                                },
                                RouteHop {
                                        pubkey: PublicKey::from_slice(&hex::decode("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap(),
                                        channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
-                                       short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
+                                       short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0
                                },
                                RouteHop {
                                        pubkey: PublicKey::from_slice(&hex::decode("027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007").unwrap()[..]).unwrap(),
                                        channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
-                                       short_channel_id: 0, fee_msat: 225, cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
+                                       short_channel_id: 0, fee_msat: 225, cltv_expiry_delta: 0
                                },
                        ]],
                };
@@ -4252,23 +4253,23 @@ mod tests {
                                RouteHop {
                                        pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(),
                                        channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
-                                       short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
+                                       short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0
                                },
                                RouteHop {
                                        pubkey: PublicKey::from_slice(&hex::decode("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap(),
                                        channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
-                                       short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
+                                       short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0
                                },
                        ],vec![
                                RouteHop {
                                        pubkey: PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap(),
                                        channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
-                                       short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
+                                       short_channel_id: 0, fee_msat: 100, cltv_expiry_delta: 0
                                },
                                RouteHop {
                                        pubkey: PublicKey::from_slice(&hex::decode("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()[..]).unwrap(),
                                        channel_features: ChannelFeatures::empty(), node_features: NodeFeatures::empty(),
-                                       short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0 // Test vectors are garbage and not generateble from a RouteHop, we fill in payloads manually
+                                       short_channel_id: 0, fee_msat: 150, cltv_expiry_delta: 0
                                },
                        ]],
                };
@@ -4277,6 +4278,17 @@ mod tests {
                assert_eq!(route.get_total_amount(), 300);
        }
 
+       #[test]
+       fn total_empty_route_no_panic() {
+               // In an earlier version of `Route::get_total_fees` and `Route::get_total_amount`, they
+               // would both panic if the route was completely empty. We test to ensure they return 0
+               // here, even though its somewhat nonsensical as a route.
+               let route = Route { paths: Vec::new() };
+
+               assert_eq!(route.get_total_fees(), 0);
+               assert_eq!(route.get_total_amount(), 0);
+       }
+
        #[cfg(not(feature = "no-std"))]
        pub(super) fn random_init_seed() -> u64 {
                // Because the default HashMap in std pulls OS randomness, we can use it as a (bad) RNG.
index ca4e96d60f73e4f44d0c5ce01a82d80671d4943a..1319f7d2101cc1c5b861acc5c3c86e78dd72277b 100644 (file)
@@ -19,7 +19,8 @@ use ln::msgs;
 use ln::msgs::DecodeError;
 use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
 use routing::network_graph::NetworkUpdate;
-use util::ser::{Writeable, Writer, MaybeReadable, Readable, VecReadWrapper, VecWriteWrapper};
+use util::ser::{BigSize, FixedLengthReader, Writeable, Writer, MaybeReadable, Readable, VecReadWrapper, VecWriteWrapper};
+use routing::router::RouteHop;
 
 use bitcoin::blockdata::script::Script;
 
@@ -170,8 +171,8 @@ pub enum Event {
        /// Indicates an outbound payment we made succeeded (i.e. it made it all the way to its target
        /// and we got back the payment preimage for it).
        ///
-       /// Note for MPP payments: in rare cases, this event may be preceded by a `PaymentFailed` event.
-       /// In this situation, you SHOULD treat this payment as having succeeded.
+       /// Note for MPP payments: in rare cases, this event may be preceded by a `PaymentPathFailed`
+       /// event. In this situation, you SHOULD treat this payment as having succeeded.
        PaymentSent {
                /// The preimage to the hash given to ChannelManager::send_payment.
                /// Note that this serves as a payment receipt, if you wish to have such a thing, you must
@@ -180,7 +181,7 @@ pub enum Event {
        },
        /// Indicates an outbound payment we made failed. Probably some intermediary node dropped
        /// something. You may wish to retry with a different route.
-       PaymentFailed {
+       PaymentPathFailed {
                /// The hash which was given to ChannelManager::send_payment.
                payment_hash: PaymentHash,
                /// Indicates the payment was rejected for some reason by the recipient. This implies that
@@ -200,6 +201,8 @@ pub enum Event {
                /// failed. This will be set to false if (1) this is an MPP payment and (2) other parts of the
                /// larger MPP payment were still in flight when this event was generated.
                all_paths_failed: bool,
+               /// The payment path that failed.
+               path: Vec<RouteHop>,
 #[cfg(test)]
                error_code: Option<u16>,
 #[cfg(test)]
@@ -291,7 +294,8 @@ impl Writeable for Event {
                                        (0, payment_preimage, required),
                                });
                        },
-                       &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref all_paths_failed,
+                       &Event::PaymentPathFailed { ref payment_hash, ref rejected_by_dest, ref network_update,
+                                                   ref all_paths_failed, ref path,
                                #[cfg(test)]
                                ref error_code,
                                #[cfg(test)]
@@ -307,6 +311,7 @@ impl Writeable for Event {
                                        (1, network_update, option),
                                        (2, rejected_by_dest, required),
                                        (3, all_paths_failed, required),
+                                       (5, path, vec_type),
                                });
                        },
                        &Event::PendingHTLCsForwardable { time_forwardable: _ } => {
@@ -335,6 +340,9 @@ impl Writeable for Event {
                                        (2, reason, required)
                                });
                        },
+                       // Note that, going forward, all new events must only write data inside of
+                       // `write_tlv_fields`. Versions 0.0.101+ will ignore odd-numbered events that write
+                       // data via `write_tlv_fields`.
                }
                Ok(())
        }
@@ -342,6 +350,8 @@ impl Writeable for Event {
 impl MaybeReadable for Event {
        fn read<R: io::Read>(reader: &mut R) -> Result<Option<Self>, msgs::DecodeError> {
                match Readable::read(reader)? {
+                       // Note that we do not write a length-prefixed TLV for FundingGenerationReady events,
+                       // unlike all other events, thus we return immediately here.
                        0u8 => Ok(None),
                        1u8 => {
                                let f = || {
@@ -398,17 +408,20 @@ impl MaybeReadable for Event {
                                        let mut rejected_by_dest = false;
                                        let mut network_update = None;
                                        let mut all_paths_failed = Some(true);
+                                       let mut path: Option<Vec<RouteHop>> = Some(vec![]);
                                        read_tlv_fields!(reader, {
                                                (0, payment_hash, required),
                                                (1, network_update, ignorable),
                                                (2, rejected_by_dest, required),
                                                (3, all_paths_failed, option),
+                                               (5, path, vec_type),
                                        });
-                                       Ok(Some(Event::PaymentFailed {
+                                       Ok(Some(Event::PaymentPathFailed {
                                                payment_hash,
                                                rejected_by_dest,
                                                network_update,
                                                all_paths_failed: all_paths_failed.unwrap(),
+                                               path: path.unwrap(),
                                                #[cfg(test)]
                                                error_code,
                                                #[cfg(test)]
@@ -459,7 +472,19 @@ impl MaybeReadable for Event {
                                Ok(Some(Event::ChannelClosed { channel_id, reason: reason.unwrap() }))
                        },
                        // Versions prior to 0.0.100 did not ignore odd types, instead returning InvalidValue.
-                       x if x % 2 == 1 => Ok(None),
+                       // Version 0.0.100 failed to properly ignore odd types, possibly resulting in corrupt
+                       // reads.
+                       x if x % 2 == 1 => {
+                               // If the event is of unknown type, assume it was written with `write_tlv_fields`,
+                               // which prefixes the whole thing with a length BigSize. Because the event is
+                               // odd-type unknown, we should treat it as `Ok(None)` even if it has some TLV
+                               // fields that are even. Thus, we avoid using `read_tlv_fields` and simply read
+                               // exactly the number of bytes specified, ignoring them entirely.
+                               let tlv_len: BigSize = Readable::read(reader)?;
+                               FixedLengthReader::new(reader, tlv_len.0)
+                                       .eat_remaining().map_err(|_| msgs::DecodeError::ShortRead)?;
+                               Ok(None)
+                       },
                        _ => Err(msgs::DecodeError::InvalidValue)
                }
        }