Automatically fail intercepts back on timeout
[rust-lightning] / lightning / src / ln / payment_tests.rs
index 57c3140dd3ef246e20c230e44b003388c536469c..ead5bcbd4694cf95c93abc717dfd633b7071e55a 100644 (file)
@@ -19,7 +19,8 @@ use crate::ln::channel::EXPIRE_PREV_CONFIG_TICKS;
 use crate::ln::channelmanager::{self, BREAKDOWN_TIMEOUT, ChannelManager, MPP_TIMEOUT_TICKS, MIN_CLTV_EXPIRY_DELTA, PaymentId, PaymentSendFailure, IDEMPOTENCY_TIMEOUT_TICKS};
 use crate::ln::msgs;
 use crate::ln::msgs::ChannelMessageHandler;
-use crate::routing::router::{PaymentParameters, get_route};
+use crate::routing::gossip::RoutingFees;
+use crate::routing::router::{find_route, get_route, PaymentParameters, RouteHint, RouteHintHop, RouteParameters};
 use crate::util::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider};
 use crate::util::test_utils;
 use crate::util::errors::APIError;
@@ -32,6 +33,7 @@ use bitcoin::network::constants::Network;
 use crate::prelude::*;
 
 use crate::ln::functional_test_utils::*;
+use crate::routing::gossip::NodeId;
 
 #[test]
 fn retry_single_path_payment() {
@@ -1240,3 +1242,319 @@ fn abandoned_send_payment_idempotent() {
        pass_along_route(&nodes[0], &[&[&nodes[1]]], 100_000, second_payment_hash, second_payment_secret);
        claim_payment(&nodes[0], &[&nodes[1]], second_payment_preimage);
 }
+
+#[derive(PartialEq)]
+enum InterceptTest {
+       Forward,
+       Fail,
+       Timeout,
+}
+
+#[test]
+fn test_trivial_inflight_htlc_tracking(){
+       // In this test, we test three scenarios:
+       // (1) Sending + claiming a payment successfully should return `None` when querying InFlightHtlcs
+       // (2) Sending a payment without claiming it should return the payment's value (500000) when querying InFlightHtlcs
+       // (3) After we claim the payment sent in (2), InFlightHtlcs should return `None` for the query.
+       let chanmon_cfgs = create_chanmon_cfgs(3);
+       let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+       let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
+       let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+
+       let (_, _, chan_1_id, _) = create_announced_chan_between_nodes(&nodes, 0, 1, channelmanager::provided_init_features(), channelmanager::provided_init_features());
+       let (_, _, chan_2_id, _) = create_announced_chan_between_nodes(&nodes, 1, 2, channelmanager::provided_init_features(), channelmanager::provided_init_features());
+
+       // Send and claim the payment. Inflight HTLCs should be empty.
+       send_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 500000);
+       {
+               let inflight_htlcs = node_chanmgrs[0].compute_inflight_htlcs();
+
+               let node_0_channel_lock = nodes[0].node.channel_state.lock().unwrap();
+               let node_1_channel_lock = nodes[1].node.channel_state.lock().unwrap();
+               let channel_1 = node_0_channel_lock.by_id.get(&chan_1_id).unwrap();
+               let channel_2 = node_1_channel_lock.by_id.get(&chan_2_id).unwrap();
+
+               let chan_1_used_liquidity = inflight_htlcs.used_liquidity_msat(
+                       &NodeId::from_pubkey(&nodes[0].node.get_our_node_id()) ,
+                       &NodeId::from_pubkey(&nodes[1].node.get_our_node_id()),
+                       channel_1.get_short_channel_id().unwrap()
+               );
+               let chan_2_used_liquidity = inflight_htlcs.used_liquidity_msat(
+                       &NodeId::from_pubkey(&nodes[1].node.get_our_node_id()) ,
+                       &NodeId::from_pubkey(&nodes[2].node.get_our_node_id()),
+                       channel_2.get_short_channel_id().unwrap()
+               );
+
+               assert_eq!(chan_1_used_liquidity, None);
+               assert_eq!(chan_2_used_liquidity, None);
+       }
+
+       // Send the payment, but do not claim it. Our inflight HTLCs should contain the pending payment.
+       let (payment_preimage, _,  _) = route_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 500000);
+       {
+               let inflight_htlcs = node_chanmgrs[0].compute_inflight_htlcs();
+
+               let node_0_channel_lock = nodes[0].node.channel_state.lock().unwrap();
+               let node_1_channel_lock = nodes[1].node.channel_state.lock().unwrap();
+               let channel_1 = node_0_channel_lock.by_id.get(&chan_1_id).unwrap();
+               let channel_2 = node_1_channel_lock.by_id.get(&chan_2_id).unwrap();
+
+               let chan_1_used_liquidity = inflight_htlcs.used_liquidity_msat(
+                       &NodeId::from_pubkey(&nodes[0].node.get_our_node_id()) ,
+                       &NodeId::from_pubkey(&nodes[1].node.get_our_node_id()),
+                       channel_1.get_short_channel_id().unwrap()
+               );
+               let chan_2_used_liquidity = inflight_htlcs.used_liquidity_msat(
+                       &NodeId::from_pubkey(&nodes[1].node.get_our_node_id()) ,
+                       &NodeId::from_pubkey(&nodes[2].node.get_our_node_id()),
+                       channel_2.get_short_channel_id().unwrap()
+               );
+
+               // First hop accounts for expected 1000 msat fee
+               assert_eq!(chan_1_used_liquidity, Some(501000));
+               assert_eq!(chan_2_used_liquidity, Some(500000));
+       }
+
+       // Now, let's claim the payment. This should result in the used liquidity to return `None`.
+       claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage);
+       {
+               let inflight_htlcs = node_chanmgrs[0].compute_inflight_htlcs();
+
+               let node_0_channel_lock = nodes[0].node.channel_state.lock().unwrap();
+               let node_1_channel_lock = nodes[1].node.channel_state.lock().unwrap();
+               let channel_1 = node_0_channel_lock.by_id.get(&chan_1_id).unwrap();
+               let channel_2 = node_1_channel_lock.by_id.get(&chan_2_id).unwrap();
+
+               let chan_1_used_liquidity = inflight_htlcs.used_liquidity_msat(
+                       &NodeId::from_pubkey(&nodes[0].node.get_our_node_id()) ,
+                       &NodeId::from_pubkey(&nodes[1].node.get_our_node_id()),
+                       channel_1.get_short_channel_id().unwrap()
+               );
+               let chan_2_used_liquidity = inflight_htlcs.used_liquidity_msat(
+                       &NodeId::from_pubkey(&nodes[1].node.get_our_node_id()) ,
+                       &NodeId::from_pubkey(&nodes[2].node.get_our_node_id()),
+                       channel_2.get_short_channel_id().unwrap()
+               );
+
+               assert_eq!(chan_1_used_liquidity, None);
+               assert_eq!(chan_2_used_liquidity, None);
+       }
+}
+
+#[test]
+fn test_holding_cell_inflight_htlcs() {
+       let chanmon_cfgs = create_chanmon_cfgs(2);
+       let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+       let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+       let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+       let channel_id = create_announced_chan_between_nodes(&nodes, 0, 1, channelmanager::provided_init_features(), channelmanager::provided_init_features()).2;
+
+       let (route, payment_hash_1, _, payment_secret_1) = get_route_and_payment_hash!(nodes[0], nodes[1], 1000000);
+       let (_, payment_hash_2, payment_secret_2) = get_payment_preimage_hash!(nodes[1]);
+
+       // Queue up two payments - one will be delivered right away, one immediately goes into the
+       // holding cell as nodes[0] is AwaitingRAA.
+       {
+               nodes[0].node.send_payment(&route, payment_hash_1, &Some(payment_secret_1), PaymentId(payment_hash_1.0)).unwrap();
+               check_added_monitors!(nodes[0], 1);
+               nodes[0].node.send_payment(&route, payment_hash_2, &Some(payment_secret_2), PaymentId(payment_hash_2.0)).unwrap();
+               check_added_monitors!(nodes[0], 0);
+       }
+
+       let inflight_htlcs = node_chanmgrs[0].compute_inflight_htlcs();
+
+       {
+               let channel_lock = nodes[0].node.channel_state.lock().unwrap();
+               let channel = channel_lock.by_id.get(&channel_id).unwrap();
+
+               let used_liquidity = inflight_htlcs.used_liquidity_msat(
+                       &NodeId::from_pubkey(&nodes[0].node.get_our_node_id()) ,
+                       &NodeId::from_pubkey(&nodes[1].node.get_our_node_id()),
+                       channel.get_short_channel_id().unwrap()
+               );
+
+               assert_eq!(used_liquidity, Some(2000000));
+       }
+
+       // Clear pending events so test doesn't throw a "Had excess message on node..." error
+       nodes[0].node.get_and_clear_pending_msg_events();
+}
+
+#[test]
+fn intercepted_payment() {
+       // Test that detecting an intercept scid on payment forward will signal LDK to generate an
+       // intercept event, which the LSP can then use to either (a) open a JIT channel to forward the
+       // payment or (b) fail the payment.
+       do_test_intercepted_payment(InterceptTest::Forward);
+       do_test_intercepted_payment(InterceptTest::Fail);
+       // Make sure that intercepted payments will be automatically failed back if too many blocks pass.
+       do_test_intercepted_payment(InterceptTest::Timeout);
+}
+
+fn do_test_intercepted_payment(test: InterceptTest) {
+       let chanmon_cfgs = create_chanmon_cfgs(3);
+       let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+
+       let mut zero_conf_chan_config = test_default_channel_config();
+       zero_conf_chan_config.manually_accept_inbound_channels = true;
+       let mut intercept_forwards_config = test_default_channel_config();
+       intercept_forwards_config.accept_intercept_htlcs = true;
+       let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, Some(intercept_forwards_config), Some(zero_conf_chan_config)]);
+
+       let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+       let scorer = test_utils::TestScorer::with_penalty(0);
+       let random_seed_bytes = chanmon_cfgs[0].keys_manager.get_secure_random_bytes();
+
+       let _ = create_announced_chan_between_nodes(&nodes, 0, 1, channelmanager::provided_init_features(), channelmanager::provided_init_features()).2;
+
+       let amt_msat = 100_000;
+       let intercept_scid = nodes[1].node.get_intercept_scid();
+       let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id())
+               .with_route_hints(vec![
+                       RouteHint(vec![RouteHintHop {
+                               src_node_id: nodes[1].node.get_our_node_id(),
+                               short_channel_id: intercept_scid,
+                               fees: RoutingFees {
+                                       base_msat: 1000,
+                                       proportional_millionths: 0,
+                               },
+                               cltv_expiry_delta: MIN_CLTV_EXPIRY_DELTA,
+                               htlc_minimum_msat: None,
+                               htlc_maximum_msat: None,
+                       }])
+               ])
+               .with_features(channelmanager::provided_invoice_features());
+       let route_params = RouteParameters {
+               payment_params,
+               final_value_msat: amt_msat,
+               final_cltv_expiry_delta: TEST_FINAL_CLTV,
+       };
+       let route = find_route(
+               &nodes[0].node.get_our_node_id(), &route_params, &nodes[0].network_graph, None, nodes[0].logger,
+               &scorer, &random_seed_bytes
+       ).unwrap();
+
+       let (payment_hash, payment_secret) = nodes[2].node.create_inbound_payment(Some(amt_msat), 60 * 60).unwrap();
+       nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), PaymentId(payment_hash.0)).unwrap();
+       let payment_event = {
+               {
+                       let mut added_monitors = nodes[0].chain_monitor.added_monitors.lock().unwrap();
+                       assert_eq!(added_monitors.len(), 1);
+                       added_monitors.clear();
+               }
+               let mut events = nodes[0].node.get_and_clear_pending_msg_events();
+               assert_eq!(events.len(), 1);
+               SendEvent::from_event(events.remove(0))
+       };
+       nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
+       commitment_signed_dance!(nodes[1], nodes[0], &payment_event.commitment_msg, false, true);
+
+       // Check that we generate the PaymentIntercepted event when an intercept forward is detected.
+       let events = nodes[1].node.get_and_clear_pending_events();
+       assert_eq!(events.len(), 1);
+       let (intercept_id, expected_outbound_amount_msat) = match events[0] {
+               crate::util::events::Event::HTLCIntercepted {
+                       intercept_id, expected_outbound_amount_msat, payment_hash: pmt_hash, inbound_amount_msat, requested_next_hop_scid: short_channel_id
+               } => {
+                       assert_eq!(pmt_hash, payment_hash);
+                       assert_eq!(inbound_amount_msat, route.get_total_amount() + route.get_total_fees());
+                       assert_eq!(short_channel_id, intercept_scid);
+                       (intercept_id, expected_outbound_amount_msat)
+               },
+               _ => panic!()
+       };
+
+       // Check for unknown channel id error.
+       let unknown_chan_id_err = nodes[1].node.forward_intercepted_htlc(intercept_id, &[42; 32], nodes[2].node.get_our_node_id(), expected_outbound_amount_msat).unwrap_err();
+       assert_eq!(unknown_chan_id_err , APIError::APIMisuseError { err: format!("Channel with id {:?} not found", [42; 32]) });
+
+       if test == InterceptTest::Fail {
+               // Ensure we can fail the intercepted payment back.
+               nodes[1].node.fail_intercepted_htlc(intercept_id).unwrap();
+               expect_pending_htlcs_forwardable_and_htlc_handling_failed_ignore!(nodes[1], vec![HTLCDestination::UnknownNextHop { requested_forward_scid: intercept_scid }]);
+               nodes[1].node.process_pending_htlc_forwards();
+               let update_fail = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+               check_added_monitors!(&nodes[1], 1);
+               assert!(update_fail.update_fail_htlcs.len() == 1);
+               let fail_msg = update_fail.update_fail_htlcs[0].clone();
+               nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &fail_msg);
+               commitment_signed_dance!(nodes[0], nodes[1], update_fail.commitment_signed, false);
+
+               // Ensure the payment fails with the expected error.
+               let fail_conditions = PaymentFailedConditions::new()
+                       .blamed_scid(intercept_scid)
+                       .blamed_chan_closed(true)
+                       .expected_htlc_error_data(0x4000 | 10, &[]);
+               expect_payment_failed_conditions(&nodes[0], payment_hash, false, fail_conditions);
+       } else if test == InterceptTest::Forward {
+               // Open the just-in-time channel so the payment can then be forwarded.
+               let (_, channel_id) = open_zero_conf_channel(&nodes[1], &nodes[2], None);
+
+               // Finally, forward the intercepted payment through and claim it.
+               nodes[1].node.forward_intercepted_htlc(intercept_id, &channel_id, nodes[2].node.get_our_node_id(), expected_outbound_amount_msat).unwrap();
+               expect_pending_htlcs_forwardable!(nodes[1]);
+
+               let payment_event = {
+                       {
+                               let mut added_monitors = nodes[1].chain_monitor.added_monitors.lock().unwrap();
+                               assert_eq!(added_monitors.len(), 1);
+                               added_monitors.clear();
+                       }
+                       let mut events = nodes[1].node.get_and_clear_pending_msg_events();
+                       assert_eq!(events.len(), 1);
+                       SendEvent::from_event(events.remove(0))
+               };
+               nodes[2].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &payment_event.msgs[0]);
+               commitment_signed_dance!(nodes[2], nodes[1], &payment_event.commitment_msg, false, true);
+               expect_pending_htlcs_forwardable!(nodes[2]);
+
+               let payment_preimage = nodes[2].node.get_payment_preimage(payment_hash, payment_secret).unwrap();
+               expect_payment_received!(&nodes[2], payment_hash, payment_secret, amt_msat, Some(payment_preimage), nodes[2].node.get_our_node_id());
+               do_claim_payment_along_route(&nodes[0], &vec!(&vec!(&nodes[1], &nodes[2])[..]), false, payment_preimage);
+               let events = nodes[0].node.get_and_clear_pending_events();
+               assert_eq!(events.len(), 2);
+               match events[0] {
+                       Event::PaymentSent { payment_preimage: ref ev_preimage, payment_hash: ref ev_hash, ref fee_paid_msat, .. } => {
+                               assert_eq!(payment_preimage, *ev_preimage);
+                               assert_eq!(payment_hash, *ev_hash);
+                               assert_eq!(fee_paid_msat, &Some(1000));
+                       },
+                       _ => panic!("Unexpected event")
+               }
+               match events[1] {
+                       Event::PaymentPathSuccessful { payment_hash: hash, .. } => {
+                               assert_eq!(hash, Some(payment_hash));
+                       },
+                       _ => panic!("Unexpected event")
+               }
+       } else if test == InterceptTest::Timeout {
+               let mut block = Block {
+                       header: BlockHeader { version: 0x20000000, prev_blockhash: nodes[0].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 },
+                       txdata: vec![],
+               };
+               connect_block(&nodes[0], &block);
+               connect_block(&nodes[1], &block);
+               let block_count = 183; // find_route adds a random CLTV offset, so hardcode rather than summing consts
+               for _ in 0..block_count {
+                       block.header.prev_blockhash = block.block_hash();
+                       connect_block(&nodes[0], &block);
+                       connect_block(&nodes[1], &block);
+               }
+               expect_pending_htlcs_forwardable_and_htlc_handling_failed!(nodes[1], vec![HTLCDestination::InvalidForward { requested_forward_scid: intercept_scid }]);
+               check_added_monitors!(nodes[1], 1);
+               let htlc_timeout_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+               assert!(htlc_timeout_updates.update_add_htlcs.is_empty());
+               assert_eq!(htlc_timeout_updates.update_fail_htlcs.len(), 1);
+               assert!(htlc_timeout_updates.update_fail_malformed_htlcs.is_empty());
+               assert!(htlc_timeout_updates.update_fee.is_none());
+
+               nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &htlc_timeout_updates.update_fail_htlcs[0]);
+               commitment_signed_dance!(nodes[0], nodes[1], htlc_timeout_updates.commitment_signed, false);
+               expect_payment_failed!(nodes[0], payment_hash, false, 0x2000 | 2, []);
+
+               // Check for unknown intercept id error.
+               let (_, channel_id) = open_zero_conf_channel(&nodes[1], &nodes[2], None);
+               let unknown_intercept_id_err = nodes[1].node.forward_intercepted_htlc(intercept_id, &channel_id, nodes[2].node.get_our_node_id(), expected_outbound_amount_msat).unwrap_err();
+               assert_eq!(unknown_intercept_id_err , APIError::APIMisuseError { err: format!("Payment with intercept id {:?} not found", intercept_id.0) });
+       }
+}