Merge pull request #1115 from TheBlueMatt/2021-10-expose-addr-vec
authorMatt Corallo <649246+TheBlueMatt@users.noreply.github.com>
Wed, 13 Oct 2021 16:54:09 +0000 (16:54 +0000)
committerGitHub <noreply@github.com>
Wed, 13 Oct 2021 16:54:09 +0000 (16:54 +0000)
Expose ReadOnlyNetworkGraph::get_addresses to C by cloning result

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/routing/network_graph.rs
lightning/src/util/events.rs

index 4a3c69f9995f882ecf5933117a79720f7cc81561..298f88ce0cc018162cf5f2a3201512641b1cd8fd 100644 (file)
@@ -3016,6 +3016,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                                                        network_update: None,
                                                                        all_paths_failed: payment.get().remaining_parts() == 0,
                                                                        path: path.clone(),
+                                                                       short_channel_id: None,
                                                                        #[cfg(test)]
                                                                        error_code: None,
                                                                        #[cfg(test)]
@@ -3069,9 +3070,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 (network_update, payment_retryable, onion_error_code, onion_error_data) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone());
+                                               let (network_update, short_channel_id, payment_retryable, onion_error_code, onion_error_data) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone());
 #[cfg(not(test))]
-                                               let (network_update, payment_retryable, _, _) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone());
+                                               let (network_update, short_channel_id, payment_retryable, _, _) = 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!
@@ -3082,6 +3083,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                                                network_update,
                                                                all_paths_failed,
                                                                path: path.clone(),
+                                                               short_channel_id,
 #[cfg(test)]
                                                                error_code: onion_error_code,
 #[cfg(test)]
@@ -3109,6 +3111,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                                                network_update: None,
                                                                all_paths_failed,
                                                                path: path.clone(),
+                                                               short_channel_id: Some(path.first().unwrap().short_channel_id),
 #[cfg(test)]
                                                                error_code: Some(*failure_code),
 #[cfg(test)]
index 00a680dfe83474d9493b943e8b16ba9100e20ffe..0d01961cd1b3f023f7bec7c3a6559b330d4bf34d 100644 (file)
@@ -973,8 +973,9 @@ macro_rules! commitment_signed_dance {
 macro_rules! get_payment_preimage_hash {
        ($dest_node: expr) => {
                {
-                       let payment_preimage = PaymentPreimage([*$dest_node.network_payment_count.borrow(); 32]);
-                       *$dest_node.network_payment_count.borrow_mut() += 1;
+                       let mut payment_count = $dest_node.network_payment_count.borrow_mut();
+                       let payment_preimage = PaymentPreimage([*payment_count; 32]);
+                       *payment_count += 1;
                        let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner());
                        let payment_secret = $dest_node.node.create_inbound_payment_for_hash(payment_hash, None, 7200, 0).unwrap();
                        (payment_preimage, payment_hash, payment_secret)
@@ -989,7 +990,9 @@ macro_rules! get_route_and_payment_hash {
                let net_graph_msg_handler = &$send_node.net_graph_msg_handler;
                let route = get_route(&$send_node.node.get_our_node_id(),
                        &net_graph_msg_handler.network_graph,
-                       &$recv_node.node.get_our_node_id(), None, None, &Vec::new(), $recv_value, TEST_FINAL_CLTV, $send_node.logger).unwrap();
+                       &$recv_node.node.get_our_node_id(), None,
+                       Some(&$send_node.node.list_usable_channels().iter().map(|a| a).collect::<Vec<_>>()),
+                       &Vec::new(), $recv_value, TEST_FINAL_CLTV, $send_node.logger).unwrap();
                (route, payment_hash, payment_preimage, payment_secret)
        }}
 }
index b412cc2bcca57b24fddb8daa2136e68893628e2d..9817d6bed62a20b1c5818bfceaf077f40a36b08e 100644 (file)
@@ -6075,11 +6075,12 @@ 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::PaymentPathFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref error_code, ref error_data, ref all_paths_failed, path: _ } => {
+               &Event::PaymentPathFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref all_paths_failed, path: _, ref short_channel_id, ref error_code, ref error_data } => {
                        assert_eq!(our_payment_hash.clone(), *payment_hash);
                        assert_eq!(*rejected_by_dest, false);
                        assert_eq!(*all_paths_failed, true);
                        assert_eq!(*network_update, None);
+                       assert_eq!(*short_channel_id, None);
                        assert_eq!(*error_code, None);
                        assert_eq!(*error_data, None);
                },
@@ -6162,11 +6163,12 @@ 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::PaymentPathFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref error_code, ref error_data, ref all_paths_failed, path: _ } => {
+               &Event::PaymentPathFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref all_paths_failed, path: _, ref short_channel_id, ref error_code, ref error_data } => {
                        assert_eq!(payment_hash_2.clone(), *payment_hash);
                        assert_eq!(*rejected_by_dest, false);
                        assert_eq!(*all_paths_failed, true);
                        assert_eq!(*network_update, None);
+                       assert_eq!(*short_channel_id, None);
                        assert_eq!(*error_code, None);
                        assert_eq!(*error_data, None);
                },
index 0e2d081c83bf862c7a3a80165c9f78b8e87ba1ec..cf5be469cc30cab6934634ac71c4f08d1dbc4723 100644 (file)
@@ -40,11 +40,11 @@ use core::default::Default;
 
 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, payment_secret: &PaymentSecret, callback_msg: F1, callback_node: F2, expected_retryable: bool, expected_error_code: Option<u16>, expected_channel_update: Option<NetworkUpdate>)
+fn run_onion_failure_test<F1,F2>(_name: &str, test_case: u8, nodes: &Vec<Node>, route: &Route, payment_hash: &PaymentHash, payment_secret: &PaymentSecret, callback_msg: F1, callback_node: F2, expected_retryable: bool, expected_error_code: Option<u16>, expected_channel_update: Option<NetworkUpdate>, expected_short_channel_id: Option<u64>)
        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, payment_secret, 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, payment_secret, callback_msg, |_|{}, callback_node, expected_retryable, expected_error_code, expected_channel_update, expected_short_channel_id);
 }
 
 // test_case
@@ -54,7 +54,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, payment_secret: &PaymentSecret, mut callback_msg: F1, mut callback_fail: F2, mut callback_node: F3, expected_retryable: bool, expected_error_code: Option<u16>, expected_channel_update: Option<NetworkUpdate>)
+fn run_onion_failure_test_with_fail_intercept<F1,F2,F3>(_name: &str, test_case: u8, nodes: &Vec<Node>, route: &Route, payment_hash: &PaymentHash, payment_secret: &PaymentSecret, mut callback_msg: F1, mut callback_fail: F2, mut callback_node: F3, expected_retryable: bool, expected_error_code: Option<u16>, expected_channel_update: Option<NetworkUpdate>, expected_short_channel_id: Option<u64>)
        where F1: for <'a> FnMut(&'a mut msgs::UpdateAddHTLC),
                                F2: for <'a> FnMut(&'a mut msgs::UpdateFailHTLC),
                                F3: FnMut(),
@@ -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::PaymentPathFailed { payment_hash:_, ref rejected_by_dest, ref network_update, ref error_code, error_data: _, ref all_paths_failed, path: _ } = &events[0] {
+       if let &Event::PaymentPathFailed { payment_hash: _, ref rejected_by_dest, ref network_update, ref all_paths_failed, path: _, ref short_channel_id, ref error_code, error_data: _ } = &events[0] {
                assert_eq!(*rejected_by_dest, !expected_retryable);
                assert_eq!(*all_paths_failed, true);
                assert_eq!(*error_code, expected_error_code);
@@ -197,20 +197,28 @@ fn run_onion_failure_test_with_fail_intercept<F1,F2,F3>(_name: &str, test_case:
                } else {
                        assert!(network_update.is_none());
                }
+               if let Some(expected_short_channel_id) = expected_short_channel_id {
+                       match short_channel_id {
+                               Some(short_channel_id) => assert_eq!(*short_channel_id, expected_short_channel_id),
+                               None => panic!("Expected short channel id"),
+                       }
+               } else {
+                       assert!(short_channel_id.is_none());
+               }
        } else {
                panic!("Unexpected event");
        }
 }
 
 impl msgs::ChannelUpdate {
-       fn dummy() -> msgs::ChannelUpdate {
+       fn dummy(short_channel_id: u64) -> msgs::ChannelUpdate {
                use bitcoin::secp256k1::ffi::Signature as FFISignature;
                use bitcoin::secp256k1::Signature;
                msgs::ChannelUpdate {
                        signature: Signature::from(unsafe { FFISignature::new() }),
                        contents: msgs::UnsignedChannelUpdate {
                                chain_hash: BlockHash::hash(&vec![0u8][..]),
-                               short_channel_id: 0,
+                               short_channel_id,
                                timestamp: 0,
                                flags: 0,
                                cltv_expiry_delta: 0,
@@ -269,10 +277,13 @@ fn test_fee_failures() {
        pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], 40_000, payment_hash_success, payment_secret_success);
        claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage_success);
 
+       // If the hop gives fee_insufficient but enough fees were provided, then the previous hop
+       // malleated the payment before forwarding, taking funds when they shouldn't have.
        let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[2]);
+       let short_channel_id = channels[0].0.contents.short_channel_id;
        run_onion_failure_test("fee_insufficient", 0, &nodes, &route, &payment_hash, &payment_secret, |msg| {
                msg.amount_msat -= 1;
-       }, || {}, true, Some(UPDATE|12), Some(NetworkUpdate::ChannelClosed { short_channel_id: channels[0].0.contents.short_channel_id, is_permanent: true}));
+       }, || {}, true, Some(UPDATE|12), Some(NetworkUpdate::ChannelClosed { short_channel_id, is_permanent: true}), Some(short_channel_id));
 
        // In an earlier version, we spuriously failed to forward payments if the expected feerate
        // changed between the channel open and the payment.
@@ -320,6 +331,7 @@ fn test_onion_failure() {
        send_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 40000);
 
        // intermediate node failure
+       let short_channel_id = channels[1].0.contents.short_channel_id;
        run_onion_failure_test("invalid_realm", 0, &nodes, &route, &payment_hash, &payment_secret, |msg| {
                let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
                let cur_height = nodes[0].best_block_info().1 + 1;
@@ -333,9 +345,10 @@ 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(NetworkUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
+       }, ||{}, true, Some(PERM|22), Some(NetworkUpdate::ChannelClosed{short_channel_id, is_permanent: true}), Some(short_channel_id));
 
        // final node failure
+       let short_channel_id = channels[1].0.contents.short_channel_id;
        run_onion_failure_test("invalid_realm", 3, &nodes, &route, &payment_hash, &payment_secret, |msg| {
                let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
                let cur_height = nodes[0].best_block_info().1 + 1;
@@ -349,7 +362,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(NetworkUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
+       }, ||{}, false, Some(PERM|22), Some(NetworkUpdate::ChannelClosed{short_channel_id, is_permanent: true}), Some(short_channel_id));
 
        // the following three with run_onion_failure_test_with_fail_intercept() test only the origin node
        // receiving simulated fail messages
@@ -362,7 +375,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(NetworkUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: false}));
+       }, ||{}, true, Some(NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: false}), Some(route.paths[0][0].short_channel_id));
 
        // final node failure
        run_onion_failure_test_with_fail_intercept("temporary_node_failure", 200, &nodes, &route, &payment_hash, &payment_secret, |_msg| {}, |msg| {
@@ -372,7 +385,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);
-       }, true, Some(NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: false}));
+       }, true, Some(NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: false}), Some(route.paths[0][1].short_channel_id));
        let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[2]);
 
        // intermediate node failure
@@ -382,7 +395,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(NetworkUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: true}));
+       }, ||{}, true, Some(PERM|NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: true}), Some(route.paths[0][0].short_channel_id));
 
        // final node failure
        run_onion_failure_test_with_fail_intercept("permanent_node_failure", 200, &nodes, &route, &payment_hash, &payment_secret, |_msg| {}, |msg| {
@@ -391,7 +404,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);
-       }, false, Some(PERM|NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: true}));
+       }, false, Some(PERM|NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: true}), Some(route.paths[0][1].short_channel_id));
        let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[2]);
 
        // intermediate node failure
@@ -403,7 +416,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);
-       }, true, Some(PERM|NODE|3), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: true}));
+       }, true, Some(PERM|NODE|3), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: true}), Some(route.paths[0][0].short_channel_id));
 
        // final node failure
        run_onion_failure_test_with_fail_intercept("required_node_feature_missing", 200, &nodes, &route, &payment_hash, &payment_secret, |_msg| {}, |msg| {
@@ -412,26 +425,31 @@ 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);
-       }, false, Some(PERM|NODE|3), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: true}));
+       }, false, Some(PERM|NODE|3), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: true}), Some(route.paths[0][1].short_channel_id));
        let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[2]);
 
+       // Our immediate peer sent UpdateFailMalformedHTLC because it couldn't understand the onion in
+       // the UpdateAddHTLC that we sent.
+       let short_channel_id = channels[0].0.contents.short_channel_id;
        run_onion_failure_test("invalid_onion_version", 0, &nodes, &route, &payment_hash, &payment_secret, |msg| { msg.onion_routing_packet.version = 1; }, ||{}, true,
-               Some(BADONION|PERM|4), None);
+               Some(BADONION|PERM|4), None, Some(short_channel_id));
 
        run_onion_failure_test("invalid_onion_hmac", 0, &nodes, &route, &payment_hash, &payment_secret, |msg| { msg.onion_routing_packet.hmac = [3; 32]; }, ||{}, true,
-               Some(BADONION|PERM|5), None);
+               Some(BADONION|PERM|5), None, Some(short_channel_id));
 
        run_onion_failure_test("invalid_onion_key", 0, &nodes, &route, &payment_hash, &payment_secret, |msg| { msg.onion_routing_packet.public_key = Err(secp256k1::Error::InvalidPublicKey);}, ||{}, true,
-               Some(BADONION|PERM|6), None);
+               Some(BADONION|PERM|6), None, Some(short_channel_id));
 
+       let short_channel_id = channels[1].0.contents.short_channel_id;
        run_onion_failure_test_with_fail_intercept("temporary_channel_failure", 100, &nodes, &route, &payment_hash, &payment_secret, |msg| {
                msg.amount_msat -= 1;
        }, |msg| {
                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(NetworkUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
+               msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[0].shared_secret[..], UPDATE|7, &ChannelUpdate::dummy(short_channel_id).encode_with_len()[..]);
+       }, ||{}, true, Some(UPDATE|7), Some(NetworkUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy(short_channel_id)}), Some(short_channel_id));
 
+       let short_channel_id = channels[1].0.contents.short_channel_id;
        run_onion_failure_test_with_fail_intercept("permanent_channel_failure", 100, &nodes, &route, &payment_hash, &payment_secret, |msg| {
                msg.amount_msat -= 1;
        }, |msg| {
@@ -439,8 +457,9 @@ 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(NetworkUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
+       }, ||{}, true, Some(PERM|8), Some(NetworkUpdate::ChannelClosed{short_channel_id, is_permanent: true}), Some(short_channel_id));
 
+       let short_channel_id = channels[1].0.contents.short_channel_id;
        run_onion_failure_test_with_fail_intercept("required_channel_feature_missing", 100, &nodes, &route, &payment_hash, &payment_secret, |msg| {
                msg.amount_msat -= 1;
        }, |msg| {
@@ -448,18 +467,20 @@ 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(NetworkUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
+       }, ||{}, true, Some(PERM|9), Some(NetworkUpdate::ChannelClosed{short_channel_id, is_permanent: true}), Some(short_channel_id));
 
        let mut bogus_route = route.clone();
        bogus_route.paths[0][1].short_channel_id -= 1;
+       let short_channel_id = bogus_route.paths[0][1].short_channel_id;
        run_onion_failure_test("unknown_next_peer", 0, &nodes, &bogus_route, &payment_hash, &payment_secret, |_| {}, ||{}, true, Some(PERM|10),
-         Some(NetworkUpdate::ChannelClosed{short_channel_id: bogus_route.paths[0][1].short_channel_id, is_permanent:true}));
+         Some(NetworkUpdate::ChannelClosed{short_channel_id, is_permanent:true}), Some(short_channel_id));
 
+       let short_channel_id = channels[1].0.contents.short_channel_id;
        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, &payment_secret, |_| {}, ||{}, true, Some(UPDATE|11), Some(NetworkUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
+       run_onion_failure_test("amount_below_minimum", 0, &nodes, &bogus_route, &payment_hash, &payment_secret, |_| {}, ||{}, true, Some(UPDATE|11), Some(NetworkUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy(short_channel_id)}), Some(short_channel_id));
 
        // 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;
@@ -468,25 +489,28 @@ fn test_onion_failure() {
 
        //TODO: with new config API, we will be able to generate both valid and
        //invalid channel_update cases.
+       let short_channel_id = channels[0].0.contents.short_channel_id;
        run_onion_failure_test("fee_insufficient", 0, &nodes, &route, &payment_hash, &payment_secret, |msg| {
                msg.amount_msat -= 1;
-       }, || {}, true, Some(UPDATE|12), Some(NetworkUpdate::ChannelClosed { short_channel_id: channels[0].0.contents.short_channel_id, is_permanent: true}));
+       }, || {}, true, Some(UPDATE|12), Some(NetworkUpdate::ChannelClosed { short_channel_id, is_permanent: true}), Some(short_channel_id));
 
+       let short_channel_id = channels[0].0.contents.short_channel_id;
        run_onion_failure_test("incorrect_cltv_expiry", 0, &nodes, &route, &payment_hash, &payment_secret, |msg| {
                // need to violate: cltv_expiry - cltv_expiry_delta >= outgoing_cltv_value
                msg.cltv_expiry -= 1;
-       }, || {}, true, Some(UPDATE|13), Some(NetworkUpdate::ChannelClosed { short_channel_id: channels[0].0.contents.short_channel_id, is_permanent: true}));
+       }, || {}, true, Some(UPDATE|13), Some(NetworkUpdate::ChannelClosed { short_channel_id, is_permanent: true}), Some(short_channel_id));
 
+       let short_channel_id = channels[1].0.contents.short_channel_id;
        run_onion_failure_test("expiry_too_soon", 0, &nodes, &route, &payment_hash, &payment_secret, |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(NetworkUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
+       }, ||{}, true, Some(UPDATE|14), Some(NetworkUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy(short_channel_id)}), Some(short_channel_id));
 
        run_onion_failure_test("unknown_payment_hash", 2, &nodes, &route, &payment_hash, &payment_secret, |_| {}, || {
                nodes[2].node.fail_htlc_backwards(&payment_hash);
-       }, false, Some(PERM|15), None);
+       }, false, Some(PERM|15), None, None);
        let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[2]);
 
        run_onion_failure_test("final_expiry_too_soon", 1, &nodes, &route, &payment_hash, &payment_secret, |msg| {
@@ -494,7 +518,7 @@ fn test_onion_failure() {
                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, Some(17), None, None);
 
        run_onion_failure_test("final_incorrect_cltv_expiry", 1, &nodes, &route, &payment_hash, &payment_secret, |_| {}, || {
                for (_, pending_forwards) in nodes[1].node.channel_state.lock().unwrap().forward_htlcs.iter_mut() {
@@ -506,7 +530,7 @@ fn test_onion_failure() {
                                }
                        }
                }
-       }, true, Some(18), None);
+       }, true, Some(18), None, Some(channels[1].0.contents.short_channel_id));
 
        run_onion_failure_test("final_incorrect_htlc_amount", 1, &nodes, &route, &payment_hash, &payment_secret, |_| {}, || {
                // violate amt_to_forward > msg.amount_msat
@@ -519,13 +543,14 @@ fn test_onion_failure() {
                                }
                        }
                }
-       }, true, Some(19), None);
+       }, true, Some(19), None, Some(channels[1].0.contents.short_channel_id));
 
+       let short_channel_id = channels[1].0.contents.short_channel_id;
        run_onion_failure_test("channel_disabled", 0, &nodes, &route, &payment_hash, &payment_secret, |_| {}, || {
                // 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(NetworkUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
+       }, true, Some(UPDATE|20), Some(NetworkUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy(short_channel_id)}), Some(short_channel_id));
        reconnect_nodes(&nodes[1], &nodes[2], (false, false), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (false, false));
 
        run_onion_failure_test("expiry_too_far", 0, &nodes, &route, &payment_hash, &payment_secret, |msg| {
@@ -538,7 +563,5 @@ 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, Some(21), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: true}), Some(route.paths[0][0].short_channel_id));
 }
-
-
index ee3ed96b5ef0efdcaaeda40a32f2e71653948ac0..20ff0c8344d4836738b7a63bad1c41db4fc3e36b 100644 (file)
@@ -75,11 +75,11 @@ pub(super) fn gen_ammag_from_shared_secret(shared_secret: &[u8]) -> [u8; 32] {
 
 // can only fail if an intermediary hop has an invalid public key or session_priv is invalid
 #[inline]
-pub(super) fn construct_onion_keys_callback<T: secp256k1::Signing, FType: FnMut(SharedSecret, [u8; 32], PublicKey, &RouteHop)> (secp_ctx: &Secp256k1<T>, path: &Vec<RouteHop>, session_priv: &SecretKey, mut callback: FType) -> Result<(), secp256k1::Error> {
+pub(super) fn construct_onion_keys_callback<T: secp256k1::Signing, FType: FnMut(SharedSecret, [u8; 32], PublicKey, &RouteHop, usize)> (secp_ctx: &Secp256k1<T>, path: &Vec<RouteHop>, session_priv: &SecretKey, mut callback: FType) -> Result<(), secp256k1::Error> {
        let mut blinded_priv = session_priv.clone();
        let mut blinded_pub = PublicKey::from_secret_key(secp_ctx, &blinded_priv);
 
-       for hop in path.iter() {
+       for (idx, hop) in path.iter().enumerate() {
                let shared_secret = SharedSecret::new(&hop.pubkey, &blinded_priv);
 
                let mut sha = Sha256::engine();
@@ -92,7 +92,7 @@ pub(super) fn construct_onion_keys_callback<T: secp256k1::Signing, FType: FnMut(
                blinded_priv.mul_assign(&blinding_factor)?;
                blinded_pub = PublicKey::from_secret_key(secp_ctx, &blinded_priv);
 
-               callback(shared_secret, blinding_factor, ephemeral_pubkey, hop);
+               callback(shared_secret, blinding_factor, ephemeral_pubkey, hop, idx);
        }
 
        Ok(())
@@ -102,7 +102,7 @@ pub(super) fn construct_onion_keys_callback<T: secp256k1::Signing, FType: FnMut(
 pub(super) fn construct_onion_keys<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T>, path: &Vec<RouteHop>, session_priv: &SecretKey) -> Result<Vec<OnionKeys>, secp256k1::Error> {
        let mut res = Vec::with_capacity(path.len());
 
-       construct_onion_keys_callback(secp_ctx, path, session_priv, |shared_secret, _blinding_factor, ephemeral_pubkey, _| {
+       construct_onion_keys_callback(secp_ctx, path, session_priv, |shared_secret, _blinding_factor, ephemeral_pubkey, _, _| {
                let (rho, mu) = gen_rho_mu_from_shared_secret(&shared_secret[..]);
 
                res.push(OnionKeys {
@@ -329,20 +329,19 @@ pub(super) fn build_first_hop_failure_packet(shared_secret: &[u8], failure_type:
 
 /// Process failure we got back from upstream on a payment we sent (implying htlc_source is an
 /// OutboundRoute).
-/// Returns update, a boolean indicating that the payment itself failed, and the error code.
+/// Returns update, a boolean indicating that the payment itself failed, the short channel id of
+/// the responsible channel, 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<NetworkUpdate>, 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<NetworkUpdate>, Option<u64>, 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;
                let mut error_code_ret = None;
                let mut error_packet_ret = None;
-               let mut next_route_hop_ix = 0;
                let mut is_from_final_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| {
-                       next_route_hop_ix += 1;
+               construct_onion_keys_callback(secp_ctx, path, session_priv, |shared_secret, _, _, route_hop, route_hop_idx| {
                        if res.is_some() { return; }
 
                        let amt_to_forward = htlc_msat - route_hop.fee_msat;
@@ -356,7 +355,10 @@ 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_final_node = path.last().unwrap().pubkey == route_hop.pubkey;
+                       // The failing hop includes either the inbound channel to the recipient or the outbound
+                       // channel from the current hop (i.e., the next hop's inbound channel).
+                       is_from_final_node = route_hop_idx + 1 == path.len();
+                       let failing_route_hop = if is_from_final_node { route_hop } else { &path[route_hop_idx + 1] };
 
                        if let Ok(err_packet) = msgs::DecodedOnionErrorPacket::read(&mut Cursor::new(&packet_decrypted)) {
                                let um = gen_um_from_shared_secret(&shared_secret[..]);
@@ -377,22 +379,27 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
 
                                                // indicate that payment parameter has failed and no need to
                                                // update Route object
-                                               let payment_failed = (match error_code & 0xff {
+                                               let payment_failed = match error_code & 0xff {
                                                        15|16|17|18|19 => true,
                                                        _ => false,
-                                               } && is_from_final_node) // PERM bit observed below even this error is from the intermediate nodes
-                                               || error_code == 21; // Special case error 21 as the Route object is bogus, TODO: Maybe fail the node if the CLTV was reasonable?
+                                               } && is_from_final_node; // PERM bit observed below even if this error is from the intermediate nodes
 
                                                let mut network_update = None;
+                                               let mut short_channel_id = None;
 
                                                if error_code & NODE == NODE {
-                                                       network_update = Some(NetworkUpdate::NodeFailure { node_id: route_hop.pubkey, is_permanent: error_code & PERM == PERM });
+                                                       let is_permanent = error_code & PERM == PERM;
+                                                       network_update = Some(NetworkUpdate::NodeFailure { node_id: route_hop.pubkey, is_permanent });
+                                                       short_channel_id = Some(route_hop.short_channel_id);
                                                }
                                                else if error_code & PERM == PERM {
-                                                       network_update = if payment_failed { None } else { Some(NetworkUpdate::ChannelClosed {
-                                                               short_channel_id: path[next_route_hop_ix - if next_route_hop_ix == path.len() { 1 } else { 0 }].short_channel_id,
-                                                               is_permanent: true,
-                                                       })};
+                                                       if !payment_failed {
+                                                               network_update = Some(NetworkUpdate::ChannelClosed {
+                                                                       short_channel_id: failing_route_hop.short_channel_id,
+                                                                       is_permanent: true,
+                                                               });
+                                                               short_channel_id = Some(failing_route_hop.short_channel_id);
+                                                       }
                                                }
                                                else if error_code & UPDATE == UPDATE {
                                                        if let Some(update_len_slice) = err_packet.failuremsg.get(debug_field_size+2..debug_field_size+4) {
@@ -404,24 +411,31 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
                                                                                let is_chan_update_invalid = match error_code & 0xff {
                                                                                        7 => false,
                                                                                        11 => amt_to_forward > chan_update.contents.htlc_minimum_msat,
-                                                                                       12 => {
-                                                                                               let new_fee = amt_to_forward.checked_mul(chan_update.contents.fee_proportional_millionths as u64).and_then(|prop_fee| { (prop_fee / 1000000).checked_add(chan_update.contents.fee_base_msat as u64) });
-                                                                                               new_fee.is_some() && route_hop.fee_msat >= new_fee.unwrap()
-                                                                                       }
+                                                                                       12 => amt_to_forward
+                                                                                               .checked_mul(chan_update.contents.fee_proportional_millionths as u64)
+                                                                                               .map(|prop_fee| prop_fee / 1_000_000)
+                                                                                               .and_then(|prop_fee| prop_fee.checked_add(chan_update.contents.fee_base_msat as u64))
+                                                                                               .map(|fee_msats| route_hop.fee_msat >= fee_msats)
+                                                                                               .unwrap_or(false),
                                                                                        13 => route_hop.cltv_expiry_delta as u16 >= chan_update.contents.cltv_expiry_delta,
                                                                                        14 => false, // expiry_too_soon; always valid?
                                                                                        20 => chan_update.contents.flags & 2 == 0,
                                                                                        _ => false, // unknown error code; take channel_update as valid
                                                                                };
-                                                                               network_update = if is_chan_update_invalid {
+                                                                               if is_chan_update_invalid {
                                                                                        // This probably indicates the node which forwarded
                                                                                        // to the node in question corrupted something.
-                                                                                       Some(NetworkUpdate::ChannelClosed {
+                                                                                       network_update = Some(NetworkUpdate::ChannelClosed {
                                                                                                short_channel_id: route_hop.short_channel_id,
                                                                                                is_permanent: true,
-                                                                                       })
+                                                                                       });
                                                                                } else {
-                                                                                       Some(NetworkUpdate::ChannelUpdateMessage {
+                                                                                       // Make sure the ChannelUpdate contains the expected
+                                                                                       // short channel id.
+                                                                                       if failing_route_hop.short_channel_id == chan_update.contents.short_channel_id {
+                                                                                               short_channel_id = Some(failing_route_hop.short_channel_id);
+                                                                                       }
+                                                                                       network_update = Some(NetworkUpdate::ChannelUpdateMessage {
                                                                                                msg: chan_update,
                                                                                        })
                                                                                };
@@ -436,7 +450,17 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
                                                                        is_permanent: true,
                                                                });
                                                        }
-                                               } else if !payment_failed {
+                                                       if short_channel_id.is_none() {
+                                                               short_channel_id = Some(route_hop.short_channel_id);
+                                                       }
+                                               } else if payment_failed {
+                                                       // Only blame the hop when a value in the HTLC doesn't match the
+                                                       // corresponding value in the onion.
+                                                       short_channel_id = match error_code & 0xff {
+                                                               18|19 => Some(route_hop.short_channel_id),
+                                                               _ => None,
+                                                       };
+                                               } else {
                                                        // We can't understand their error messages and they failed to
                                                        // forward...they probably can't understand our forwards so its
                                                        // really not worth trying any further.
@@ -444,12 +468,13 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
                                                                node_id: route_hop.pubkey,
                                                                is_permanent: true,
                                                        });
+                                                       short_channel_id = Some(route_hop.short_channel_id);
                                                }
 
                                                // TODO: Here (and a few other places) we assume that BADONION errors
                                                // are always "sourced" from the node previous to the one which failed
                                                // to decode the onion.
-                                               res = Some((network_update, !(error_code & PERM == PERM && is_from_final_node)));
+                                               res = Some((network_update, short_channel_id, !(error_code & PERM == PERM && is_from_final_node)));
 
                                                let (description, title) = errors::get_onion_error_description(error_code);
                                                if debug_field_size > 0 && err_packet.failuremsg.len() >= 4 + debug_field_size {
@@ -461,20 +486,22 @@ pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &
                                        } else {
                                                // Useless packet that we can't use but it passed HMAC, so it
                                                // definitely came from the peer in question
-                                               res = Some((Some(NetworkUpdate::NodeFailure {
+                                               let network_update = Some(NetworkUpdate::NodeFailure {
                                                        node_id: route_hop.pubkey,
                                                        is_permanent: true,
-                                               }), !is_from_final_node));
+                                               });
+                                               let short_channel_id = Some(route_hop.short_channel_id);
+                                               res = Some((network_update, short_channel_id, !is_from_final_node));
                                        }
                                }
                        }
                }).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)
+               if let Some((channel_update, short_channel_id, payment_retryable)) = res {
+                       (channel_update, short_channel_id, payment_retryable, 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, None, !is_from_final_node, None, None)
                }
        } else { unreachable!(); }
 }
index acf51e535d2f682463a15b5119385d05e987377f..f65b8fa657a5fc2e77da24a33578ecf51089e8c4 100644 (file)
@@ -1813,6 +1813,7 @@ mod tests {
                                network_update: Some(NetworkUpdate::ChannelUpdateMessage {
                                        msg: valid_channel_update,
                                }),
+                               short_channel_id: None,
                                error_code: None,
                                error_data: None,
                        });
@@ -1838,6 +1839,7 @@ mod tests {
                                        short_channel_id,
                                        is_permanent: false,
                                }),
+                               short_channel_id: None,
                                error_code: None,
                                error_data: None,
                        });
@@ -1861,6 +1863,7 @@ mod tests {
                                        short_channel_id,
                                        is_permanent: true,
                                }),
+                               short_channel_id: None,
                                error_code: None,
                                error_data: None,
                        });
index 37c64364b1817bf89e4477ce650a70f52ba0b938..9f7f4b4e5e00130a3581c5dd397497b3be0ba8ff 100644 (file)
@@ -208,6 +208,11 @@ pub enum Event {
                all_paths_failed: bool,
                /// The payment path that failed.
                path: Vec<RouteHop>,
+               /// The channel responsible for the failed payment path.
+               ///
+               /// If this is `Some`, then the corresponding channel should be avoided when the payment is
+               /// retried. May be `None` for older [`Event`] serializations.
+               short_channel_id: Option<u64>,
 #[cfg(test)]
                error_code: Option<u16>,
 #[cfg(test)]
@@ -309,7 +314,7 @@ impl Writeable for Event {
                                });
                        },
                        &Event::PaymentPathFailed { ref payment_hash, ref rejected_by_dest, ref network_update,
-                                                   ref all_paths_failed, ref path,
+                                                   ref all_paths_failed, ref path, ref short_channel_id,
                                #[cfg(test)]
                                ref error_code,
                                #[cfg(test)]
@@ -326,6 +331,7 @@ impl Writeable for Event {
                                        (2, rejected_by_dest, required),
                                        (3, all_paths_failed, required),
                                        (5, path, vec_type),
+                                       (7, short_channel_id, option),
                                });
                        },
                        &Event::PendingHTLCsForwardable { time_forwardable: _ } => {
@@ -435,12 +441,14 @@ impl MaybeReadable for Event {
                                        let mut network_update = None;
                                        let mut all_paths_failed = Some(true);
                                        let mut path: Option<Vec<RouteHop>> = Some(vec![]);
+                                       let mut short_channel_id = None;
                                        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),
+                                               (7, short_channel_id, ignorable),
                                        });
                                        Ok(Some(Event::PaymentPathFailed {
                                                payment_hash,
@@ -448,6 +456,7 @@ impl MaybeReadable for Event {
                                                network_update,
                                                all_paths_failed: all_paths_failed.unwrap(),
                                                path: path.unwrap(),
+                                               short_channel_id,
                                                #[cfg(test)]
                                                error_code,
                                                #[cfg(test)]
@@ -480,23 +489,29 @@ impl MaybeReadable for Event {
                                f()
                        },
                        9u8 => {
-                               let mut channel_id = [0; 32];
-                               let mut reason = None;
-                               read_tlv_fields!(reader, {
-                                       (0, channel_id, required),
-                                       (2, reason, ignorable),
-                               });
-                               if reason.is_none() { return Ok(None); }
-                               Ok(Some(Event::ChannelClosed { channel_id, reason: reason.unwrap() }))
+                               let f = || {
+                                       let mut channel_id = [0; 32];
+                                       let mut reason = None;
+                                       read_tlv_fields!(reader, {
+                                               (0, channel_id, required),
+                                               (2, reason, ignorable),
+                                       });
+                                       if reason.is_none() { return Ok(None); }
+                                       Ok(Some(Event::ChannelClosed { channel_id, reason: reason.unwrap() }))
+                               };
+                               f()
                        },
                        11u8 => {
-                               let mut channel_id = [0; 32];
-                               let mut transaction = Transaction{ version: 2, lock_time: 0, input: Vec::new(), output: Vec::new() };
-                               read_tlv_fields!(reader, {
-                                       (0, channel_id, required),
-                                       (2, transaction, required),
-                               });
-                               Ok(Some(Event::DiscardFunding { channel_id, transaction } ))
+                               let f = || {
+                                       let mut channel_id = [0; 32];
+                                       let mut transaction = Transaction{ version: 2, lock_time: 0, input: Vec::new(), output: Vec::new() };
+                                       read_tlv_fields!(reader, {
+                                               (0, channel_id, required),
+                                               (2, transaction, required),
+                                       });
+                                       Ok(Some(Event::DiscardFunding { channel_id, transaction } ))
+                               };
+                               f()
                        },
                        // Versions prior to 0.0.100 did not ignore odd types, instead returning InvalidValue.
                        // Version 0.0.100 failed to properly ignore odd types, possibly resulting in corrupt