@@ -12,32 +12,39 @@ use std::str::FromStr;
1212use std:: time:: { Duration , SystemTime , UNIX_EPOCH } ;
1313
1414use e2e_tests:: {
15- close_channel, find_available_port, list_payments, mine_and_sync, run_cli, run_cli_raw,
16- run_cli_with_config, send_bolt11_payment, setup_funded_channel, wait_for_channels,
17- wait_for_event, wait_for_gossip, wait_for_onchain_balance, wait_for_settled_balance,
18- wait_for_usable_channel, LdkServerConfig , LdkServerHandle , TestBitcoind , TestConfigBuilder ,
15+ assert_replacement, close_channel, find_available_port, list_payments, mine_and_sync,
16+ payment_for_tx, run_cli, run_cli_raw, run_cli_with_config, send_bolt11_payment,
17+ setup_funded_channel, wait_for_channels, wait_for_event, wait_for_gossip,
18+ wait_for_onchain_balance, wait_for_settled_balance, wait_for_transaction,
19+ wait_for_usable_channel, wait_for_wallet_sync, LdkServerConfig , LdkServerHandle , TestBitcoind ,
20+ TestConfigBuilder ,
1921} ;
2022use hex_conservative:: { DisplayHex , FromHex } ;
2123use ldk_node:: bitcoin:: hashes:: { sha256, Hash } ;
24+ use ldk_node:: bitcoin:: Amount ;
2225use ldk_node:: lightning:: ln:: msgs:: SocketAddress ;
2326use ldk_node:: lightning:: offers:: offer:: Offer ;
2427use ldk_node:: lightning:: offers:: refund:: Refund ;
2528use ldk_node:: lightning_invoice:: Bolt11Invoice ;
2629use ldk_server_client:: error:: LdkServerErrorCode :: InvalidRequestError ;
2730use ldk_server_client:: ldk_server_grpc:: api:: {
28- open_channel_request, Bolt11ClaimForIdRequest , Bolt11FailForIdRequest , Bolt11ReceiveRequest ,
29- Bolt12ReceiveRequest , GetChannelForwardingStatsRequest , GetForwardedPaymentDetailsRequest ,
31+ onchain_send_request, open_channel_request, Bolt11ClaimForIdRequest , Bolt11FailForIdRequest ,
32+ Bolt11ReceiveRequest , Bolt11SendRequest , Bolt12ReceiveRequest ,
33+ GetChannelForwardingStatsRequest , GetForwardedPaymentDetailsRequest , GetPaymentDetailsRequest ,
3034 ListChannelForwardingStatsRequest , ListChannelPairForwardingStatsRequest ,
31- ListForwardedPaymentsRequest , OnchainReceiveRequest , OpenChannelRequest ,
35+ ListForwardedPaymentsRequest , OnchainBumpFeeRequest , OnchainReceiveRequest , OnchainSendRequest ,
36+ OpenChannelRequest ,
3237} ;
3338use ldk_server_client:: ldk_server_grpc:: events:: event_envelope:: Event ;
3439use ldk_server_client:: ldk_server_grpc:: events:: {
3540 ChannelClosureInitiator , ChannelState , ChannelStateChangeReasonKind , PaymentFailureReason ,
3641} ;
3742use ldk_server_client:: ldk_server_grpc:: types:: {
38- bolt11_invoice_description, Bolt11InvoiceDescription , ChannelShutdownState , ReserveType ,
43+ bolt11_invoice_description, Bolt11InvoiceDescription , ChannelShutdownState , PaymentDirection ,
44+ ReserveType ,
3945} ;
4046use ldk_server_grpc:: types:: payment_kind;
47+ use serde_json:: json;
4148
4249#[ tokio:: test]
4350async fn test_cli_get_node_info ( ) {
@@ -411,6 +418,20 @@ async fn test_cli_onchain_send() {
411418 let recv_output = run_cli ( & server, & [ "onchain-receive" ] ) ;
412419 let dest_addr = recv_output[ "address" ] . as_str ( ) . unwrap ( ) ;
413420
421+ for rate in [ 0 , u64:: MAX / 250 + 1 , u64:: MAX ] {
422+ let error = server
423+ . client ( )
424+ . onchain_send ( OnchainSendRequest {
425+ address : dest_addr. into ( ) ,
426+ amount : Some ( onchain_send_request:: Amount :: AmountSats ( 50_000 ) ) ,
427+ fee_rate_sat_per_vb : Some ( rate) ,
428+ } )
429+ . await
430+ . unwrap_err ( ) ;
431+ assert_eq ! ( error. error_code, InvalidRequestError ) ;
432+ assert_eq ! ( error. message, ldk_node:: NodeError :: InvalidFeeRate . to_string( ) ) ;
433+ }
434+
414435 let output = run_cli ( & server, & [ "onchain-send" , dest_addr, "50000sat" ] ) ;
415436 assert ! ( !output[ "txid" ] . as_str( ) . unwrap( ) . is_empty( ) ) ;
416437}
@@ -435,6 +456,176 @@ async fn test_cli_onchain_send_all() {
435456 wait_for_settled_balance ( & server, 0 , Duration :: from_secs ( 30 ) ) . await ;
436457}
437458
459+ #[ tokio:: test]
460+ async fn test_onchain_fee_bump_client_cli ( ) {
461+ let bitcoind = TestBitcoind :: new ( ) ;
462+ let server = LdkServerHandle :: start ( & bitcoind) . await ;
463+ let address = server. client ( ) . onchain_receive ( OnchainReceiveRequest { } ) . await . unwrap ( ) . address ;
464+ bitcoind. fund_address ( & address, 1.0 ) ;
465+ mine_and_sync ( & bitcoind, & [ & server] , 6 ) . await ;
466+ wait_for_onchain_balance ( server. client ( ) , Duration :: from_secs ( 30 ) ) . await ;
467+ let destination = bitcoind. bitcoind . client . new_address ( ) . unwrap ( ) . to_string ( ) ;
468+ let amount = Amount :: from_sat ( 50_000 ) ;
469+ let original = server
470+ . client ( )
471+ . onchain_send ( OnchainSendRequest {
472+ address : destination. clone ( ) ,
473+ amount : Some ( onchain_send_request:: Amount :: AmountSats ( amount. to_sat ( ) ) ) ,
474+ fee_rate_sat_per_vb : Some ( 2 ) ,
475+ } )
476+ . await
477+ . unwrap ( )
478+ . txid ;
479+ wait_for_transaction ( & bitcoind, & original) . await ;
480+ let payment = payment_for_tx ( & server, & original) . await ;
481+ assert_ne ! ( payment. payment_id, original, "payment IDs use a different byte order" ) ;
482+
483+ // A rate below the original must fail instead of selecting an automatic rate.
484+ let error = server
485+ . client ( )
486+ . onchain_bump_fee ( OnchainBumpFeeRequest {
487+ payment_id : payment. payment_id . clone ( ) ,
488+ fee_rate_sat_per_vb : Some ( 1 ) ,
489+ } )
490+ . await
491+ . unwrap_err ( ) ;
492+ assert_eq ! ( error. error_code, InvalidRequestError ) ;
493+
494+ let replacement = server
495+ . client ( )
496+ . onchain_bump_fee ( OnchainBumpFeeRequest {
497+ payment_id : payment. payment_id . clone ( ) ,
498+ fee_rate_sat_per_vb : Some ( 5 ) ,
499+ } )
500+ . await
501+ . unwrap ( )
502+ . txid ;
503+ assert_replacement ( & bitcoind, & original, & replacement, & destination, amount) . await ;
504+ wait_for_wallet_sync ( & server) . await ;
505+ let cli =
506+ run_cli ( & server, & [ "onchain-bump-fee" , & payment. payment_id , "--fee-rate-sat-per-vb" , "10" ] ) ;
507+ let cli_txid = cli[ "txid" ] . as_str ( ) . unwrap ( ) ;
508+ assert_replacement ( & bitcoind, & replacement, cli_txid, & destination, amount) . await ;
509+ let updated = payment_for_tx ( & server, cli_txid) . await ;
510+ assert_eq ! ( updated. payment_id, payment. payment_id) ;
511+ assert_eq ! ( updated. amount_msat, Some ( amount. to_sat( ) * 1000 ) ) ;
512+ assert ! ( updated. fee_paid_msat > payment. fee_paid_msat) ;
513+
514+ mine_and_sync ( & bitcoind, & [ & server] , 6 ) . await ;
515+ // Wait for wallet confirmation, which can follow the Lightning tip update.
516+ tokio:: time:: timeout ( Duration :: from_secs ( 30 ) , async {
517+ loop {
518+ let details = server
519+ . client ( )
520+ . get_payment_details ( GetPaymentDetailsRequest {
521+ payment_id : payment. payment_id . clone ( ) ,
522+ } )
523+ . await
524+ . unwrap ( )
525+ . payment
526+ . unwrap ( ) ;
527+ if let Some ( payment_kind:: Kind :: Onchain ( onchain) ) =
528+ details. kind . and_then ( |kind| kind. kind )
529+ {
530+ if matches ! (
531+ onchain. status. and_then( |status| status. status) ,
532+ Some ( ldk_server_grpc:: types:: confirmation_status:: Status :: Confirmed ( _) )
533+ ) {
534+ break ;
535+ }
536+ }
537+ tokio:: time:: sleep ( Duration :: from_millis ( 100 ) ) . await ;
538+ }
539+ } )
540+ . await
541+ . unwrap ( ) ;
542+ let error = server
543+ . client ( )
544+ . onchain_bump_fee ( OnchainBumpFeeRequest {
545+ payment_id : payment. payment_id ,
546+ fee_rate_sat_per_vb : Some ( 20 ) ,
547+ } )
548+ . await
549+ . unwrap_err ( ) ;
550+ assert_eq ! ( error. error_code, InvalidRequestError ) ;
551+ }
552+
553+ #[ tokio:: test]
554+ async fn test_onchain_fee_bump_invalid_requests_and_ineligible_payments ( ) {
555+ let bitcoind = TestBitcoind :: new ( ) ;
556+ let server = LdkServerHandle :: start ( & bitcoind) . await ;
557+ for id in [ String :: new ( ) , "ab" . repeat ( 31 ) , "ab" . repeat ( 33 ) , "zz" . repeat ( 32 ) , "00" . repeat ( 32 ) ] {
558+ let error = server
559+ . client ( )
560+ . onchain_bump_fee ( OnchainBumpFeeRequest { payment_id : id, fee_rate_sat_per_vb : None } )
561+ . await
562+ . unwrap_err ( ) ;
563+ assert_eq ! ( error. error_code, InvalidRequestError ) ;
564+ }
565+ for rate in [ 0 , u64:: MAX / 250 + 1 , u64:: MAX ] {
566+ let error = server
567+ . client ( )
568+ . onchain_bump_fee ( OnchainBumpFeeRequest {
569+ payment_id : "00" . repeat ( 32 ) ,
570+ fee_rate_sat_per_vb : Some ( rate) ,
571+ } )
572+ . await
573+ . unwrap_err ( ) ;
574+ assert_eq ! ( error. error_code, InvalidRequestError ) ;
575+ assert_eq ! ( error. message, ldk_node:: NodeError :: InvalidFeeRate . to_string( ) ) ;
576+ }
577+
578+ let peer = LdkServerHandle :: start ( & bitcoind) . await ;
579+ let invoice = peer
580+ . client ( )
581+ . bolt11_receive ( Bolt11ReceiveRequest {
582+ amount_msat : Some ( 100_000 ) ,
583+ expiry_secs : 3600 ,
584+ ..Default :: default ( )
585+ } )
586+ . await
587+ . unwrap ( )
588+ . invoice ;
589+ // A failed send with no route still records an outbound Lightning payment.
590+ let _ = server. client ( ) . bolt11_send ( Bolt11SendRequest { invoice, ..Default :: default ( ) } ) . await ;
591+
592+ let payments = list_payments ( & server) . await ;
593+ let lightning = payments
594+ . iter ( )
595+ . find ( |p| {
596+ matches ! (
597+ p. kind. as_ref( ) . and_then( |k| k. kind. as_ref( ) ) ,
598+ Some ( payment_kind:: Kind :: Bolt11 ( _) )
599+ )
600+ } )
601+ . unwrap ( ) ;
602+ let error = server
603+ . client ( )
604+ . onchain_bump_fee ( OnchainBumpFeeRequest {
605+ payment_id : lightning. payment_id . clone ( ) ,
606+ fee_rate_sat_per_vb : None ,
607+ } )
608+ . await
609+ . unwrap_err ( ) ;
610+ assert_eq ! ( error. error_code, InvalidRequestError ) ;
611+
612+ let address = server. client ( ) . onchain_receive ( OnchainReceiveRequest { } ) . await . unwrap ( ) . address ;
613+ // Leave the incoming payment unconfirmed to exercise the direction guard.
614+ let txid: String =
615+ bitcoind. bitcoind . client . call ( "sendtoaddress" , & [ json ! ( address) , json ! ( 0.1 ) ] ) . unwrap ( ) ;
616+ let incoming = payment_for_tx ( & server, & txid) . await ;
617+ assert_eq ! ( incoming. direction, PaymentDirection :: Inbound as i32 ) ;
618+ let error = server
619+ . client ( )
620+ . onchain_bump_fee ( OnchainBumpFeeRequest {
621+ payment_id : incoming. payment_id ,
622+ fee_rate_sat_per_vb : None ,
623+ } )
624+ . await
625+ . unwrap_err ( ) ;
626+ assert_eq ! ( error. error_code, InvalidRequestError ) ;
627+ }
628+
438629#[ tokio:: test]
439630async fn test_cli_connect_peer ( ) {
440631 let bitcoind = TestBitcoind :: new ( ) ;
0 commit comments