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