@@ -1068,18 +1068,14 @@ def _assert_can_add_htlc(self, *, htlc_proposer: HTLCOwner, amount_msat: int,
1068
1068
"""Raises PaymentFailure if the htlc_proposer cannot add this new HTLC.
1069
1069
(this is relevant both for forwarding and endpoint)
1070
1070
"""
1071
+ # NOTE: ignore_min_htlc_value never used?
1071
1072
htlc_receiver = htlc_proposer .inverted ()
1072
1073
# note: all these tests are about the *receiver's* *next* commitment transaction,
1073
1074
# and the constraints are the ones imposed by their config
1074
1075
ctn = self .get_next_ctn (htlc_receiver )
1075
1076
chan_config = self .config [htlc_receiver ]
1076
- if self .get_state () != ChannelState .OPEN :
1077
- raise PaymentFailure ('Channel not open' , self .get_state ())
1078
- if htlc_proposer == LOCAL :
1079
- if not self .can_send_ctx_updates ():
1080
- raise PaymentFailure ('Channel cannot send ctx updates' )
1081
- if not self .can_send_update_add_htlc ():
1082
- raise PaymentFailure ('Channel cannot add htlc' )
1077
+
1078
+ htlc_min , htlc_max , num_htlcs = self .determine_htlc_constraints (htlc_proposer )
1083
1079
1084
1080
# If proposer is LOCAL we apply stricter checks as that is behaviour we can control.
1085
1081
# This should lead to fewer disagreements (i.e. channels failing).
@@ -1089,32 +1085,67 @@ def _assert_can_add_htlc(self, *, htlc_proposer: HTLCOwner, amount_msat: int,
1089
1085
if not ignore_min_htlc_value :
1090
1086
if amount_msat <= 0 :
1091
1087
raise PaymentFailure ("HTLC value must be positive" )
1092
- if amount_msat < chan_config . htlc_minimum_msat :
1088
+ if amount_msat < htlc_min :
1093
1089
raise PaymentFailure (f'HTLC value too small: { amount_msat } msat' )
1094
1090
1095
1091
# check proposer can afford htlc
1096
1092
max_can_send_msat = self .available_to_spend (htlc_proposer , strict = strict )
1097
1093
if max_can_send_msat < amount_msat :
1098
1094
raise PaymentFailure (f'Not enough balance. can send: { max_can_send_msat } , tried: { amount_msat } ' )
1099
1095
1096
+ # check "max_htlc_value_in_flight_msat"
1097
+ current_htlc_sum = htlcsum (self .hm .htlcs_by_direction (htlc_receiver , direction = RECEIVED , ctn = ctn ).values ())
1098
+ if current_htlc_sum + amount_msat > chan_config .max_htlc_value_in_flight_msat :
1099
+ raise PaymentFailure (f'HTLC value sum (sum of pending htlcs: { current_htlc_sum / 1000 } sat '
1100
+ f'plus new htlc: { amount_msat / 1000 } sat) '
1101
+ f'would exceed max allowed: { chan_config .max_htlc_value_in_flight_msat / 1000 } sat' )
1102
+
1103
+ def determine_htlc_constraints (self , htlc_proposer : HTLCOwner ) -> Tuple [int , int , int ]:
1104
+ """Raises PaymentFailure if no valid amount range can be determined, or if no HTLCs can be added.
1105
+ (this is relevant both for forwarding and endpoint)
1106
+ """
1107
+ htlc_receiver = htlc_proposer .inverted ()
1108
+ ctn = self .get_next_ctn (htlc_receiver )
1109
+ chan_config = self .config [htlc_receiver ]
1110
+ if self .get_state () != ChannelState .OPEN :
1111
+ raise PaymentFailure ('Channel not open' , self .get_state ())
1112
+ if htlc_proposer == LOCAL :
1113
+ if not self .can_send_ctx_updates ():
1114
+ raise PaymentFailure ('Channel cannot send ctx updates' )
1115
+ if not self .can_send_update_add_htlc ():
1116
+ raise PaymentFailure ('Channel cannot add htlc' )
1117
+
1118
+ # If proposer is LOCAL we apply stricter checks as that is behaviour we can control.
1119
+ # This should lead to fewer disagreements (i.e. channels failing).
1120
+ strict = (htlc_proposer == LOCAL )
1121
+
1100
1122
# check "max_accepted_htlcs"
1101
1123
# this is the loose check BOLT-02 specifies:
1102
- if len (self .hm .htlcs_by_direction (htlc_receiver , direction = RECEIVED , ctn = ctn )) + 1 > chan_config .max_accepted_htlcs :
1124
+ remaining_num_htlcs = (chan_config .max_accepted_htlcs
1125
+ - len (self .hm .htlcs_by_direction (htlc_receiver , direction = RECEIVED , ctn = ctn )))
1126
+ if remaining_num_htlcs < 1 :
1103
1127
raise PaymentFailure ('Too many HTLCs already in channel' )
1104
1128
# however, c-lightning is a lot stricter, so extra checks:
1105
1129
# https://github.com/ElementsProject/lightning/blob/4dcd4ca1556b13b6964a10040ba1d5ef82de4788/channeld/full_channel.c#L581
1106
1130
if strict :
1107
1131
max_concurrent_htlcs = min (self .config [htlc_proposer ].max_accepted_htlcs ,
1108
1132
self .config [htlc_receiver ].max_accepted_htlcs )
1109
- if len (self .hm .htlcs (htlc_receiver , ctn = ctn )) + 1 > max_concurrent_htlcs :
1133
+ remaining_num_htlcs = max_concurrent_htlcs - len (self .hm .htlcs (htlc_receiver , ctn = ctn ))
1134
+ if remaining_num_htlcs < 1 :
1110
1135
raise PaymentFailure ('Too many HTLCs already in channel' )
1111
1136
1112
1137
# check "max_htlc_value_in_flight_msat"
1113
1138
current_htlc_sum = htlcsum (self .hm .htlcs_by_direction (htlc_receiver , direction = RECEIVED , ctn = ctn ).values ())
1114
- if current_htlc_sum + amount_msat > chan_config .max_htlc_value_in_flight_msat :
1115
- raise PaymentFailure (f'HTLC value sum (sum of pending htlcs: { current_htlc_sum / 1000 } sat '
1116
- f'plus new htlc: { amount_msat / 1000 } sat) '
1117
- f'would exceed max allowed: { chan_config .max_htlc_value_in_flight_msat / 1000 } sat' )
1139
+ remaining_max_inflight = chan_config .max_htlc_value_in_flight_msat - current_htlc_sum
1140
+
1141
+ max_can_send_msat = min (self .available_to_spend (htlc_proposer , strict = strict ), remaining_max_inflight )
1142
+
1143
+ if max_can_send_msat <= chan_config .htlc_minimum_msat :
1144
+ raise PaymentFailure (f'Remaining allowed inflight HTLC amount '
1145
+ f'(max: { chan_config .max_htlc_value_in_flight_msat / 1000 } sat, '
1146
+ f'sum of pending htlcs: { current_htlc_sum / 1000 } sat) '
1147
+ f'does not exceed min HTLC amount (min: { chan_config .htlc_minimum_msat / 1000 } sat). ' )
1148
+ return chan_config .htlc_minimum_msat , max_can_send_msat , remaining_num_htlcs
1118
1149
1119
1150
def can_pay (self , amount_msat : int , * , check_frozen = False ) -> bool :
1120
1151
"""Returns whether we can add an HTLC of given value."""
0 commit comments