@@ -13,8 +13,8 @@ use bitcoin::secp256k1::{self, PublicKey, Secp256k1};
1313use lightning_invoice:: Bolt11Invoice ;
1414
1515use crate :: blinded_path:: payment:: {
16- BlindedPaymentPath , DummyTlvs , ForwardTlvs , PaymentConstraints , PaymentForwardNode ,
17- PaymentRelay , ReceiveTlvs ,
16+ compute_next_htlc_minimum_msat , BlindedPaymentPath , DummyTlvs , ForwardTlvs , PaymentConstraints ,
17+ PaymentForwardNode , PaymentRelay , ReceiveTlvs ,
1818} ;
1919use crate :: blinded_path:: { BlindedHop , Direction , IntroductionNode } ;
2020use crate :: crypto:: chacha20:: ChaCha20 ;
@@ -150,6 +150,7 @@ where
150150 let network_graph = self . network_graph . deref ( ) . read_only ( ) ;
151151 let is_recipient_announced =
152152 network_graph. nodes ( ) . contains_key ( & NodeId :: from_pubkey ( & recipient) ) ;
153+ let dummy_tlvs = DummyTlvs :: default ( ) ;
153154
154155 let paths = first_hops. into_iter ( )
155156 . filter ( |details| details. counterparty . features . supports_route_blinding ( ) )
@@ -176,12 +177,32 @@ where
176177 None => return None ,
177178 } ;
178179
179- let cltv_expiry_delta = payment_relay. cltv_expiry_delta as u32 ;
180+ let cltv_expiry_delta = payment_relay. cltv_expiry_delta as u32
181+ + dummy_tlvs. payment_relay . cltv_expiry_delta as u32 * DEFAULT_PAYMENT_DUMMY_HOPS as u32 ;
182+ let dummy_hops_htlc_minimum_msat = [ dummy_tlvs; DEFAULT_PAYMENT_DUMMY_HOPS ]
183+ . iter ( )
184+ . fold ( 1 , |htlc_minimum_msat, dummy_tlvs| {
185+ compute_next_htlc_minimum_msat (
186+ htlc_minimum_msat,
187+ & dummy_tlvs. payment_constraints ,
188+ & dummy_tlvs. payment_relay ,
189+ )
190+ } ) ;
191+ let htlc_minimum_msat = cmp:: max (
192+ details. inbound_htlc_minimum_msat . unwrap_or ( 0 ) ,
193+ cmp:: max (
194+ tlvs. payment_constraints . htlc_minimum_msat ,
195+ dummy_hops_htlc_minimum_msat,
196+ ) ,
197+ ) ;
198+ if amount_msats. unwrap_or ( u64:: MAX ) < htlc_minimum_msat {
199+ return None ;
200+ }
180201 let payment_constraints = PaymentConstraints {
181202 max_cltv_expiry : tlvs. payment_constraints
182203 . max_cltv_expiry
183204 . saturating_add ( cltv_expiry_delta) ,
184- htlc_minimum_msat : details . inbound_htlc_minimum_msat . unwrap_or ( 0 ) ,
205+ htlc_minimum_msat,
185206 } ;
186207 Some ( PaymentForwardNode {
187208 tlvs : ForwardTlvs {
@@ -197,7 +218,7 @@ where
197218 } )
198219 . map ( |forward_node| {
199220 BlindedPaymentPath :: new_with_dummy_hops (
200- & [ forward_node] , recipient, & [ DummyTlvs :: default ( ) ; DEFAULT_PAYMENT_DUMMY_HOPS ] ,
221+ & [ forward_node] , recipient, & [ dummy_tlvs ; DEFAULT_PAYMENT_DUMMY_HOPS ] ,
201222 local_node_receive_key, tlvs. clone ( ) , u64:: MAX , MIN_FINAL_CLTV_EXPIRY_DELTA , & self . entropy_source , secp_ctx
202223 )
203224 } )
@@ -209,7 +230,7 @@ where
209230 _ => {
210231 if network_graph. nodes ( ) . contains_key ( & NodeId :: from_pubkey ( & recipient) ) {
211232 BlindedPaymentPath :: new_with_dummy_hops (
212- & [ ] , recipient, & [ DummyTlvs :: default ( ) ; DEFAULT_PAYMENT_DUMMY_HOPS ] ,
233+ & [ ] , recipient, & [ dummy_tlvs ; DEFAULT_PAYMENT_DUMMY_HOPS ] ,
213234 local_node_receive_key, tlvs, u64:: MAX , MIN_FINAL_CLTV_EXPIRY_DELTA , & self . entropy_source , secp_ctx
214235 ) . map ( |path| vec ! [ path] )
215236 } else {
@@ -4077,21 +4098,26 @@ fn build_route_from_hops_internal<L: Logger>(
40774098
40784099#[ cfg( test) ]
40794100mod tests {
4080- use crate :: blinded_path:: payment:: { BlindedPayInfo , BlindedPaymentPath } ;
4101+ use crate :: blinded_path:: payment:: {
4102+ BlindedPayInfo , BlindedPaymentPath , Bolt12RefundContext , PaymentConstraints ,
4103+ PaymentContext , ReceiveTlvs ,
4104+ } ;
40814105 use crate :: blinded_path:: BlindedHop ;
40824106 use crate :: chain:: transaction:: OutPoint ;
40834107 use crate :: crypto:: chacha20:: ChaCha20 ;
40844108 use crate :: ln:: chan_utils:: make_funding_redeemscript;
4085- use crate :: ln:: channel_state:: { ChannelCounterparty , ChannelDetails , ChannelShutdownState } ;
4109+ use crate :: ln:: channel_state:: {
4110+ ChannelCounterparty , ChannelDetails , ChannelShutdownState , CounterpartyForwardingInfo ,
4111+ } ;
40864112 use crate :: ln:: channelmanager;
40874113 use crate :: ln:: msgs:: { UnsignedChannelUpdate , MAX_VALUE_MSAT } ;
40884114 use crate :: ln:: types:: ChannelId ;
40894115 use crate :: routing:: gossip:: { EffectiveCapacity , NetworkGraph , NodeId , P2PGossipSync } ;
40904116 use crate :: routing:: router:: {
40914117 add_random_cltv_offset, build_route_from_hops_internal, default_node_features, get_route,
4092- BlindedPathCandidate , BlindedTail , CandidateRouteHop , InFlightHtlcs , Path ,
4118+ BlindedPathCandidate , BlindedTail , CandidateRouteHop , DefaultRouter , InFlightHtlcs , Path ,
40934119 PaymentParameters , PublicHopCandidate , Route , RouteHint , RouteHintHop , RouteHop ,
4094- RouteParameters , RoutingFees , ScorerAccountingForInFlightHtlcs ,
4120+ RouteParameters , Router , RoutingFees , ScorerAccountingForInFlightHtlcs ,
40954121 DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA , MAX_PATH_LENGTH_ESTIMATE ,
40964122 } ;
40974123 use crate :: routing:: scoring:: {
@@ -4101,6 +4127,7 @@ mod tests {
41014127 use crate :: routing:: test_utils:: * ;
41024128 use crate :: routing:: utxo:: UtxoResult ;
41034129 use crate :: types:: features:: { BlindedHopFeatures , ChannelFeatures , InitFeatures , NodeFeatures } ;
4130+ use crate :: types:: payment:: PaymentSecret ;
41044131 use crate :: util:: config:: UserConfig ;
41054132 #[ cfg( c_bindings) ]
41064133 use crate :: util:: ser:: Writer ;
@@ -4120,7 +4147,8 @@ mod tests {
41204147
41214148 use crate :: io:: Cursor ;
41224149 use crate :: prelude:: * ;
4123- use crate :: sync:: { Arc , Mutex } ;
4150+ use crate :: sign:: { RandomBytes , ReceiveAuthKey } ;
4151+ use crate :: sync:: { Arc , Mutex , RwLock } ;
41244152
41254153 #[ rustfmt:: skip]
41264154 fn get_channel_details ( short_channel_id : Option < u64 > , node_id : PublicKey ,
@@ -8980,6 +9008,57 @@ mod tests {
89809008 } else { panic ! ( ) }
89819009 }
89829010
9011+ #[ test]
9012+ fn blinded_path_creation_respects_dummy_tail_htlc_minimum ( ) {
9013+ let secp_ctx = Secp256k1 :: new ( ) ;
9014+ let logger = Arc :: new ( ln_test_utils:: TestLogger :: new ( ) ) ;
9015+ let network_graph = Arc :: new ( NetworkGraph :: new ( Network :: Testnet , Arc :: clone ( & logger) ) ) ;
9016+ let scorer = Arc :: new ( RwLock :: new ( ln_test_utils:: TestScorer :: new ( ) ) ) ;
9017+ let router = DefaultRouter :: new (
9018+ Arc :: clone ( & network_graph) ,
9019+ Arc :: clone ( & logger) ,
9020+ Arc :: new ( RandomBytes :: new ( [ 42 ; 32 ] ) ) ,
9021+ Arc :: clone ( & scorer) ,
9022+ Default :: default ( ) ,
9023+ ) ;
9024+ let config = UserConfig :: default ( ) ;
9025+
9026+ let mut first_hop = get_channel_details (
9027+ Some ( 42 ) ,
9028+ ln_test_utils:: pubkey ( 43 ) ,
9029+ channelmanager:: provided_init_features ( & config) ,
9030+ 1_000_000 ,
9031+ ) ;
9032+ first_hop. inbound_capacity_msat = 1_000_000 ;
9033+ first_hop. inbound_htlc_minimum_msat = Some ( 1_000 ) ;
9034+ first_hop. inbound_htlc_maximum_msat = Some ( 1_000_000 ) ;
9035+ first_hop. counterparty . forwarding_info = Some ( CounterpartyForwardingInfo {
9036+ fee_base_msat : 1_000 ,
9037+ fee_proportional_millionths : 0 ,
9038+ cltv_expiry_delta : 18 ,
9039+ } ) ;
9040+
9041+ let tlvs = ReceiveTlvs {
9042+ payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
9043+ payment_constraints : PaymentConstraints {
9044+ max_cltv_expiry : u32:: MAX ,
9045+ htlc_minimum_msat : 2_500 ,
9046+ } ,
9047+ payment_context : PaymentContext :: Bolt12Refund ( Bolt12RefundContext { } ) ,
9048+ } ;
9049+
9050+ assert ! ( router
9051+ . create_blinded_payment_paths(
9052+ ln_test_utils:: pubkey( 44 ) ,
9053+ ReceiveAuthKey ( [ 41 ; 32 ] ) ,
9054+ vec![ first_hop] ,
9055+ tlvs,
9056+ Some ( 2_000 ) ,
9057+ & secp_ctx,
9058+ )
9059+ . is_err( ) ) ;
9060+ }
9061+
89839062 #[ test]
89849063 #[ rustfmt:: skip]
89859064 fn path_contribution_includes_min_htlc_overpay ( ) {
0 commit comments