@@ -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,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