@@ -19,6 +19,7 @@ use crate::events::{Event, ClosureReason, HTLCHandlingFailureType};
19
19
use crate :: ln:: msgs:: { BaseMessageHandler , ChannelMessageHandler , Init , MessageSendEvent } ;
20
20
use crate :: ln:: types:: ChannelId ;
21
21
use crate :: sign:: OutputSpender ;
22
+ use crate :: types:: payment:: PaymentHash ;
22
23
use crate :: types:: string:: UntrustedString ;
23
24
use crate :: util:: ser:: Writeable ;
24
25
@@ -899,3 +900,213 @@ fn test_retries_own_commitment_broadcast_after_reorg() {
899
900
do_test_retries_own_commitment_broadcast_after_reorg ( true , false ) ;
900
901
do_test_retries_own_commitment_broadcast_after_reorg ( true , true ) ;
901
902
}
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