]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Add flag to indicate when a payment was rejected by the first hop
authorMatt Corallo <git@bluematt.me>
Sun, 28 Mar 2021 19:42:49 +0000 (15:42 -0400)
committerMatt Corallo <git@bluematt.me>
Sun, 28 Mar 2021 19:42:49 +0000 (15:42 -0400)
This was requested by a user wishing to brute-force the expected
feerate as a stopgap to trampoline, allowing them to have the
first hop node return a route with missing first-hop feerate info.

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/ln/onion_utils.rs
lightning/src/util/events.rs

index 50f8ccbb74f4833175b00060fcd7ee882873c71b..c90aa55ac5e8166a825a9ba05320f33ed74a5d0c 100644 (file)
@@ -2006,6 +2006,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                                events::Event::PaymentFailed {
                                                        payment_hash,
                                                        rejected_by_dest: false,
+                                                       rejected_by_first_hop: false,
 #[cfg(test)]
                                                        error_code: None,
 #[cfg(test)]
@@ -2039,9 +2040,9 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                match &onion_error {
                                        &HTLCFailReason::LightningError { ref err } => {
 #[cfg(test)]
-                                               let (channel_update, payment_retryable, onion_error_code, onion_error_data) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone());
+                                               let (channel_update, payment_retryable, rejected_by_first_hop, onion_error_code, onion_error_data) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone());
 #[cfg(not(test))]
-                                               let (channel_update, payment_retryable, _, _) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone());
+                                               let (channel_update, payment_retryable, rejected_by_first_hop, _, _) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone());
                                                // TODO: If we decided to blame ourselves (or one of our channels) in
                                                // process_onion_failure we should close that channel as it implies our
                                                // next-hop is needlessly blaming us!
@@ -2056,6 +2057,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                                        events::Event::PaymentFailed {
                                                                payment_hash: payment_hash.clone(),
                                                                rejected_by_dest: !payment_retryable,
+                                                               rejected_by_first_hop,
 #[cfg(test)]
                                                                error_code: onion_error_code,
 #[cfg(test)]
@@ -2080,6 +2082,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                                        events::Event::PaymentFailed {
                                                                payment_hash: payment_hash.clone(),
                                                                rejected_by_dest: path.len() == 1,
+                                                               rejected_by_first_hop: true,
 #[cfg(test)]
                                                                error_code: Some(*failure_code),
 #[cfg(test)]
index e7f61c6a3dfeefb23f845b7a47d6ab0efa5abf60..5ee88de30e609d21e858cadaefd848aa60ed17bb 100644 (file)
@@ -882,7 +882,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, ref error_code, ref error_data } => {
+                       Event::PaymentFailed { ref payment_hash, rejected_by_dest, rejected_by_first_hop: _, 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");
index c7a79e32210639fddcd4c3d01ab16e6f311248a3..a4a547da103aac66732c7d07dfcc8b0d74c1a3f0 100644 (file)
@@ -5952,8 +5952,9 @@ 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 error_code, ref error_data } => {
+               &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref rejected_by_first_hop, ref error_code, ref error_data } => {
                        assert_eq!(our_payment_hash.clone(), *payment_hash);
+                       assert_eq!(*rejected_by_first_hop, false);
                        assert_eq!(*rejected_by_dest, false);
                        assert_eq!(*error_code, None);
                        assert_eq!(*error_data, None);
@@ -6032,8 +6033,9 @@ 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 error_code, ref error_data } => {
+               &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref rejected_by_first_hop, ref error_code, ref error_data } => {
                        assert_eq!(payment_hash_2.clone(), *payment_hash);
+                       assert_eq!(*rejected_by_first_hop, false);
                        assert_eq!(*rejected_by_dest, false);
                        assert_eq!(*error_code, None);
                        assert_eq!(*error_data, None);
index 147ac73105e26bdf9c2c6b02f8d6519a053cc9cf..33c4b86dd902996e39db8aa31357dfc1eef2b5f6 100644 (file)
@@ -36,11 +36,11 @@ use std::io;
 
 use ln::functional_test_utils::*;
 
-fn run_onion_failure_test<F1,F2>(_name: &str, test_case: u8, nodes: &Vec<Node>, route: &Route, payment_hash: &PaymentHash, callback_msg: F1, callback_node: F2, expected_retryable: bool, expected_error_code: Option<u16>, expected_channel_update: Option<HTLCFailChannelUpdate>)
+fn run_onion_failure_test<F1,F2>(_name: &str, test_case: u8, nodes: &Vec<Node>, route: &Route, payment_hash: &PaymentHash, callback_msg: F1, callback_node: F2, expected_retryable: bool, expected_first_hop_rejection: bool, expected_error_code: Option<u16>, expected_channel_update: Option<HTLCFailChannelUpdate>)
        where F1: for <'a> FnMut(&'a mut msgs::UpdateAddHTLC),
                                F2: FnMut(),
 {
-       run_onion_failure_test_with_fail_intercept(_name, test_case, nodes, route, payment_hash, callback_msg, |_|{}, callback_node, expected_retryable, expected_error_code, expected_channel_update);
+       run_onion_failure_test_with_fail_intercept(_name, test_case, nodes, route, payment_hash, callback_msg, |_|{}, callback_node, expected_retryable, expected_first_hop_rejection, expected_error_code, expected_channel_update);
 }
 
 // test_case
@@ -50,7 +50,7 @@ fn run_onion_failure_test<F1,F2>(_name: &str, test_case: u8, nodes: &Vec<Node>,
 // 3: final node fails backward (but tamper onion payloads from node0)
 // 100: trigger error in the intermediate node and tamper returning fail_htlc
 // 200: trigger error in the final node and tamper returning fail_htlc
-fn run_onion_failure_test_with_fail_intercept<F1,F2,F3>(_name: &str, test_case: u8, nodes: &Vec<Node>, route: &Route, payment_hash: &PaymentHash, mut callback_msg: F1, mut callback_fail: F2, mut callback_node: F3, expected_retryable: bool, expected_error_code: Option<u16>, expected_channel_update: Option<HTLCFailChannelUpdate>)
+fn run_onion_failure_test_with_fail_intercept<F1,F2,F3>(_name: &str, test_case: u8, nodes: &Vec<Node>, route: &Route, payment_hash: &PaymentHash, mut callback_msg: F1, mut callback_fail: F2, mut callback_node: F3, expected_retryable: bool, expected_first_hop_rejection: bool, expected_error_code: Option<u16>, expected_channel_update: Option<HTLCFailChannelUpdate>)
        where F1: for <'a> FnMut(&'a mut msgs::UpdateAddHTLC),
                                F2: for <'a> FnMut(&'a mut msgs::UpdateFailHTLC),
                                F3: FnMut(),
@@ -159,7 +159,8 @@ 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 error_code, error_data: _ } = &events[0] {
+       if let &Event::PaymentFailed { payment_hash:_, ref rejected_by_dest, ref rejected_by_first_hop, ref error_code, error_data: _ } = &events[0] {
+               assert_eq!(*rejected_by_first_hop, expected_first_hop_rejection);
                assert_eq!(*rejected_by_dest, !expected_retryable);
                assert_eq!(*error_code, expected_error_code);
        } else {
@@ -288,7 +289,7 @@ fn test_onion_failure() {
                // describing a length-1 TLV payload, which is obviously bogus.
                new_payloads[0].data[0] = 1;
                msg.onion_routing_packet = onion_utils::construct_onion_packet_bogus_hopdata(new_payloads, onion_keys, [0; 32], &payment_hash);
-       }, ||{}, true, Some(PERM|22), Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));//XXX incremented channels idx here
+       }, ||{}, true, true, Some(PERM|22), Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));//XXX incremented channels idx here
 
        // final node failure
        run_onion_failure_test("invalid_realm", 3, &nodes, &route, &payment_hash, |msg| {
@@ -304,7 +305,7 @@ fn test_onion_failure() {
                // length-1 TLV payload, which is obviously bogus.
                new_payloads[1].data[0] = 1;
                msg.onion_routing_packet = onion_utils::construct_onion_packet_bogus_hopdata(new_payloads, onion_keys, [0; 32], &payment_hash);
-       }, ||{}, false, Some(PERM|22), Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
+       }, ||{}, false, false, Some(PERM|22), Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
 
        // the following three with run_onion_failure_test_with_fail_intercept() test only the origin node
        // receiving simulated fail messages
@@ -317,7 +318,7 @@ fn test_onion_failure() {
                let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
                let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
                msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[0].shared_secret[..], NODE|2, &[0;0]);
-       }, ||{}, true, Some(NODE|2), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: false}));
+       }, ||{}, true, true, Some(NODE|2), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: false}));
 
        // final node failure
        run_onion_failure_test_with_fail_intercept("temporary_node_failure", 200, &nodes, &route, &payment_hash, |_msg| {}, |msg| {
@@ -327,7 +328,7 @@ fn test_onion_failure() {
                msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[1].shared_secret[..], NODE|2, &[0;0]);
        }, ||{
                nodes[2].node.fail_htlc_backwards(&payment_hash, &None);
-       }, true, Some(NODE|2), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: false}));
+       }, true, false, Some(NODE|2), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: false}));
 
        // intermediate node failure
        run_onion_failure_test_with_fail_intercept("permanent_node_failure", 100, &nodes, &route, &payment_hash, |msg| {
@@ -336,7 +337,7 @@ fn test_onion_failure() {
                let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
                let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
                msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[0].shared_secret[..], PERM|NODE|2, &[0;0]);
-       }, ||{}, true, Some(PERM|NODE|2), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: true}));
+       }, ||{}, true, true, Some(PERM|NODE|2), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: true}));
 
        // final node failure
        run_onion_failure_test_with_fail_intercept("permanent_node_failure", 200, &nodes, &route, &payment_hash, |_msg| {}, |msg| {
@@ -345,7 +346,7 @@ fn test_onion_failure() {
                msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[1].shared_secret[..], PERM|NODE|2, &[0;0]);
        }, ||{
                nodes[2].node.fail_htlc_backwards(&payment_hash, &None);
-       }, false, Some(PERM|NODE|2), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: true}));
+       }, false, false, Some(PERM|NODE|2), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: true}));
 
        // intermediate node failure
        run_onion_failure_test_with_fail_intercept("required_node_feature_missing", 100, &nodes, &route, &payment_hash, |msg| {
@@ -356,7 +357,7 @@ fn test_onion_failure() {
                msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[0].shared_secret[..], PERM|NODE|3, &[0;0]);
        }, ||{
                nodes[2].node.fail_htlc_backwards(&payment_hash, &None);
-       }, true, Some(PERM|NODE|3), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: true}));
+       }, true, true, Some(PERM|NODE|3), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: true}));
 
        // final node failure
        run_onion_failure_test_with_fail_intercept("required_node_feature_missing", 200, &nodes, &route, &payment_hash, |_msg| {}, |msg| {
@@ -365,15 +366,15 @@ fn test_onion_failure() {
                msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[1].shared_secret[..], PERM|NODE|3, &[0;0]);
        }, ||{
                nodes[2].node.fail_htlc_backwards(&payment_hash, &None);
-       }, false, Some(PERM|NODE|3), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: true}));
+       }, false, false, Some(PERM|NODE|3), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: true}));
 
-       run_onion_failure_test("invalid_onion_version", 0, &nodes, &route, &payment_hash, |msg| { msg.onion_routing_packet.version = 1; }, ||{}, true,
+       run_onion_failure_test("invalid_onion_version", 0, &nodes, &route, &payment_hash, |msg| { msg.onion_routing_packet.version = 1; }, ||{}, true, true,
                Some(BADONION|PERM|4), None);
 
-       run_onion_failure_test("invalid_onion_hmac", 0, &nodes, &route, &payment_hash, |msg| { msg.onion_routing_packet.hmac = [3; 32]; }, ||{}, true,
+       run_onion_failure_test("invalid_onion_hmac", 0, &nodes, &route, &payment_hash, |msg| { msg.onion_routing_packet.hmac = [3; 32]; }, ||{}, true, true,
                Some(BADONION|PERM|5), None);
 
-       run_onion_failure_test("invalid_onion_key", 0, &nodes, &route, &payment_hash, |msg| { msg.onion_routing_packet.public_key = Err(secp256k1::Error::InvalidPublicKey);}, ||{}, true,
+       run_onion_failure_test("invalid_onion_key", 0, &nodes, &route, &payment_hash, |msg| { msg.onion_routing_packet.public_key = Err(secp256k1::Error::InvalidPublicKey);}, ||{}, true, true,
                Some(BADONION|PERM|6), None);
 
        run_onion_failure_test_with_fail_intercept("temporary_channel_failure", 100, &nodes, &route, &payment_hash, |msg| {
@@ -382,7 +383,7 @@ fn test_onion_failure() {
                let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
                let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
                msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[0].shared_secret[..], UPDATE|7, &ChannelUpdate::dummy().encode_with_len()[..]);
-       }, ||{}, true, Some(UPDATE|7), Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
+       }, ||{}, true, true, Some(UPDATE|7), Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
 
        run_onion_failure_test_with_fail_intercept("permanent_channel_failure", 100, &nodes, &route, &payment_hash, |msg| {
                msg.amount_msat -= 1;
@@ -391,7 +392,7 @@ fn test_onion_failure() {
                let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
                msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[0].shared_secret[..], PERM|8, &[0;0]);
                // short_channel_id from the processing node
-       }, ||{}, true, Some(PERM|8), Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
+       }, ||{}, true, true, Some(PERM|8), Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
 
        run_onion_failure_test_with_fail_intercept("required_channel_feature_missing", 100, &nodes, &route, &payment_hash, |msg| {
                msg.amount_msat -= 1;
@@ -400,18 +401,18 @@ fn test_onion_failure() {
                let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
                msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[0].shared_secret[..], PERM|9, &[0;0]);
                // short_channel_id from the processing node
-       }, ||{}, true, Some(PERM|9), Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
+       }, ||{}, true, true, Some(PERM|9), Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));//XXX
 
        let mut bogus_route = route.clone();
        bogus_route.paths[0][1].short_channel_id -= 1;
-       run_onion_failure_test("unknown_next_peer", 0, &nodes, &bogus_route, &payment_hash, |_| {}, ||{}, true, Some(PERM|10),
+       run_onion_failure_test("unknown_next_peer", 0, &nodes, &bogus_route, &payment_hash, |_| {}, ||{}, true, true, Some(PERM|10),
          Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: bogus_route.paths[0][1].short_channel_id, is_permanent:true}));
 
        let amt_to_forward = nodes[1].node.channel_state.lock().unwrap().by_id.get(&channels[1].2).unwrap().get_counterparty_htlc_minimum_msat() - 1;
        let mut bogus_route = route.clone();
        let route_len = bogus_route.paths[0].len();
        bogus_route.paths[0][route_len-1].fee_msat = amt_to_forward;
-       run_onion_failure_test("amount_below_minimum", 0, &nodes, &bogus_route, &payment_hash, |_| {}, ||{}, true, Some(UPDATE|11), Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
+       run_onion_failure_test("amount_below_minimum", 0, &nodes, &bogus_route, &payment_hash, |_| {}, ||{}, true, true, Some(UPDATE|11), Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
 
        // Test a positive test-case with one extra msat, meeting the minimum.
        bogus_route.paths[0][route_len-1].fee_msat = amt_to_forward + 1;
@@ -422,30 +423,30 @@ fn test_onion_failure() {
        //invalid channel_update cases.
        run_onion_failure_test("fee_insufficient", 0, &nodes, &route, &payment_hash, |msg| {
                msg.amount_msat -= 1;
-       }, || {}, true, Some(UPDATE|12), Some(msgs::HTLCFailChannelUpdate::ChannelClosed { short_channel_id: channels[0].0.contents.short_channel_id, is_permanent: true}));
+       }, || {}, true, true, Some(UPDATE|12), Some(msgs::HTLCFailChannelUpdate::ChannelClosed { short_channel_id: channels[0].0.contents.short_channel_id, is_permanent: true}));
 
        run_onion_failure_test("incorrect_cltv_expiry", 0, &nodes, &route, &payment_hash, |msg| {
                // need to violate: cltv_expiry - cltv_expiry_delta >= outgoing_cltv_value
                msg.cltv_expiry -= 1;
-       }, || {}, true, Some(UPDATE|13), Some(msgs::HTLCFailChannelUpdate::ChannelClosed { short_channel_id: channels[0].0.contents.short_channel_id, is_permanent: true}));
+       }, || {}, true, true, Some(UPDATE|13), Some(msgs::HTLCFailChannelUpdate::ChannelClosed { short_channel_id: channels[0].0.contents.short_channel_id, is_permanent: true}));
 
        run_onion_failure_test("expiry_too_soon", 0, &nodes, &route, &payment_hash, |msg| {
                let height = msg.cltv_expiry - CLTV_CLAIM_BUFFER - LATENCY_GRACE_PERIOD_BLOCKS + 1;
                connect_blocks(&nodes[0], height - nodes[0].best_block_info().1);
                connect_blocks(&nodes[1], height - nodes[1].best_block_info().1);
                connect_blocks(&nodes[2], height - nodes[2].best_block_info().1);
-       }, ||{}, true, Some(UPDATE|14), Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
+       }, ||{}, true, true, Some(UPDATE|14), Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
 
        run_onion_failure_test("unknown_payment_hash", 2, &nodes, &route, &payment_hash, |_| {}, || {
                nodes[2].node.fail_htlc_backwards(&payment_hash, &None);
-       }, false, Some(PERM|15), None);
+       }, false, false, Some(PERM|15), None);
 
        run_onion_failure_test("final_expiry_too_soon", 1, &nodes, &route, &payment_hash, |msg| {
                let height = msg.cltv_expiry - CLTV_CLAIM_BUFFER - LATENCY_GRACE_PERIOD_BLOCKS + 1;
                connect_blocks(&nodes[0], height - nodes[0].best_block_info().1);
                connect_blocks(&nodes[1], height - nodes[1].best_block_info().1);
                connect_blocks(&nodes[2], height - nodes[2].best_block_info().1);
-       }, || {}, true, Some(17), None);
+       }, || {}, true, false, Some(17), None);
 
        run_onion_failure_test("final_incorrect_cltv_expiry", 1, &nodes, &route, &payment_hash, |_| {}, || {
                for (_, pending_forwards) in nodes[1].node.channel_state.lock().unwrap().forward_htlcs.iter_mut() {
@@ -457,7 +458,7 @@ fn test_onion_failure() {
                                }
                        }
                }
-       }, true, Some(18), None);
+       }, true, false, Some(18), None);
 
        run_onion_failure_test("final_incorrect_htlc_amount", 1, &nodes, &route, &payment_hash, |_| {}, || {
                // violate amt_to_forward > msg.amount_msat
@@ -470,13 +471,13 @@ fn test_onion_failure() {
                                }
                        }
                }
-       }, true, Some(19), None);
+       }, true, false, Some(19), None);
 
        run_onion_failure_test("channel_disabled", 0, &nodes, &route, &payment_hash, |_| {}, || {
                // disconnect event to the channel between nodes[1] ~ nodes[2]
                nodes[1].node.peer_disconnected(&nodes[2].node.get_our_node_id(), false);
                nodes[2].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
-       }, true, Some(UPDATE|20), Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
+       }, true, true, Some(UPDATE|20), Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
        reconnect_nodes(&nodes[1], &nodes[2], (false, false), (0, 0), (0, 0), (0, 0), (0, 0), (false, false));
 
        run_onion_failure_test("expiry_too_far", 0, &nodes, &route, &payment_hash, |msg| {
@@ -489,7 +490,7 @@ fn test_onion_failure() {
                let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash);
                msg.cltv_expiry = htlc_cltv;
                msg.onion_routing_packet = onion_packet;
-       }, ||{}, true, Some(21), None);
+       }, ||{}, true, true, Some(21), None);
 }
 
 
index 1c45da14a382c78aef88acda8a1a3d56ea0c1dca..4364b815b1c6ae6dfc605a61b8d553fd7923f18f 100644 (file)
@@ -327,7 +327,7 @@ pub(super) fn build_first_hop_failure_packet(shared_secret: &[u8], failure_type:
 /// OutboundRoute).
 /// Returns update, a boolean indicating that the payment itself failed, and the error code.
 #[inline]
-pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &Secp256k1<T>, logger: &L, htlc_source: &HTLCSource, mut packet_decrypted: Vec<u8>) -> (Option<msgs::HTLCFailChannelUpdate>, bool, Option<u16>, Option<Vec<u8>>) where L::Target: Logger {
+pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &Secp256k1<T>, logger: &L, htlc_source: &HTLCSource, mut packet_decrypted: Vec<u8>) -> (Option<msgs::HTLCFailChannelUpdate>, bool, bool, Option<u16>, Option<Vec<u8>>) where L::Target: Logger {
        if let &HTLCSource::OutboundRoute { ref path, ref session_priv, ref first_hop_htlc_msat } = htlc_source {
                let mut res = None;
                let mut htlc_msat = *first_hop_htlc_msat;
@@ -335,6 +335,7 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
                let mut error_packet_ret = None;
                let mut next_route_hop_ix = 0;
                let mut is_from_final_node = false;
+               let mut is_from_first_node = false;
 
                // Handle packed channel/node updates for passing back for the route handler
                construct_onion_keys_callback(secp_ctx, path, session_priv, |shared_secret, _, _, route_hop| {
@@ -352,6 +353,7 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
                        chacha.process(&packet_decrypted, &mut decryption_tmp[..]);
                        packet_decrypted = decryption_tmp;
 
+                       is_from_first_node = path.first().unwrap().pubkey == route_hop.pubkey;
                        is_from_final_node = path.last().unwrap().pubkey == route_hop.pubkey;
 
                        if let Ok(err_packet) = msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(&packet_decrypted)) {
@@ -466,11 +468,11 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
                        }
                }).expect("Route that we sent via spontaneously grew invalid keys in the middle of it?");
                if let Some((channel_update, payment_retryable)) = res {
-                       (channel_update, payment_retryable, error_code_ret, error_packet_ret)
+                       (channel_update, payment_retryable, is_from_first_node, error_code_ret, error_packet_ret)
                } else {
                        // only not set either packet unparseable or hmac does not match with any
                        // payment not retryable only when garbage is from the final node
-                       (None, !is_from_final_node, None, None)
+                       (None, !is_from_final_node, is_from_first_node, None, None)
                }
        } else { unreachable!(); }
 }
index 118503c040d09fafc6d6783a7f5b1edfe839430b..ffd761770c1e06998c2409e3dfe0f049aac72217 100644 (file)
@@ -107,7 +107,17 @@ pub enum Event {
                /// Indicates the payment was rejected for some reason by the recipient. This implies that
                /// the payment has failed, not just the route in question. If this is not set, you may
                /// retry the payment via a different route.
+               ///
+               /// Note that if the destination node returned garbage which we were unable to
+               /// understanding, this will *not* be set, indicating that retrying the payment over
+               /// another path is unlikely to solve the issue.
                rejected_by_dest: bool,
+               /// Indicates the payment was rejected by the first hop in the route. This may be useful to
+               /// detect when an otherwise-trusted peer rejected a payment.
+               ///
+               /// Note that this is not set if we reject the payment ourselves (eg because our connection
+               /// to the next-hop peer was closed).
+               rejected_by_first_hop: bool,
 #[cfg(test)]
                error_code: Option<u16>,
 #[cfg(test)]
@@ -155,7 +165,7 @@ impl Writeable for Event {
                                3u8.write(writer)?;
                                payment_preimage.write(writer)?;
                        },
-                       &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest,
+                       &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref rejected_by_first_hop,
                                #[cfg(test)]
                                ref error_code,
                                #[cfg(test)]
@@ -164,6 +174,7 @@ impl Writeable for Event {
                                4u8.write(writer)?;
                                payment_hash.write(writer)?;
                                rejected_by_dest.write(writer)?;
+                               rejected_by_first_hop.write(writer)?;
                                #[cfg(test)]
                                error_code.write(writer)?;
                                #[cfg(test)]
@@ -204,6 +215,7 @@ impl MaybeReadable for Event {
                        4u8 => Ok(Some(Event::PaymentFailed {
                                        payment_hash: Readable::read(reader)?,
                                        rejected_by_dest: Readable::read(reader)?,
+                                       rejected_by_first_hop: Readable::read(reader)?,
                                        #[cfg(test)]
                                        error_code: Readable::read(reader)?,
                                        #[cfg(test)]