@@ -207,6 +207,29 @@ where
207
207
208
208
/// Pays the given [`Invoice`], caching it for later use in case a retry is needed.
209
209
pub fn pay_invoice ( & self , invoice : & Invoice ) -> Result < PaymentId , PaymentError > {
210
+ if invoice. amount_milli_satoshis ( ) . is_none ( ) {
211
+ Err ( PaymentError :: Invoice ( "amount missing" ) )
212
+ } else {
213
+ self . pay_invoice_internal ( invoice, None )
214
+ }
215
+ }
216
+
217
+ /// Pays the given zero-value [`Invoice`] using the given amount, caching it for later use in
218
+ /// case a retry is needed.
219
+ pub fn pay_zero_value_invoice (
220
+ & self , invoice : & Invoice , amount_msats : u64
221
+ ) -> Result < PaymentId , PaymentError > {
222
+ if invoice. amount_milli_satoshis ( ) . is_some ( ) {
223
+ Err ( PaymentError :: Invoice ( "amount unexpected" ) )
224
+ } else {
225
+ self . pay_invoice_internal ( invoice, Some ( amount_msats) )
226
+ }
227
+ }
228
+
229
+ fn pay_invoice_internal (
230
+ & self , invoice : & Invoice , amount_msats : Option < u64 >
231
+ ) -> Result < PaymentId , PaymentError > {
232
+ debug_assert ! ( invoice. amount_milli_satoshis( ) . is_some( ) ^ amount_msats. is_some( ) ) ;
210
233
let payment_hash = PaymentHash ( invoice. payment_hash ( ) . clone ( ) . into_inner ( ) ) ;
211
234
let mut invoice_cache = self . invoice_cache . lock ( ) . unwrap ( ) ;
212
235
match invoice_cache. entry ( payment_hash) {
@@ -216,8 +239,7 @@ where
216
239
let payee_features = invoice. features ( ) . cloned ( ) ;
217
240
let first_hops = self . payer . first_hops ( ) ;
218
241
let last_hops = invoice. route_hints ( ) ;
219
- let final_value_msat = invoice. amount_milli_satoshis ( )
220
- . ok_or ( PaymentError :: Invoice ( "amount missing" ) ) ?;
242
+ let final_value_msat = invoice. amount_milli_satoshis ( ) . or ( amount_msats) . unwrap ( ) ;
221
243
let final_cltv = invoice. min_final_cltv_expiry ( ) as u32 ;
222
244
let route = self . router . find_route (
223
245
& payer,
@@ -353,6 +375,21 @@ mod tests {
353
375
. unwrap ( )
354
376
}
355
377
378
+ fn zero_value_invoice ( payment_preimage : PaymentPreimage ) -> Invoice {
379
+ let payment_hash = Sha256 :: hash ( & payment_preimage. 0 ) ;
380
+ let private_key = SecretKey :: from_slice ( & [ 42 ; 32 ] ) . unwrap ( ) ;
381
+ InvoiceBuilder :: new ( Currency :: Bitcoin )
382
+ . description ( "test" . into ( ) )
383
+ . payment_hash ( payment_hash)
384
+ . payment_secret ( PaymentSecret ( [ 0 ; 32 ] ) )
385
+ . current_timestamp ( )
386
+ . min_final_cltv_expiry ( 144 )
387
+ . build_signed ( |hash| {
388
+ Secp256k1 :: new ( ) . sign_recoverable ( hash, & private_key)
389
+ } )
390
+ . unwrap ( )
391
+ }
392
+
356
393
#[ test]
357
394
fn pays_invoice_on_first_attempt ( ) {
358
395
let event_handled = core:: cell:: RefCell :: new ( false ) ;
@@ -637,6 +674,50 @@ mod tests {
637
674
}
638
675
}
639
676
677
+ #[ test]
678
+ fn pays_zero_value_invoice_using_amount ( ) {
679
+ let event_handled = core:: cell:: RefCell :: new ( false ) ;
680
+ let event_handler = |_: & _ | { * event_handled. borrow_mut ( ) = true ; } ;
681
+
682
+ let payment_preimage = PaymentPreimage ( [ 1 ; 32 ] ) ;
683
+ let invoice = zero_value_invoice ( payment_preimage) ;
684
+ let final_value_msat = 100 ;
685
+
686
+ let payer = TestPayer :: new ( ) . expect_value_msat ( final_value_msat) ;
687
+ let router = TestRouter { } ;
688
+ let logger = TestLogger :: new ( ) ;
689
+ let invoice_payer =
690
+ InvoicePayer :: new ( & payer, router, & logger, event_handler, RetryAttempts ( 0 ) ) ;
691
+ assert ! ( invoice_payer. pay_zero_value_invoice( & invoice, final_value_msat) . is_ok( ) ) ;
692
+ assert_eq ! ( * payer. attempts. borrow( ) , 1 ) ;
693
+
694
+ invoice_payer. handle_event ( & Event :: PaymentSent { payment_preimage } ) ;
695
+ assert_eq ! ( * event_handled. borrow( ) , true ) ;
696
+ assert_eq ! ( * payer. attempts. borrow( ) , 1 ) ;
697
+ }
698
+
699
+ #[ test]
700
+ fn fails_paying_zero_value_invoice_with_amount ( ) {
701
+ let event_handled = core:: cell:: RefCell :: new ( false ) ;
702
+ let event_handler = |_: & _ | { * event_handled. borrow_mut ( ) = true ; } ;
703
+
704
+ let payer = TestPayer :: new ( ) ;
705
+ let router = TestRouter { } ;
706
+ let logger = TestLogger :: new ( ) ;
707
+ let invoice_payer =
708
+ InvoicePayer :: new ( & payer, router, & logger, event_handler, RetryAttempts ( 0 ) ) ;
709
+
710
+ let payment_preimage = PaymentPreimage ( [ 1 ; 32 ] ) ;
711
+ let invoice = invoice ( payment_preimage) ;
712
+
713
+ // Cannot repay an invoice pending payment.
714
+ match invoice_payer. pay_zero_value_invoice ( & invoice, 100 ) {
715
+ Err ( PaymentError :: Invoice ( "amount unexpected" ) ) => { } ,
716
+ Err ( _) => panic ! ( "unexpected error" ) ,
717
+ Ok ( _) => panic ! ( "expected invoice error" ) ,
718
+ }
719
+ }
720
+
640
721
struct TestRouter ;
641
722
642
723
impl TestRouter {
0 commit comments