@@ -196,6 +196,29 @@ where
196
196
197
197
/// Pays the given [`Invoice`], caching it for later use in case a retry is needed.
198
198
pub fn pay_invoice ( & self , invoice : & Invoice ) -> Result < PaymentId , PaymentError > {
199
+ if invoice. amount_milli_satoshis ( ) . is_none ( ) {
200
+ Err ( PaymentError :: Invoice ( "amount missing" ) )
201
+ } else {
202
+ self . pay_invoice_internal ( invoice, None )
203
+ }
204
+ }
205
+
206
+ /// Pays the given zero-value [`Invoice`] using the given amount, caching it for later use in
207
+ /// case a retry is needed.
208
+ pub fn pay_zero_value_invoice (
209
+ & self , invoice : & Invoice , amount_msats : u64
210
+ ) -> Result < PaymentId , PaymentError > {
211
+ if invoice. amount_milli_satoshis ( ) . is_some ( ) {
212
+ Err ( PaymentError :: Invoice ( "amount unexpected" ) )
213
+ } else {
214
+ self . pay_invoice_internal ( invoice, Some ( amount_msats) )
215
+ }
216
+ }
217
+
218
+ fn pay_invoice_internal (
219
+ & self , invoice : & Invoice , amount_msats : Option < u64 >
220
+ ) -> Result < PaymentId , PaymentError > {
221
+ debug_assert ! ( invoice. amount_milli_satoshis( ) . is_some( ) ^ amount_msats. is_some( ) ) ;
199
222
let payment_hash = PaymentHash ( invoice. payment_hash ( ) . clone ( ) . into_inner ( ) ) ;
200
223
let mut payment_cache = self . payment_cache . lock ( ) . unwrap ( ) ;
201
224
match payment_cache. entry ( payment_hash) {
@@ -206,11 +229,9 @@ where
206
229
if let Some ( features) = invoice. features ( ) {
207
230
payee = payee. with_features ( features. clone ( ) ) ;
208
231
}
209
- let final_value_msat = invoice. amount_milli_satoshis ( )
210
- . ok_or ( PaymentError :: Invoice ( "amount missing" ) ) ?;
211
232
let params = RouteParameters {
212
233
payee,
213
- final_value_msat,
234
+ final_value_msat : invoice . amount_milli_satoshis ( ) . or ( amount_msats ) . unwrap ( ) ,
214
235
final_cltv_expiry_delta : invoice. min_final_cltv_expiry ( ) as u32 ,
215
236
} ;
216
237
let first_hops = self . payer . first_hops ( ) ;
@@ -334,6 +355,21 @@ mod tests {
334
355
. unwrap ( )
335
356
}
336
357
358
+ fn zero_value_invoice ( payment_preimage : PaymentPreimage ) -> Invoice {
359
+ let payment_hash = Sha256 :: hash ( & payment_preimage. 0 ) ;
360
+ let private_key = SecretKey :: from_slice ( & [ 42 ; 32 ] ) . unwrap ( ) ;
361
+ InvoiceBuilder :: new ( Currency :: Bitcoin )
362
+ . description ( "test" . into ( ) )
363
+ . payment_hash ( payment_hash)
364
+ . payment_secret ( PaymentSecret ( [ 0 ; 32 ] ) )
365
+ . current_timestamp ( )
366
+ . min_final_cltv_expiry ( 144 )
367
+ . build_signed ( |hash| {
368
+ Secp256k1 :: new ( ) . sign_recoverable ( hash, & private_key)
369
+ } )
370
+ . unwrap ( )
371
+ }
372
+
337
373
#[ test]
338
374
fn pays_invoice_on_first_attempt ( ) {
339
375
let event_handled = core:: cell:: RefCell :: new ( false ) ;
@@ -631,6 +667,55 @@ mod tests {
631
667
}
632
668
}
633
669
670
+ #[ test]
671
+ fn pays_zero_value_invoice_using_amount ( ) {
672
+ let event_handled = core:: cell:: RefCell :: new ( false ) ;
673
+ let event_handler = |_: & _ | { * event_handled. borrow_mut ( ) = true ; } ;
674
+
675
+ let payment_preimage = PaymentPreimage ( [ 1 ; 32 ] ) ;
676
+ let invoice = zero_value_invoice ( payment_preimage) ;
677
+ let payment_hash = PaymentHash ( invoice. payment_hash ( ) . clone ( ) . into_inner ( ) ) ;
678
+ let final_value_msat = 100 ;
679
+
680
+ let payer = TestPayer :: new ( ) . expect_value_msat ( final_value_msat) ;
681
+ let router = TestRouter { } ;
682
+ let logger = TestLogger :: new ( ) ;
683
+ let invoice_payer =
684
+ InvoicePayer :: new ( & payer, router, & logger, event_handler, RetryAttempts ( 0 ) ) ;
685
+
686
+ let payment_id =
687
+ Some ( invoice_payer. pay_zero_value_invoice ( & invoice, final_value_msat) . unwrap ( ) ) ;
688
+ assert_eq ! ( * payer. attempts. borrow( ) , 1 ) ;
689
+
690
+ invoice_payer. handle_event ( & Event :: PaymentSent {
691
+ payment_id, payment_preimage, payment_hash
692
+ } ) ;
693
+ assert_eq ! ( * event_handled. borrow( ) , true ) ;
694
+ assert_eq ! ( * payer. attempts. borrow( ) , 1 ) ;
695
+ }
696
+
697
+ #[ test]
698
+ fn fails_paying_zero_value_invoice_with_amount ( ) {
699
+ let event_handled = core:: cell:: RefCell :: new ( false ) ;
700
+ let event_handler = |_: & _ | { * event_handled. borrow_mut ( ) = true ; } ;
701
+
702
+ let payer = TestPayer :: new ( ) ;
703
+ let router = TestRouter { } ;
704
+ let logger = TestLogger :: new ( ) ;
705
+ let invoice_payer =
706
+ InvoicePayer :: new ( & payer, router, & logger, event_handler, RetryAttempts ( 0 ) ) ;
707
+
708
+ let payment_preimage = PaymentPreimage ( [ 1 ; 32 ] ) ;
709
+ let invoice = invoice ( payment_preimage) ;
710
+
711
+ // Cannot repay an invoice pending payment.
712
+ match invoice_payer. pay_zero_value_invoice ( & invoice, 100 ) {
713
+ Err ( PaymentError :: Invoice ( "amount unexpected" ) ) => { } ,
714
+ Err ( _) => panic ! ( "unexpected error" ) ,
715
+ Ok ( _) => panic ! ( "expected invoice error" ) ,
716
+ }
717
+ }
718
+
634
719
struct TestRouter ;
635
720
636
721
impl TestRouter {
0 commit comments