@@ -19,6 +19,7 @@ use crate::events::{Event, ClosureReason, HTLCHandlingFailureType};
1919use crate :: ln:: msgs:: { BaseMessageHandler , ChannelMessageHandler , Init , MessageSendEvent } ;
2020use crate :: ln:: types:: ChannelId ;
2121use crate :: sign:: OutputSpender ;
22+ use crate :: types:: payment:: PaymentHash ;
2223use crate :: types:: string:: UntrustedString ;
2324use crate :: util:: ser:: Writeable ;
2425
@@ -899,3 +900,219 @@ 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 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 the 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
1065+ disconnect_blocks ( & nodes[ 0 ] , 1 ) ;
1066+ let balances = nodes[ 0 ] . chain_monitor . chain_monitor . get_claimable_balances ( & [ ] ) ;
1067+ assert_eq ! ( balances. len( ) , if use_third_htlc { 3 } else { 2 } ) ;
1068+
1069+ connect_blocks ( & nodes[ 0 ] , 100 ) ;
1070+ let txn = nodes[ 0 ] . tx_broadcaster . txn_broadcast ( ) ;
1071+ let mut claiming_outpoints = new_hash_set ( ) ;
1072+ for tx in txn. iter ( ) {
1073+ for input in tx. input . iter ( ) {
1074+ claiming_outpoints. insert ( input. previous_output ) ;
1075+ }
1076+ }
1077+ assert_eq ! ( claiming_outpoints. len( ) , if use_third_htlc { 3 } else { 2 } ) ;
1078+ } else {
1079+ // Connect a final block, which puts `bs_htlc_spend_tx` at `ANTI_REORG_DELAY` and we wipe
1080+ // the claimable balances for the first two HTLCs.
1081+ connect_blocks ( & nodes[ 0 ] , 1 ) ;
1082+ let balances = nodes[ 0 ] . chain_monitor . chain_monitor . get_claimable_balances ( & [ ] ) ;
1083+ assert_eq ! ( balances. len( ) , if use_third_htlc { 1 } else { 0 } ) ;
1084+
1085+ // Connect two more blocks to get `as_third_htlc_spend_tx` to `ANTI_REORG_DELAY` confs.
1086+ connect_blocks ( & nodes[ 0 ] , 2 ) ;
1087+ if use_third_htlc {
1088+ let failed_events = nodes[ 0 ] . node . get_and_clear_pending_events ( ) ;
1089+ assert_eq ! ( failed_events. len( ) , 2 ) ;
1090+ let mut found_expected_events = [ false , false ] ;
1091+ for event in failed_events {
1092+ match event {
1093+ Event :: PaymentFailed { payment_hash : Some ( payment_hash) , .. } |Event :: PaymentPathFailed { payment_hash, .. } => {
1094+ let path_failed = matches ! ( event, Event :: PaymentPathFailed { .. } ) ;
1095+ if payment_hash == payment_hash_c {
1096+ found_expected_events[ if path_failed { 1 } else { 0 } ] = true ;
1097+ } else {
1098+ panic ! ( "Wrong payment hash {event:?}" ) ;
1099+ }
1100+ } ,
1101+ _ => panic ! ( "Wrong event {event:?}" ) ,
1102+ }
1103+ }
1104+ assert_eq ! ( found_expected_events, [ true , true ] ) ;
1105+ }
1106+
1107+ // Further, there should be no spendable balances.
1108+ assert ! ( nodes[ 0 ] . chain_monitor. chain_monitor. get_claimable_balances( & [ ] ) . is_empty( ) ) ;
1109+ }
1110+ }
1111+
1112+ #[ test]
1113+ fn test_split_htlc_expiry_tracking ( ) {
1114+ do_test_split_htlc_expiry_tracking ( true , true ) ;
1115+ do_test_split_htlc_expiry_tracking ( false , true ) ;
1116+ do_test_split_htlc_expiry_tracking ( true , false ) ;
1117+ do_test_split_htlc_expiry_tracking ( false , false ) ;
1118+ }
0 commit comments