@@ -28,7 +28,7 @@ use common::{
2828} ;
2929use electrsd:: corepc_node:: Node as BitcoinD ;
3030use electrsd:: ElectrsD ;
31- use ldk_node:: config:: { AsyncPaymentsRole , EsploraSyncConfig } ;
31+ use ldk_node:: config:: { AsyncPaymentsRole , ChannelConfig , EsploraSyncConfig } ;
3232use ldk_node:: entropy:: NodeEntropy ;
3333use ldk_node:: liquidity:: LSPS2ServiceConfig ;
3434use ldk_node:: payment:: {
@@ -304,6 +304,100 @@ async fn multi_hop_sending() {
304304 expect_payment_successful_event ! ( nodes[ 0 ] , payment_id, Some ( fee_paid_msat) ) ;
305305}
306306
307+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
308+ async fn split_underpaid_bolt11_payment ( ) {
309+ let ( bitcoind, electrsd) = setup_bitcoind_and_electrsd ( ) ;
310+ let chain_source = random_chain_source ( & bitcoind, & electrsd) ;
311+ let ( node_a, node_b) = setup_two_nodes ( & chain_source, false , true , false ) ;
312+ let node_c = setup_node ( & chain_source, random_config ( true ) ) ;
313+
314+ let addr_c = node_c. onchain_payment ( ) . new_address ( ) . unwrap ( ) ;
315+ let premine_amount_sat = 5_000_000 ;
316+ premine_and_distribute_funds (
317+ & bitcoind. client ,
318+ & electrsd. client ,
319+ vec ! [ addr_c] ,
320+ Amount :: from_sat ( premine_amount_sat) ,
321+ )
322+ . await ;
323+ node_c. sync_wallets ( ) . unwrap ( ) ;
324+ assert_eq ! ( node_c. list_balances( ) . spendable_onchain_balance_sats, premine_amount_sat) ;
325+
326+ let mut receiver_channel_config = ChannelConfig :: default ( ) ;
327+ receiver_channel_config. accept_underpaying_htlcs = true ;
328+
329+ // The receiver opens both channels so its per-channel config accepts underpaying HTLCs.
330+ // It pushes liquidity to both payers so each payer can send half of the invoice back.
331+ let channel_amount_sat = 1_000_000 ;
332+ let push_amount_msat = Some ( 500_000_000 ) ;
333+ for payer in [ & node_a, & node_b] {
334+ node_c
335+ . open_channel (
336+ payer. node_id ( ) ,
337+ payer. listening_addresses ( ) . unwrap ( ) . first ( ) . unwrap ( ) . clone ( ) ,
338+ channel_amount_sat,
339+ push_amount_msat,
340+ Some ( receiver_channel_config) ,
341+ )
342+ . unwrap ( ) ;
343+
344+ let funding_txo_c = expect_channel_pending_event ! ( node_c, payer. node_id( ) ) ;
345+ let funding_txo_payer = expect_channel_pending_event ! ( payer, node_c. node_id( ) ) ;
346+ assert_eq ! ( funding_txo_c, funding_txo_payer) ;
347+ wait_for_tx ( & electrsd. client , funding_txo_c. txid ) . await ;
348+
349+ node_c. sync_wallets ( ) . unwrap ( ) ;
350+ }
351+
352+ generate_blocks_and_wait ( & bitcoind. client , & electrsd. client , 6 ) . await ;
353+
354+ for node in [ & node_a, & node_b, & node_c] {
355+ node. sync_wallets ( ) . unwrap ( ) ;
356+ }
357+
358+ expect_channel_ready_event ! ( node_c, node_a. node_id( ) ) ;
359+ expect_channel_ready_event ! ( node_a, node_c. node_id( ) ) ;
360+ expect_channel_ready_event ! ( node_c, node_b. node_id( ) ) ;
361+ expect_channel_ready_event ! ( node_b, node_c. node_id( ) ) ;
362+
363+ let amount_msat = 100_000_000 ;
364+ let half_amount_msat = amount_msat / 2 ;
365+ let invoice_description =
366+ Bolt11InvoiceDescription :: Direct ( Description :: new ( String :: from ( "split" ) ) . unwrap ( ) ) ;
367+ let invoice =
368+ node_c. bolt11_payment ( ) . receive ( amount_msat, & invoice_description. into ( ) , 3600 ) . unwrap ( ) ;
369+
370+ // Each payer sends only half the invoice amount, while declaring the full invoice amount as
371+ // the total MPP value. The receiver should claim only once both HTLCs arrive.
372+ let payment_id_a = node_a
373+ . bolt11_payment ( )
374+ . send_using_amount_underpaying ( & invoice, half_amount_msat, None )
375+ . unwrap ( ) ;
376+ let payment_id_b = node_b
377+ . bolt11_payment ( )
378+ . send_using_amount_underpaying ( & invoice, half_amount_msat, None )
379+ . unwrap ( ) ;
380+
381+ let receiver_payment_id = expect_payment_received_event ! ( node_c, amount_msat) ;
382+ assert_eq ! ( receiver_payment_id, Some ( PaymentId ( invoice. payment_hash( ) . 0 ) ) ) ;
383+ expect_payment_successful_event ! ( node_a, Some ( payment_id_a) , None ) ;
384+ expect_payment_successful_event ! ( node_b, Some ( payment_id_b) , None ) ;
385+
386+ // The receiver records the full invoice amount; each payer records only its own half.
387+ let receiver_payments =
388+ node_c. list_payments_with_filter ( |p| p. id == receiver_payment_id. unwrap ( ) ) ;
389+ assert_eq ! ( receiver_payments. len( ) , 1 ) ;
390+ assert_eq ! ( receiver_payments. first( ) . unwrap( ) . amount_msat, Some ( amount_msat) ) ;
391+
392+ let node_a_payments = node_a. list_payments_with_filter ( |p| p. id == payment_id_a) ;
393+ assert_eq ! ( node_a_payments. len( ) , 1 ) ;
394+ assert_eq ! ( node_a_payments. first( ) . unwrap( ) . amount_msat, Some ( half_amount_msat) ) ;
395+
396+ let node_b_payments = node_b. list_payments_with_filter ( |p| p. id == payment_id_b) ;
397+ assert_eq ! ( node_b_payments. len( ) , 1 ) ;
398+ assert_eq ! ( node_b_payments. first( ) . unwrap( ) . amount_msat, Some ( half_amount_msat) ) ;
399+ }
400+
307401#[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
308402async fn start_stop_reinit ( ) {
309403 let ( bitcoind, electrsd) = setup_bitcoind_and_electrsd ( ) ;
0 commit comments