@@ -50,6 +50,7 @@ use crate::payment::asynchronous::static_invoice_store::StaticInvoiceStore;
5050use crate :: payment:: store:: {
5151 PaymentDetails , PaymentDetailsUpdate , PaymentDirection , PaymentKind , PaymentStatus ,
5252} ;
53+ use crate :: payment:: PaymentMetadata ;
5354use crate :: runtime:: Runtime ;
5455use crate :: types:: {
5556 CustomTlvRecord , DynStore , KeysManager , OnionMessenger , PaymentStore , Sweeper , Wallet ,
@@ -580,6 +581,36 @@ where
580581 }
581582 }
582583
584+ fn fail_claimable_payment (
585+ & self , payment_id : PaymentId , payment_hash : & PaymentHash ,
586+ ) -> Result < ( ) , ReplayEvent > {
587+ self . channel_manager . fail_htlc_backwards ( payment_hash) ;
588+
589+ let update = PaymentDetailsUpdate {
590+ hash : Some ( Some ( * payment_hash) ) ,
591+ status : Some ( PaymentStatus :: Failed ) ,
592+ ..PaymentDetailsUpdate :: new ( payment_id)
593+ } ;
594+ match self . payment_store . update ( update) {
595+ Ok ( _) => Ok ( ( ) ) ,
596+ Err ( e) => {
597+ log_error ! ( self . logger, "Failed to access payment store: {}" , e) ;
598+ Err ( ReplayEvent ( ) )
599+ } ,
600+ }
601+ }
602+
603+ fn lsps2_max_total_opening_fee_msat ( payment_metadata : & [ u8 ] , amount_msat : u64 ) -> Option < u64 > {
604+ let metadata = PaymentMetadata :: read ( & mut & payment_metadata[ ..] ) . ok ( ) ?;
605+ let lsps2_parameters = metadata. lsps2_parameters ?;
606+ lsps2_parameters. max_total_opening_fee_msat . or_else ( || {
607+ lsps2_parameters. max_proportional_opening_fee_ppm_msat . and_then ( |max_prop_fee| {
608+ // If it's a variable amount payment, compute the actual fee.
609+ compute_opening_fee ( amount_msat, 0 , max_prop_fee)
610+ } )
611+ } )
612+ }
613+
583614 pub async fn handle_event ( & self , event : LdkEvent ) -> Result < ( ) , ReplayEvent > {
584615 match event {
585616 LdkEvent :: FundingGenerationReady {
@@ -693,7 +724,8 @@ where
693724 ..
694725 } => {
695726 let payment_id = PaymentId ( payment_hash. 0 ) ;
696- if let Some ( info) = self . payment_store . get ( & payment_id) {
727+ let payment_info = self . payment_store . get ( & payment_id) ;
728+ if let Some ( info) = payment_info. as_ref ( ) {
697729 if info. direction == PaymentDirection :: Outbound {
698730 log_info ! (
699731 self . logger,
@@ -716,14 +748,13 @@ where
716748 }
717749
718750 if info. status == PaymentStatus :: Succeeded
719- || matches ! ( info. kind, PaymentKind :: Spontaneous { .. } )
751+ || matches ! ( & info. kind, PaymentKind :: Spontaneous { .. } )
720752 {
721- let stored_preimage = match info. kind {
753+ let stored_preimage = match & info. kind {
722754 PaymentKind :: Bolt11 { preimage, .. }
723- | PaymentKind :: Bolt11Jit { preimage, .. }
724755 | PaymentKind :: Bolt12Offer { preimage, .. }
725756 | PaymentKind :: Bolt12Refund { preimage, .. }
726- | PaymentKind :: Spontaneous { preimage, .. } => preimage,
757+ | PaymentKind :: Spontaneous { preimage, .. } => * preimage,
727758 _ => None ,
728759 } ;
729760
@@ -758,22 +789,28 @@ where
758789 } ,
759790 } ;
760791 }
792+ }
761793
762- let max_total_opening_fee_msat = match info. kind {
763- PaymentKind :: Bolt11Jit { lsp_fee_limits, .. } => {
764- lsp_fee_limits
765- . max_total_opening_fee_msat
766- . or_else ( || {
767- lsp_fee_limits. max_proportional_opening_fee_ppm_msat . and_then (
768- |max_prop_fee| {
769- // If it's a variable amount payment, compute the actual fee.
770- compute_opening_fee ( amount_msat, 0 , max_prop_fee)
771- } ,
772- )
773- } )
774- . unwrap_or ( 0 )
775- } ,
776- _ => 0 ,
794+ if counterparty_skimmed_fee_msat > 0 {
795+ let max_total_opening_fee_msat = match & purpose {
796+ PaymentPurpose :: Bolt11InvoicePayment { .. } => onion_fields
797+ . as_ref ( )
798+ . and_then ( |fields| fields. payment_metadata . as_ref ( ) )
799+ . and_then ( |metadata| {
800+ Self :: lsps2_max_total_opening_fee_msat ( metadata, amount_msat)
801+ } ) ,
802+ _ => None ,
803+ } ;
804+
805+ let Some ( max_total_opening_fee_msat) = max_total_opening_fee_msat else {
806+ log_info ! (
807+ self . logger,
808+ "Refusing inbound payment with hash {} as the counterparty withheld {}msat without valid BOLT11 LSPS2 payment metadata" ,
809+ hex_utils:: to_string( & payment_hash. 0 ) ,
810+ counterparty_skimmed_fee_msat,
811+ ) ;
812+ self . fail_claimable_payment ( payment_id, & payment_hash) ?;
813+ return Ok ( ( ) ) ;
777814 } ;
778815
779816 if counterparty_skimmed_fee_msat > max_total_opening_fee_msat {
@@ -784,26 +821,13 @@ where
784821 counterparty_skimmed_fee_msat,
785822 max_total_opening_fee_msat,
786823 ) ;
787- self . channel_manager . fail_htlc_backwards ( & payment_hash) ;
788-
789- let update = PaymentDetailsUpdate {
790- hash : Some ( Some ( payment_hash) ) ,
791- status : Some ( PaymentStatus :: Failed ) ,
792- ..PaymentDetailsUpdate :: new ( payment_id)
793- } ;
794- match self . payment_store . update ( update) {
795- Ok ( _) => return Ok ( ( ) ) ,
796- Err ( e) => {
797- log_error ! ( self . logger, "Failed to access payment store: {}" , e) ;
798- return Err ( ReplayEvent ( ) ) ;
799- } ,
800- } ;
824+ self . fail_claimable_payment ( payment_id, & payment_hash) ?;
825+ return Ok ( ( ) ) ;
801826 }
802827
803- // If the LSP skimmed anything, update our stored payment.
804- if counterparty_skimmed_fee_msat > 0 {
805- match info. kind {
806- PaymentKind :: Bolt11Jit { .. } => {
828+ if let Some ( info) = payment_info. as_ref ( ) {
829+ match & info. kind {
830+ PaymentKind :: Bolt11 { .. } => {
807831 let update = PaymentDetailsUpdate {
808832 counterparty_skimmed_fee_msat : Some ( Some ( counterparty_skimmed_fee_msat) ) ,
809833 ..PaymentDetailsUpdate :: new ( payment_id)
@@ -816,16 +840,17 @@ where
816840 } ,
817841 } ;
818842 }
819- _ => debug_assert ! ( false , "We only expect the counterparty to get away with withholding fees for JIT payments." ) ,
843+ _ => debug_assert ! ( false , "We only expect the counterparty to get away with withholding fees for BOLT11 payments." ) ,
820844 }
821845 }
846+ }
822847
848+ if let Some ( info) = payment_info {
823849 // If this is known by the store but ChannelManager doesn't know the preimage,
824850 // the payment has been registered via `_for_hash` variants and needs to be manually claimed via
825851 // user interaction.
826852 match info. kind {
827- PaymentKind :: Bolt11 { preimage, .. }
828- | PaymentKind :: Bolt11Jit { preimage, .. } => {
853+ PaymentKind :: Bolt11 { preimage, .. } => {
829854 if purpose. preimage ( ) . is_none ( ) {
830855 debug_assert ! (
831856 preimage. is_none( ) ,
@@ -1890,8 +1915,58 @@ mod tests {
18901915
18911916 use super :: * ;
18921917 use crate :: io:: test_utils:: InMemoryStore ;
1918+ use crate :: payment:: store:: LSPS2Parameters ;
18931919 use crate :: types:: DynStoreWrapper ;
18941920
1921+ #[ test]
1922+ fn lsps2_payment_metadata_decodes_total_fee_limit ( ) {
1923+ let metadata = PaymentMetadata {
1924+ lsps2_parameters : Some ( LSPS2Parameters {
1925+ max_total_opening_fee_msat : Some ( 42_000 ) ,
1926+ max_proportional_opening_fee_ppm_msat : None ,
1927+ } ) ,
1928+ } ;
1929+
1930+ assert_eq ! (
1931+ EventHandler :: <Arc <TestLogger >>:: lsps2_max_total_opening_fee_msat(
1932+ & metadata. encode( ) ,
1933+ 100_000
1934+ ) ,
1935+ Some ( 42_000 )
1936+ ) ;
1937+ }
1938+
1939+ #[ test]
1940+ fn lsps2_payment_metadata_missing_or_malformed_limit_is_rejected ( ) {
1941+ let empty_metadata = PaymentMetadata { lsps2_parameters : None } . encode ( ) ;
1942+ let metadata_without_fee_limit = PaymentMetadata {
1943+ lsps2_parameters : Some ( LSPS2Parameters {
1944+ max_total_opening_fee_msat : None ,
1945+ max_proportional_opening_fee_ppm_msat : None ,
1946+ } ) ,
1947+ }
1948+ . encode ( ) ;
1949+
1950+ assert_eq ! (
1951+ EventHandler :: <Arc <TestLogger >>:: lsps2_max_total_opening_fee_msat(
1952+ & empty_metadata,
1953+ 100_000
1954+ ) ,
1955+ None
1956+ ) ;
1957+ assert_eq ! (
1958+ EventHandler :: <Arc <TestLogger >>:: lsps2_max_total_opening_fee_msat( & [ 0xff ] , 100_000 ) ,
1959+ None
1960+ ) ;
1961+ assert_eq ! (
1962+ EventHandler :: <Arc <TestLogger >>:: lsps2_max_total_opening_fee_msat(
1963+ & metadata_without_fee_limit,
1964+ 100_000
1965+ ) ,
1966+ None
1967+ ) ;
1968+ }
1969+
18951970 #[ tokio:: test]
18961971 async fn event_queue_persistence ( ) {
18971972 let store: Arc < DynStore > = Arc :: new ( DynStoreWrapper ( InMemoryStore :: new ( ) ) ) ;
0 commit comments