X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fpayment_tests.rs;h=ead5bcbd4694cf95c93abc717dfd633b7071e55a;hb=7809c5515c0bd7e3ae7a79c7634dd578b75d4386;hp=27dc67a0dadf738c46f9cd5fda36db554b639a73;hpb=8d93dba37031208878a6046110dc3f6e56796b65;p=rust-lightning diff --git a/lightning/src/ln/payment_tests.rs b/lightning/src/ln/payment_tests.rs index 27dc67a0..ead5bcbd 100644 --- a/lightning/src/ln/payment_tests.rs +++ b/lightning/src/ln/payment_tests.rs @@ -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() { @@ -785,8 +787,9 @@ fn do_test_dup_htlc_onchain_fails_on_reload(persist_manager_post_event: bool, co let funding_txo = OutPoint { txid: funding_tx.txid(), index: 0 }; let mon_updates: Vec<_> = chanmon_cfgs[0].persister.chain_sync_monitor_persistences.lock().unwrap() .get_mut(&funding_txo).unwrap().drain().collect(); - // If we are using chain::Confirm instead of chain::Listen, we will get the same update twice - assert!(mon_updates.len() == 1 || mon_updates.len() == 2); + // If we are using chain::Confirm instead of chain::Listen, we will get the same update twice. + // If we're testing connection idempotency we may get substantially more. + assert!(mon_updates.len() >= 1); assert!(nodes[0].chain_monitor.release_pending_monitor_events().is_empty()); assert!(nodes[0].node.get_and_clear_pending_events().is_empty()); @@ -1239,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) }); + } +}