|
12 | 12 | //! Further functional tests which test blockchain reorganizations.
|
13 | 13 |
|
14 | 14 | use crate::chain::chaininterface::LowerBoundedFeeEstimator;
|
15 |
| -use crate::chain::channelmonitor::{ANTI_REORG_DELAY, LATENCY_GRACE_PERIOD_BLOCKS}; |
| 15 | +use crate::chain::channelmonitor::{ANTI_REORG_DELAY, Balance, LATENCY_GRACE_PERIOD_BLOCKS}; |
16 | 16 | use crate::chain::transaction::OutPoint;
|
17 | 17 | use crate::chain::Confirm;
|
18 | 18 | use crate::events::{Event, ClosureReason, HTLCHandlingFailureType};
|
19 | 19 | use crate::ln::msgs::{BaseMessageHandler, ChannelMessageHandler, Init, MessageSendEvent};
|
20 | 20 | use crate::ln::types::ChannelId;
|
21 | 21 | use crate::sign::OutputSpender;
|
| 22 | +use crate::types::payment::PaymentHash; |
22 | 23 | use crate::types::string::UntrustedString;
|
23 | 24 | use crate::util::ser::Writeable;
|
24 | 25 |
|
@@ -899,3 +900,226 @@ fn test_retries_own_commitment_broadcast_after_reorg() {
|
899 | 900 | do_test_retries_own_commitment_broadcast_after_reorg(true, false);
|
900 | 901 | do_test_retries_own_commitment_broadcast_after_reorg(true, true);
|
901 | 902 | }
|
| 903 | + |
| 904 | +fn do_test_split_htlc_expiry_tracking(use_third_htlc: bool, reorg_out: bool) { |
| 905 | + // Previously, we had a bug where if there were two HTLCs which expired at different heights, |
| 906 | + // and a counterparty commitment transaction confirmed spending both of them, we'd continually |
| 907 | + // rebroadcast attempted HTLC claims against the higher-expiry HTLC forever. |
| 908 | + let chanmon_cfgs = create_chanmon_cfgs(2); |
| 909 | + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); |
| 910 | + |
| 911 | + // This test relies on being able to consolidate HTLC claims into a single transaction, which |
| 912 | + // requires anchors: |
| 913 | + let mut config = test_default_channel_config(); |
| 914 | + config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true; |
| 915 | + config.manually_accept_inbound_channels = true; |
| 916 | + |
| 917 | + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config.clone()), Some(config)]); |
| 918 | + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); |
| 919 | + |
| 920 | + let coinbase_tx = provide_anchor_reserves(&nodes); |
| 921 | + |
| 922 | + let node_a_id = nodes[0].node.get_our_node_id(); |
| 923 | + let node_b_id = nodes[1].node.get_our_node_id(); |
| 924 | + |
| 925 | + let (_, _, chan_id, funding_tx) = |
| 926 | + create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 0); |
| 927 | + |
| 928 | + // Route two non-dust HTLCs with different expiry, with a third having the same expiry as the |
| 929 | + // second if `use_third_htlc` is set. |
| 930 | + let (preimage_a, payment_hash_a, ..) = route_payment(&nodes[0], &[&nodes[1]], 100_000_000); |
| 931 | + connect_blocks(&nodes[0], 2); |
| 932 | + connect_blocks(&nodes[1], 2); |
| 933 | + let (preimage_b, payment_hash_b, ..) = route_payment(&nodes[0], &[&nodes[1]], 100_000_000); |
| 934 | + let payment_hash_c = if use_third_htlc { |
| 935 | + route_payment(&nodes[0], &[&nodes[1]], 100_000_000).1 |
| 936 | + } else { |
| 937 | + PaymentHash([0; 32]) |
| 938 | + }; |
| 939 | + |
| 940 | + // First disconnect peers so that we don't have to deal with messages: |
| 941 | + nodes[0].node.peer_disconnected(node_b_id); |
| 942 | + nodes[1].node.peer_disconnected(node_a_id); |
| 943 | + |
| 944 | + // Give node B preimages so that it will claim the first two HTLCs on-chain. |
| 945 | + nodes[1].node.claim_funds(preimage_a); |
| 946 | + expect_payment_claimed!(nodes[1], payment_hash_a, 100_000_000); |
| 947 | + nodes[1].node.claim_funds(preimage_b); |
| 948 | + expect_payment_claimed!(nodes[1], payment_hash_b, 100_000_000); |
| 949 | + check_added_monitors(&nodes[1], 2); |
| 950 | + |
| 951 | + let err = "Channel force-closed".to_string(); |
| 952 | + |
| 953 | + // Force-close and fetch node B's commitment transaction and the transaction claiming the first |
| 954 | + // two HTLCs. |
| 955 | + nodes[1].node.force_close_broadcasting_latest_txn(&chan_id, &node_a_id, err).unwrap(); |
| 956 | + check_closed_broadcast(&nodes[1], 1, true); |
| 957 | + check_added_monitors(&nodes[1], 1); |
| 958 | + let reason = ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(true) }; |
| 959 | + check_closed_event(&nodes[1], 1, reason, false, &[node_a_id], 10_000_000); |
| 960 | + |
| 961 | + let mut txn = nodes[1].tx_broadcaster.txn_broadcast(); |
| 962 | + assert_eq!(txn.len(), 1); |
| 963 | + let commitment_tx = txn.pop().unwrap(); |
| 964 | + check_spends!(commitment_tx, funding_tx); |
| 965 | + |
| 966 | + mine_transaction(&nodes[0], &commitment_tx); |
| 967 | + check_closed_broadcast(&nodes[0], 1, true); |
| 968 | + let reason = ClosureReason::CommitmentTxConfirmed; |
| 969 | + check_closed_event(&nodes[0], 1, reason, false, &[node_b_id], 10_000_000); |
| 970 | + check_added_monitors(&nodes[0], 1); |
| 971 | + |
| 972 | + mine_transaction(&nodes[1], &commitment_tx); |
| 973 | + let mut bump_events = nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events(); |
| 974 | + assert_eq!(bump_events.len(), 1); |
| 975 | + match bump_events.pop().unwrap() { |
| 976 | + Event::BumpTransaction(bump_event) => { |
| 977 | + nodes[1].bump_tx_handler.handle_event(&bump_event); |
| 978 | + }, |
| 979 | + ev => panic!("Unexpected event {ev:?}"), |
| 980 | + } |
| 981 | + |
| 982 | + let mut txn = nodes[1].tx_broadcaster.txn_broadcast(); |
| 983 | + if nodes[1].connect_style.borrow().updates_best_block_first() { |
| 984 | + assert_eq!(txn.len(), 2, "{txn:?}"); |
| 985 | + check_spends!(txn[0], funding_tx); |
| 986 | + } else { |
| 987 | + assert_eq!(txn.len(), 1, "{txn:?}"); |
| 988 | + } |
| 989 | + let bs_htlc_spend_tx = txn.pop().unwrap(); |
| 990 | + check_spends!(bs_htlc_spend_tx, commitment_tx, coinbase_tx); |
| 991 | + |
| 992 | + // Now connect blocks until the first HTLC expires |
| 993 | + assert_eq!(nodes[0].tx_broadcaster.txn_broadcast().len(), 0); |
| 994 | + connect_blocks(&nodes[0], TEST_FINAL_CLTV - 2); |
| 995 | + let mut txn = nodes[0].tx_broadcaster.txn_broadcast(); |
| 996 | + assert_eq!(txn.len(), 1); |
| 997 | + let as_first_htlc_spend_tx = txn.pop().unwrap(); |
| 998 | + check_spends!(as_first_htlc_spend_tx, commitment_tx); |
| 999 | + |
| 1000 | + // But confirm B's dual-HTLC-claim transaction instead. A should now have nothing to broadcast |
| 1001 | + // as the third HTLC (if there is one) won't expire for another block. |
| 1002 | + mine_transaction(&nodes[0], &bs_htlc_spend_tx); |
| 1003 | + let mut txn = nodes[0].tx_broadcaster.txn_broadcast(); |
| 1004 | + assert_eq!(txn.len(), 0); |
| 1005 | + |
| 1006 | + let sent_events = nodes[0].node.get_and_clear_pending_events(); |
| 1007 | + assert_eq!(sent_events.len(), 4, "{sent_events:?}"); |
| 1008 | + let mut found_expected_events = [false, false, false, false]; |
| 1009 | + for event in sent_events { |
| 1010 | + match event { |
| 1011 | + Event::PaymentSent { payment_hash, .. }|Event::PaymentPathSuccessful { payment_hash: Some(payment_hash), .. } => { |
| 1012 | + let path_success = matches!(event, Event::PaymentPathSuccessful { .. }); |
| 1013 | + if payment_hash == payment_hash_a { |
| 1014 | + found_expected_events[0 + if path_success { 1 } else { 0 }] = true; |
| 1015 | + } else if payment_hash == payment_hash_b { |
| 1016 | + found_expected_events[2 + if path_success { 1 } else { 0 }] = true; |
| 1017 | + } else { |
| 1018 | + panic!("Wrong payment hash {event:?}"); |
| 1019 | + } |
| 1020 | + }, |
| 1021 | + _ => panic!("Wrong event {event:?}"), |
| 1022 | + } |
| 1023 | + } |
| 1024 | + assert_eq!(found_expected_events, [true, true, true, true]); |
| 1025 | + |
| 1026 | + // However if we connect one more block the third HTLC will time out and A should claim it |
| 1027 | + connect_blocks(&nodes[0], 1); |
| 1028 | + let mut txn = nodes[0].tx_broadcaster.txn_broadcast(); |
| 1029 | + if use_third_htlc { |
| 1030 | + assert_eq!(txn.len(), 1); |
| 1031 | + let as_third_htlc_spend_tx = txn.pop().unwrap(); |
| 1032 | + check_spends!(as_third_htlc_spend_tx, commitment_tx); |
| 1033 | + // Previously, node A would generate a bogus claim here, trying to claim both HTLCs B and C in |
| 1034 | + // one transaction, so we check that the single input being spent was not already spent in node |
| 1035 | + // B's HTLC claim transaction. |
| 1036 | + assert_eq!(as_third_htlc_spend_tx.input.len(), 1, "{as_third_htlc_spend_tx:?}"); |
| 1037 | + for spent_input in bs_htlc_spend_tx.input.iter() { |
| 1038 | + let third_htlc_vout = as_third_htlc_spend_tx.input[0].previous_output.vout; |
| 1039 | + assert_ne!(third_htlc_vout, spent_input.previous_output.vout); |
| 1040 | + } |
| 1041 | + |
| 1042 | + mine_transaction(&nodes[0], &as_third_htlc_spend_tx); |
| 1043 | + |
| 1044 | + assert_eq!(&nodes[0].node.get_and_clear_pending_events(), &[]); |
| 1045 | + } else { |
| 1046 | + assert_eq!(txn.len(), 0); |
| 1047 | + // Connect a block so that both cases end with the same height |
| 1048 | + connect_blocks(&nodes[0], 1); |
| 1049 | + } |
| 1050 | + |
| 1051 | + // At this point all HTLCs have been resolved and no further transactions should be generated. |
| 1052 | + // We connect blocks until one block before `bs_htlc_spend_tx` reaches `ANTI_REORG_DELAY` |
| 1053 | + // confirmations. |
| 1054 | + connect_blocks(&nodes[0], ANTI_REORG_DELAY - 4); |
| 1055 | + let mut txn = nodes[0].tx_broadcaster.txn_broadcast(); |
| 1056 | + assert_eq!(txn.len(), 0); |
| 1057 | + assert!(nodes[0].node.get_and_clear_pending_events().is_empty()); |
| 1058 | + |
| 1059 | + if reorg_out { |
| 1060 | + // Reorg out bs_htlc_spend_tx, letting node A claim all the HTLCs instead. |
| 1061 | + disconnect_blocks(&nodes[0], ANTI_REORG_DELAY - 2); |
| 1062 | + assert_eq!(nodes[0].tx_broadcaster.txn_broadcast().len(), 0); |
| 1063 | + |
| 1064 | + // As soon as bs_htlc_spend_tx is disconnected, node A should consider all HTLCs |
| 1065 | + // claimable-on-timeout. |
| 1066 | + disconnect_blocks(&nodes[0], 1); |
| 1067 | + let balances = nodes[0].chain_monitor.chain_monitor.get_claimable_balances(&[]); |
| 1068 | + assert_eq!(balances.len(), if use_third_htlc { 3 } else { 2 }); |
| 1069 | + for balance in balances { |
| 1070 | + if let Balance::MaybeTimeoutClaimableHTLC { .. } = balance { |
| 1071 | + } else { |
| 1072 | + panic!("Unexpected balance {balance:?}"); |
| 1073 | + } |
| 1074 | + } |
| 1075 | + |
| 1076 | + connect_blocks(&nodes[0], 100); |
| 1077 | + let txn = nodes[0].tx_broadcaster.txn_broadcast(); |
| 1078 | + let mut claiming_outpoints = new_hash_set(); |
| 1079 | + for tx in txn.iter() { |
| 1080 | + for input in tx.input.iter() { |
| 1081 | + claiming_outpoints.insert(input.previous_output); |
| 1082 | + } |
| 1083 | + } |
| 1084 | + assert_eq!(claiming_outpoints.len(), if use_third_htlc { 3 } else { 2 }); |
| 1085 | + } else { |
| 1086 | + // Connect a final block, which puts `bs_htlc_spend_tx` at `ANTI_REORG_DELAY` and we wipe |
| 1087 | + // the claimable balances for the first two HTLCs. |
| 1088 | + connect_blocks(&nodes[0], 1); |
| 1089 | + let balances = nodes[0].chain_monitor.chain_monitor.get_claimable_balances(&[]); |
| 1090 | + assert_eq!(balances.len(), if use_third_htlc { 1 } else { 0 }); |
| 1091 | + |
| 1092 | + // Connect two more blocks to get `as_third_htlc_spend_tx` to `ANTI_REORG_DELAY` confs. |
| 1093 | + connect_blocks(&nodes[0], 2); |
| 1094 | + if use_third_htlc { |
| 1095 | + let failed_events = nodes[0].node.get_and_clear_pending_events(); |
| 1096 | + assert_eq!(failed_events.len(), 2); |
| 1097 | + let mut found_expected_events = [false, false]; |
| 1098 | + for event in failed_events { |
| 1099 | + match event { |
| 1100 | + Event::PaymentFailed { payment_hash: Some(payment_hash), .. }|Event::PaymentPathFailed { payment_hash, .. } => { |
| 1101 | + let path_failed = matches!(event, Event::PaymentPathFailed { .. }); |
| 1102 | + if payment_hash == payment_hash_c { |
| 1103 | + found_expected_events[if path_failed { 1 } else { 0 }] = true; |
| 1104 | + } else { |
| 1105 | + panic!("Wrong payment hash {event:?}"); |
| 1106 | + } |
| 1107 | + }, |
| 1108 | + _ => panic!("Wrong event {event:?}"), |
| 1109 | + } |
| 1110 | + } |
| 1111 | + assert_eq!(found_expected_events, [true, true]); |
| 1112 | + } |
| 1113 | + |
| 1114 | + // Further, there should be no spendable balances. |
| 1115 | + assert!(nodes[0].chain_monitor.chain_monitor.get_claimable_balances(&[]).is_empty()); |
| 1116 | + } |
| 1117 | +} |
| 1118 | + |
| 1119 | +#[test] |
| 1120 | +fn test_split_htlc_expiry_tracking() { |
| 1121 | + do_test_split_htlc_expiry_tracking(true, true); |
| 1122 | + do_test_split_htlc_expiry_tracking(false, true); |
| 1123 | + do_test_split_htlc_expiry_tracking(true, false); |
| 1124 | + do_test_split_htlc_expiry_tracking(false, false); |
| 1125 | +} |
0 commit comments