Skip to content

Commit ff95a56

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.
1 parent 0808478 commit ff95a56

File tree

1 file changed

+211
-0
lines changed

1 file changed

+211
-0
lines changed

lightning/src/ln/reorg_tests.rs

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use crate::events::{Event, ClosureReason, HTLCHandlingFailureType};
1919
use crate::ln::msgs::{BaseMessageHandler, ChannelMessageHandler, Init, MessageSendEvent};
2020
use crate::ln::types::ChannelId;
2121
use crate::sign::OutputSpender;
22+
use crate::types::payment::PaymentHash;
2223
use crate::types::string::UntrustedString;
2324
use crate::util::ser::Writeable;
2425

@@ -899,3 +900,213 @@ fn test_retries_own_commitment_broadcast_after_reorg() {
899900
do_test_retries_own_commitment_broadcast_after_reorg(true, false);
900901
do_test_retries_own_commitment_broadcast_after_reorg(true, true);
901902
}
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 the 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+
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+
assert_eq!(nodes[0].tx_broadcaster.txn_broadcast().len(), 0);
991+
// Now connect blocks until the first HTLC expires
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+
eprintln!("\n\n\n\n\n\n\n\n\n");
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+
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 4);
1051+
let mut txn = nodes[0].tx_broadcaster.txn_broadcast();
1052+
assert_eq!(txn.len(), 0);
1053+
assert!(nodes[0].node.get_and_clear_pending_events().is_empty());
1054+
1055+
if reorg_out {
1056+
// Reorg out bs_htlc_spend_tx, letting node A the claim all the HTLCs instead.
1057+
disconnect_blocks(&nodes[0], ANTI_REORG_DELAY - 2);
1058+
assert_eq!(nodes[0].tx_broadcaster.txn_broadcast().len(), 0);
1059+
1060+
// As soon as bs_htlc_spend_tx is disconnected
1061+
disconnect_blocks(&nodes[0], 1);
1062+
let balances = nodes[0].chain_monitor.chain_monitor.get_claimable_balances(&[]);
1063+
assert_eq!(balances.len(), if use_third_htlc { 3 } else { 2 });
1064+
1065+
connect_blocks(&nodes[0], 100);
1066+
let txn = nodes[0].tx_broadcaster.txn_broadcast();
1067+
let mut claiming_outpoints = new_hash_set();
1068+
for tx in txn.iter() {
1069+
for input in tx.input.iter() {
1070+
claiming_outpoints.insert(input.previous_output);
1071+
}
1072+
}
1073+
assert_eq!(claiming_outpoints.len(), if use_third_htlc { 3 } else { 2 });
1074+
} else {
1075+
connect_blocks(&nodes[0], 1);
1076+
let balances = nodes[0].chain_monitor.chain_monitor.get_claimable_balances(&[]);
1077+
assert_eq!(balances.len(), if use_third_htlc { 1 } else { 0 });
1078+
1079+
// Connect 3 more blocks to get as_third_htlc_spend_tx to ANTI_REORG_DELAY confirmations.
1080+
connect_blocks(&nodes[0], 2);
1081+
if use_third_htlc {
1082+
let failed_events = nodes[0].node.get_and_clear_pending_events();
1083+
assert_eq!(failed_events.len(), 2);
1084+
let mut found_expected_events = [false, false];
1085+
for event in failed_events {
1086+
match event {
1087+
Event::PaymentFailed { payment_hash: Some(payment_hash), .. }|Event::PaymentPathFailed { payment_hash, .. } => {
1088+
let path_failed = matches!(event, Event::PaymentPathFailed { .. });
1089+
if payment_hash == payment_hash_c {
1090+
found_expected_events[if path_failed { 1 } else { 0 }] = true;
1091+
} else {
1092+
panic!("Wrong payment hash {event:?}");
1093+
}
1094+
},
1095+
_ => panic!("Wrong event {event:?}"),
1096+
}
1097+
}
1098+
assert_eq!(found_expected_events, [true, true]);
1099+
}
1100+
1101+
// Further, there should be no spendable balances.
1102+
assert!(nodes[0].chain_monitor.chain_monitor.get_claimable_balances(&[]).is_empty());
1103+
}
1104+
}
1105+
1106+
#[test]
1107+
fn test_split_htlc_expiry_tracking() {
1108+
do_test_split_htlc_expiry_tracking(true, true);
1109+
do_test_split_htlc_expiry_tracking(false, true);
1110+
do_test_split_htlc_expiry_tracking(true, false);
1111+
do_test_split_htlc_expiry_tracking(false, false);
1112+
}

0 commit comments

Comments
 (0)