@@ -197,6 +197,29 @@ where
197
197
198
198
/// Pays the given [`Invoice`], caching it for later use in case a retry is needed.
199
199
pub fn pay_invoice ( & self , invoice : & Invoice ) -> Result < PaymentId , PaymentError > {
200
+ if invoice. amount_milli_satoshis ( ) . is_none ( ) {
201
+ Err ( PaymentError :: Invoice ( "amount missing" ) )
202
+ } else {
203
+ self . pay_invoice_internal ( invoice, None )
204
+ }
205
+ }
206
+
207
+ /// Pays the given zero-value [`Invoice`] using the given amount, caching it for later use in
208
+ /// case a retry is needed.
209
+ pub fn pay_zero_value_invoice (
210
+ & self , invoice : & Invoice , amount_msats : u64
211
+ ) -> Result < PaymentId , PaymentError > {
212
+ if invoice. amount_milli_satoshis ( ) . is_some ( ) {
213
+ Err ( PaymentError :: Invoice ( "amount unexpected" ) )
214
+ } else {
215
+ self . pay_invoice_internal ( invoice, Some ( amount_msats) )
216
+ }
217
+ }
218
+
219
+ fn pay_invoice_internal (
220
+ & self , invoice : & Invoice , amount_msats : Option < u64 >
221
+ ) -> Result < PaymentId , PaymentError > {
222
+ debug_assert ! ( invoice. amount_milli_satoshis( ) . is_some( ) ^ amount_msats. is_some( ) ) ;
200
223
let payment_hash = PaymentHash ( invoice. payment_hash ( ) . clone ( ) . into_inner ( ) ) ;
201
224
let mut payment_cache = self . payment_cache . lock ( ) . unwrap ( ) ;
202
225
match payment_cache. entry ( payment_hash) {
@@ -207,11 +230,9 @@ where
207
230
if let Some ( features) = invoice. features ( ) {
208
231
payee = payee. with_features ( features. clone ( ) ) ;
209
232
}
210
- let final_value_msat = invoice. amount_milli_satoshis ( )
211
- . ok_or ( PaymentError :: Invoice ( "amount missing" ) ) ?;
212
233
let params = RouteParameters {
213
234
payee,
214
- final_value_msat,
235
+ final_value_msat : invoice . amount_milli_satoshis ( ) . or ( amount_msats ) . unwrap ( ) ,
215
236
final_cltv_expiry_delta : invoice. min_final_cltv_expiry ( ) as u32 ,
216
237
} ;
217
238
let first_hops = self . payer . first_hops ( ) ;
@@ -342,6 +363,21 @@ mod tests {
342
363
. unwrap ( )
343
364
}
344
365
366
+ fn zero_value_invoice ( payment_preimage : PaymentPreimage ) -> Invoice {
367
+ let payment_hash = Sha256 :: hash ( & payment_preimage. 0 ) ;
368
+ let private_key = SecretKey :: from_slice ( & [ 42 ; 32 ] ) . unwrap ( ) ;
369
+ InvoiceBuilder :: new ( Currency :: Bitcoin )
370
+ . description ( "test" . into ( ) )
371
+ . payment_hash ( payment_hash)
372
+ . payment_secret ( PaymentSecret ( [ 0 ; 32 ] ) )
373
+ . current_timestamp ( )
374
+ . min_final_cltv_expiry ( 144 )
375
+ . build_signed ( |hash| {
376
+ Secp256k1 :: new ( ) . sign_recoverable ( hash, & private_key)
377
+ } )
378
+ . unwrap ( )
379
+ }
380
+
345
381
#[ test]
346
382
fn pays_invoice_on_first_attempt ( ) {
347
383
let event_handled = core:: cell:: RefCell :: new ( false ) ;
@@ -681,6 +717,55 @@ mod tests {
681
717
}
682
718
}
683
719
720
+ #[ test]
721
+ fn pays_zero_value_invoice_using_amount ( ) {
722
+ let event_handled = core:: cell:: RefCell :: new ( false ) ;
723
+ let event_handler = |_: & _ | { * event_handled. borrow_mut ( ) = true ; } ;
724
+
725
+ let payment_preimage = PaymentPreimage ( [ 1 ; 32 ] ) ;
726
+ let invoice = zero_value_invoice ( payment_preimage) ;
727
+ let payment_hash = PaymentHash ( invoice. payment_hash ( ) . clone ( ) . into_inner ( ) ) ;
728
+ let final_value_msat = 100 ;
729
+
730
+ let payer = TestPayer :: new ( ) . expect_value_msat ( final_value_msat) ;
731
+ let router = TestRouter { } ;
732
+ let logger = TestLogger :: new ( ) ;
733
+ let invoice_payer =
734
+ InvoicePayer :: new ( & payer, router, & logger, event_handler, RetryAttempts ( 0 ) ) ;
735
+
736
+ let payment_id =
737
+ Some ( invoice_payer. pay_zero_value_invoice ( & invoice, final_value_msat) . unwrap ( ) ) ;
738
+ assert_eq ! ( * payer. attempts. borrow( ) , 1 ) ;
739
+
740
+ invoice_payer. handle_event ( & Event :: PaymentSent {
741
+ payment_id, payment_preimage, payment_hash
742
+ } ) ;
743
+ assert_eq ! ( * event_handled. borrow( ) , true ) ;
744
+ assert_eq ! ( * payer. attempts. borrow( ) , 1 ) ;
745
+ }
746
+
747
+ #[ test]
748
+ fn fails_paying_zero_value_invoice_with_amount ( ) {
749
+ let event_handled = core:: cell:: RefCell :: new ( false ) ;
750
+ let event_handler = |_: & _ | { * event_handled. borrow_mut ( ) = true ; } ;
751
+
752
+ let payer = TestPayer :: new ( ) ;
753
+ let router = TestRouter { } ;
754
+ let logger = TestLogger :: new ( ) ;
755
+ let invoice_payer =
756
+ InvoicePayer :: new ( & payer, router, & logger, event_handler, RetryAttempts ( 0 ) ) ;
757
+
758
+ let payment_preimage = PaymentPreimage ( [ 1 ; 32 ] ) ;
759
+ let invoice = invoice ( payment_preimage) ;
760
+
761
+ // Cannot repay an invoice pending payment.
762
+ match invoice_payer. pay_zero_value_invoice ( & invoice, 100 ) {
763
+ Err ( PaymentError :: Invoice ( "amount unexpected" ) ) => { } ,
764
+ Err ( _) => panic ! ( "unexpected error" ) ,
765
+ Ok ( _) => panic ! ( "expected invoice error" ) ,
766
+ }
767
+ }
768
+
684
769
struct TestRouter ;
685
770
686
771
impl TestRouter {
0 commit comments