Skip to content

Commit 62bd236

Browse files
committed
Add a test case for the issues fixed in the previous few commits
This adds a single test which exercises both the ability to prune locktimed packages when inputs are spent as well as the creation-height tracking for locktimed packages. Trivial conflicts resolved in: * lightning/src/ln/reorg_tests.rs
1 parent 6b730e0 commit 62bd236

File tree

1 file changed

+225
-1
lines changed

1 file changed

+225
-1
lines changed

lightning/src/ln/reorg_tests.rs

Lines changed: 225 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@
1010
//! Further functional tests which test blockchain reorganizations.
1111
1212
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};
1414
use crate::chain::transaction::OutPoint;
1515
use crate::chain::Confirm;
1616
use crate::events::{Event, MessageSendEventsProvider, ClosureReason, HTLCDestination, MessageSendEvent};
1717
use crate::ln::msgs::{ChannelMessageHandler, Init};
1818
use crate::ln::types::ChannelId;
1919
use crate::sign::OutputSpender;
20+
use crate::types::payment::PaymentHash;
2021
use crate::util::ser::Writeable;
2122
use crate::util::string::UntrustedString;
2223

@@ -897,3 +898,226 @@ fn test_retries_own_commitment_broadcast_after_reorg() {
897898
do_test_retries_own_commitment_broadcast_after_reorg(true, false);
898899
do_test_retries_own_commitment_broadcast_after_reorg(true, true);
899900
}
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

Comments
 (0)