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