@@ -3147,6 +3147,163 @@ async fn do_lsps2_client_service_integration(client_trusts_lsp: bool) {
31473147 assert_eq ! ( client_node. payment( & payment_id) . unwrap( ) . status, PaymentStatus :: Failed ) ;
31483148}
31493149
3150+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
3151+ async fn bolt12_lsps2_client_service_integration ( ) {
3152+ let ( bitcoind, electrsd) = setup_bitcoind_and_electrsd ( ) ;
3153+ let esplora_url = format ! ( "http://{}" , electrsd. esplora_url. as_ref( ) . unwrap( ) ) ;
3154+
3155+ let mut sync_config = EsploraSyncConfig :: default ( ) ;
3156+ sync_config. background_sync_config = None ;
3157+
3158+ let channel_opening_fee_ppm = 10_000 ;
3159+ let lsps2_service_config = LSPS2ServiceConfig {
3160+ require_token : None ,
3161+ advertise_service : false ,
3162+ channel_opening_fee_ppm,
3163+ channel_over_provisioning_ppm : 100_000 ,
3164+ max_payment_size_msat : 1_000_000_000 ,
3165+ min_payment_size_msat : 0 ,
3166+ min_channel_lifetime : 100 ,
3167+ min_channel_opening_fee_msat : 0 ,
3168+ max_client_to_self_delay : 1024 ,
3169+ client_trusts_lsp : true ,
3170+ disable_client_reserve : false ,
3171+ } ;
3172+
3173+ let service_config = random_config ( ) ;
3174+ setup_builder ! ( service_builder, service_config. node_config) ;
3175+ service_builder. set_chain_source_esplora ( esplora_url. clone ( ) , Some ( sync_config) ) ;
3176+ service_builder. enable_liquidity_provider ( lsps2_service_config) ;
3177+ let service_node = service_builder. build ( service_config. node_entropy . into ( ) ) . unwrap ( ) ;
3178+ service_node. start ( ) . unwrap ( ) ;
3179+
3180+ let service_node_id = service_node. node_id ( ) ;
3181+ let service_addr = service_node. listening_addresses ( ) . unwrap ( ) . first ( ) . unwrap ( ) . clone ( ) ;
3182+
3183+ let client_config = random_config ( ) ;
3184+ setup_builder ! ( client_builder, client_config. node_config) ;
3185+ client_builder. set_chain_source_esplora ( esplora_url. clone ( ) , Some ( sync_config) ) ;
3186+ client_builder. add_liquidity_source ( service_node_id, service_addr, None , true ) ;
3187+ let client_node = client_builder. build ( client_config. node_entropy . into ( ) ) . unwrap ( ) ;
3188+ client_node. start ( ) . unwrap ( ) ;
3189+
3190+ let payer_config = random_config ( ) ;
3191+ setup_builder ! ( payer_builder, payer_config. node_config) ;
3192+ payer_builder. set_chain_source_esplora ( esplora_url. clone ( ) , Some ( sync_config) ) ;
3193+ let payer_node = payer_builder. build ( payer_config. node_entropy . into ( ) ) . unwrap ( ) ;
3194+ payer_node. start ( ) . unwrap ( ) ;
3195+
3196+ let client_addr = client_node. listening_addresses ( ) . unwrap ( ) . first ( ) . unwrap ( ) . clone ( ) ;
3197+ payer_node. connect ( client_node. node_id ( ) , client_addr, false ) . unwrap ( ) ;
3198+
3199+ let service_onchain_addr = service_node. onchain_payment ( ) . new_address ( ) . unwrap ( ) ;
3200+ let client_onchain_addr = client_node. onchain_payment ( ) . new_address ( ) . unwrap ( ) ;
3201+ let payer_onchain_addr = payer_node. onchain_payment ( ) . new_address ( ) . unwrap ( ) ;
3202+ premine_and_distribute_funds (
3203+ & bitcoind. client ,
3204+ & electrsd. client ,
3205+ vec ! [ service_onchain_addr, client_onchain_addr, payer_onchain_addr] ,
3206+ Amount :: from_sat ( 10_000_000 ) ,
3207+ )
3208+ . await ;
3209+ service_node. sync_wallets ( ) . unwrap ( ) ;
3210+ client_node. sync_wallets ( ) . unwrap ( ) ;
3211+ payer_node. sync_wallets ( ) . unwrap ( ) ;
3212+
3213+ open_channel ( & payer_node, & service_node, 5_000_000 , true , & electrsd) . await ;
3214+ generate_blocks_and_wait ( & bitcoind. client , & electrsd. client , 6 ) . await ;
3215+ service_node. sync_wallets ( ) . unwrap ( ) ;
3216+ payer_node. sync_wallets ( ) . unwrap ( ) ;
3217+ expect_channel_ready_event ! ( payer_node, service_node. node_id( ) ) ;
3218+ expect_channel_ready_event ! ( service_node, payer_node. node_id( ) ) ;
3219+ while payer_node. status ( ) . latest_node_announcement_broadcast_timestamp . is_none ( ) {
3220+ tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( 10 ) ) . await ;
3221+ }
3222+ tokio:: time:: sleep ( std:: time:: Duration :: from_secs ( 1 ) ) . await ;
3223+
3224+ let fixed_amount_msat = 100_000_000 ;
3225+ // The recipient has no channel yet, but the connected payer gives the default message router a
3226+ // peer through which it can construct an offer path. This keeps the long-lived offer addressed
3227+ // by a blinded node path; the single-use intercept SCID is introduced only in the invoice.
3228+ assert ! ( client_node. list_channels( ) . is_empty( ) ) ;
3229+ let fixed_offer =
3230+ client_node. bolt12_payment ( ) . receive ( fixed_amount_msat, "fixed" , None , None ) . unwrap ( ) ;
3231+ assert ! ( !fixed_offer. paths( ) . is_empty( ) ) ;
3232+ assert_ne ! ( fixed_offer. issuer_signing_pubkey( ) , Some ( client_node. node_id( ) ) ) ;
3233+ let fixed_payment_id =
3234+ payer_node. bolt12_payment ( ) . send ( & fixed_offer, None , None , None ) . unwrap ( ) ;
3235+
3236+ expect_channel_pending_event ! ( service_node, client_node. node_id( ) ) ;
3237+ expect_channel_ready_event ! ( service_node, client_node. node_id( ) ) ;
3238+ expect_event ! ( service_node, PaymentForwarded ) ;
3239+ expect_channel_pending_event ! ( client_node, service_node. node_id( ) ) ;
3240+ expect_channel_ready_event ! ( client_node, service_node. node_id( ) ) ;
3241+ expect_payment_successful_event ! ( payer_node, Some ( fixed_payment_id) , None ) ;
3242+ let fixed_fee_msat = fixed_amount_msat * channel_opening_fee_ppm as u64 / 1_000_000 ;
3243+ let fixed_received_msat = fixed_amount_msat - fixed_fee_msat;
3244+ let fixed_receiver_payment_id =
3245+ expect_payment_received_event ! ( client_node, fixed_received_msat) . unwrap ( ) ;
3246+ match client_node. payment ( & fixed_receiver_payment_id) . unwrap ( ) . kind {
3247+ PaymentKind :: Bolt12Offer { counterparty_skimmed_fee_msat, .. } => {
3248+ assert_eq ! ( counterparty_skimmed_fee_msat, Some ( fixed_fee_msat) ) ;
3249+ } ,
3250+ _ => panic ! ( "Unexpected payment kind" ) ,
3251+ }
3252+
3253+ // The over-provisioned part of the first JIT channel can receive this payment in full. The
3254+ // offers flow must therefore keep the ordinary blinded path and avoid negotiating or exposing a
3255+ // second intercept SCID.
3256+ let client_channel_count = client_node. list_channels ( ) . len ( ) ;
3257+ let service_channel_count = service_node. list_channels ( ) . len ( ) ;
3258+ let ordinary_amount_msat = 5_000_000 ;
3259+ let ordinary_offer = client_node
3260+ . bolt12_payment ( )
3261+ . receive ( ordinary_amount_msat, "existing inbound" , None , None )
3262+ . unwrap ( ) ;
3263+ let ordinary_payment_id =
3264+ payer_node. bolt12_payment ( ) . send ( & ordinary_offer, None , None , None ) . unwrap ( ) ;
3265+
3266+ expect_event ! ( service_node, PaymentForwarded ) ;
3267+ expect_payment_successful_event ! ( payer_node, Some ( ordinary_payment_id) , None ) ;
3268+ let ordinary_receiver_payment_id =
3269+ expect_payment_received_event ! ( client_node, ordinary_amount_msat) . unwrap ( ) ;
3270+ match client_node. payment ( & ordinary_receiver_payment_id) . unwrap ( ) . kind {
3271+ PaymentKind :: Bolt12Offer { counterparty_skimmed_fee_msat, .. } => {
3272+ assert_eq ! ( counterparty_skimmed_fee_msat, None ) ;
3273+ } ,
3274+ _ => panic ! ( "Unexpected payment kind" ) ,
3275+ }
3276+ assert_eq ! ( client_node. list_channels( ) . len( ) , client_channel_count) ;
3277+ assert_eq ! ( service_node. list_channels( ) . len( ) , service_channel_count) ;
3278+
3279+ // The first JIT channel cannot carry this larger variable payment. The variable offer disables
3280+ // MPP, so the payer must use the fresh JIT path rather than splitting across both paths.
3281+ let variable_amount_msat = 200_000_000 ;
3282+ let variable_offer =
3283+ client_node. bolt12_payment ( ) . receive_variable_amount ( "variable" , None ) . unwrap ( ) ;
3284+ let variable_payment_id = payer_node
3285+ . bolt12_payment ( )
3286+ . send_using_amount ( & variable_offer, variable_amount_msat, None , None , None )
3287+ . unwrap ( ) ;
3288+
3289+ expect_channel_pending_event ! ( service_node, client_node. node_id( ) ) ;
3290+ expect_channel_ready_event ! ( service_node, client_node. node_id( ) ) ;
3291+ expect_event ! ( service_node, PaymentForwarded ) ;
3292+ expect_channel_pending_event ! ( client_node, service_node. node_id( ) ) ;
3293+ expect_channel_ready_event ! ( client_node, service_node. node_id( ) ) ;
3294+ expect_payment_successful_event ! ( payer_node, Some ( variable_payment_id) , None ) ;
3295+ let variable_fee_msat = variable_amount_msat * channel_opening_fee_ppm as u64 / 1_000_000 ;
3296+ let variable_received_msat = variable_amount_msat - variable_fee_msat;
3297+ let variable_receiver_payment_id =
3298+ expect_payment_received_event ! ( client_node, variable_received_msat) . unwrap ( ) ;
3299+ match client_node. payment ( & variable_receiver_payment_id) . unwrap ( ) . kind {
3300+ PaymentKind :: Bolt12Offer { counterparty_skimmed_fee_msat, .. } => {
3301+ assert_eq ! ( counterparty_skimmed_fee_msat, Some ( variable_fee_msat) ) ;
3302+ } ,
3303+ _ => panic ! ( "Unexpected payment kind" ) ,
3304+ }
3305+ }
3306+
31503307#[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
31513308async fn facade_logging ( ) {
31523309 let ( bitcoind, electrsd) = setup_bitcoind_and_electrsd ( ) ;
0 commit comments