forked from lightningdevkit/rust-lightning
-
Notifications
You must be signed in to change notification settings - Fork 2
/
payment.rs
2427 lines (2142 loc) · 94.7 KB
/
payment.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// This file is Copyright its original authors, visible in version control
// history.
//
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// You may not use this file except in accordance with one or both of these
// licenses.
//! A module for paying Lightning invoices and sending spontaneous payments.
//!
//! Defines an [`InvoicePayer`] utility for sending payments, parameterized by [`Payer`] and
//! [`Router`] traits. Implementations of [`Payer`] provide the payer's node id, channels, and means
//! to send a payment over a [`Route`]. Implementations of [`Router`] find a [`Route`] between payer
//! and payee using information provided by the payer and from the payee's [`Invoice`], when
//! applicable.
//!
//! [`InvoicePayer`] uses its [`Router`] parameterization for optionally notifying scorers upon
//! receiving the [`Event::PaymentPathFailed`] and [`Event::PaymentPathSuccessful`] events.
//! It also does the same for payment probe failure and success events using [`Event::ProbeFailed`]
//! and [`Event::ProbeSuccessful`].
//!
//! [`InvoicePayer`] is capable of retrying failed payments. It accomplishes this by implementing
//! [`EventHandler`] which decorates a user-provided handler. It will intercept any
//! [`Event::PaymentPathFailed`] events and retry the failed paths for a fixed number of total
//! attempts or until retry is no longer possible. In such a situation, [`InvoicePayer`] will pass
//! along the events to the user-provided handler.
//!
//! # Example
//!
//! ```
//! # extern crate lightning;
//! # extern crate lightning_invoice;
//! # extern crate secp256k1;
//! #
//! # use lightning::io;
//! # use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
//! # use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure};
//! # use lightning::ln::msgs::LightningError;
//! # use lightning::routing::gossip::NodeId;
//! # use lightning::routing::router::{Route, RouteHop, RouteParameters};
//! # use lightning::routing::scoring::{ChannelUsage, Score};
//! # use lightning::util::events::{Event, EventHandler, EventsProvider};
//! # use lightning::util::logger::{Logger, Record};
//! # use lightning::util::ser::{Writeable, Writer};
//! # use lightning_invoice::Invoice;
//! # use lightning_invoice::payment::{InFlightHtlcs, InvoicePayer, Payer, Retry, Router};
//! # use secp256k1::PublicKey;
//! # use std::cell::RefCell;
//! # use std::ops::Deref;
//! #
//! # struct FakeEventProvider {}
//! # impl EventsProvider for FakeEventProvider {
//! # fn process_pending_events<H: Deref>(&self, handler: H) where H::Target: EventHandler {}
//! # }
//! #
//! # struct FakePayer {}
//! # impl Payer for FakePayer {
//! # fn node_id(&self) -> PublicKey { unimplemented!() }
//! # fn first_hops(&self) -> Vec<ChannelDetails> { unimplemented!() }
//! # fn send_payment(
//! # &self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>
//! # ) -> Result<PaymentId, PaymentSendFailure> { unimplemented!() }
//! # fn send_spontaneous_payment(
//! # &self, route: &Route, payment_preimage: PaymentPreimage
//! # ) -> Result<PaymentId, PaymentSendFailure> { unimplemented!() }
//! # fn retry_payment(
//! # &self, route: &Route, payment_id: PaymentId
//! # ) -> Result<(), PaymentSendFailure> { unimplemented!() }
//! # fn abandon_payment(&self, payment_id: PaymentId) { unimplemented!() }
//! # }
//! #
//! # struct FakeRouter {}
//! # impl Router for FakeRouter {
//! # fn find_route(
//! # &self, payer: &PublicKey, params: &RouteParameters, payment_hash: &PaymentHash,
//! # first_hops: Option<&[&ChannelDetails]>, _inflight_htlcs: InFlightHtlcs
//! # ) -> Result<Route, LightningError> { unimplemented!() }
//! #
//! # fn notify_payment_path_failed(&self, path: &[&RouteHop], short_channel_id: u64) { unimplemented!() }
//! # fn notify_payment_path_successful(&self, path: &[&RouteHop]) { unimplemented!() }
//! # fn notify_payment_probe_successful(&self, path: &[&RouteHop]) { unimplemented!() }
//! # fn notify_payment_probe_failed(&self, path: &[&RouteHop], short_channel_id: u64) { unimplemented!() }
//! # }
//! #
//! # struct FakeScorer {}
//! # impl Writeable for FakeScorer {
//! # fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> { unimplemented!(); }
//! # }
//! # impl Score for FakeScorer {
//! # fn channel_penalty_msat(
//! # &self, _short_channel_id: u64, _source: &NodeId, _target: &NodeId, _usage: ChannelUsage
//! # ) -> u64 { 0 }
//! # fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
//! # fn payment_path_successful(&mut self, _path: &[&RouteHop]) {}
//! # fn probe_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
//! # fn probe_successful(&mut self, _path: &[&RouteHop]) {}
//! # }
//! #
//! # struct FakeLogger {}
//! # impl Logger for FakeLogger {
//! # fn log(&self, record: &Record) { unimplemented!() }
//! # }
//! #
//! # fn main() {
//! let event_handler = |event: &Event| {
//! match event {
//! Event::PaymentPathFailed { .. } => println!("payment failed after retries"),
//! Event::PaymentSent { .. } => println!("payment successful"),
//! _ => {},
//! }
//! };
//! # let payer = FakePayer {};
//! # let router = FakeRouter {};
//! # let scorer = RefCell::new(FakeScorer {});
//! # let logger = FakeLogger {};
//! let invoice_payer = InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
//!
//! let invoice = "...";
//! if let Ok(invoice) = invoice.parse::<Invoice>() {
//! invoice_payer.pay_invoice(&invoice).unwrap();
//!
//! # let event_provider = FakeEventProvider {};
//! loop {
//! event_provider.process_pending_events(&invoice_payer);
//! }
//! }
//! # }
//! ```
//!
//! # Note
//!
//! The [`Route`] is computed before each payment attempt. Any updates affecting path finding such
//! as updates to the network graph or changes to channel scores should be applied prior to
//! retries, typically by way of composing [`EventHandler`]s accordingly.
use crate::Invoice;
use bitcoin_hashes::Hash;
use bitcoin_hashes::sha256::Hash as Sha256;
use crate::prelude::*;
use lightning::io;
use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure};
use lightning::ln::msgs::LightningError;
use lightning::routing::gossip::NodeId;
use lightning::routing::router::{PaymentParameters, Route, RouteHop, RouteParameters};
use lightning::util::errors::APIError;
use lightning::util::events::{Event, EventHandler};
use lightning::util::logger::Logger;
use lightning::util::ser::Writeable;
use time_utils::Time;
use crate::sync::Mutex;
use secp256k1::PublicKey;
use core::fmt;
use core::fmt::{Debug, Display, Formatter};
use core::ops::Deref;
use core::time::Duration;
#[cfg(feature = "std")]
use std::time::SystemTime;
/// A utility for paying [`Invoice`]s and sending spontaneous payments.
///
/// See [module-level documentation] for details.
///
/// [module-level documentation]: crate::payment
pub type InvoicePayer<P, R, L, E> = InvoicePayerUsingTime::<P, R, L, E, ConfiguredTime>;
#[cfg(not(feature = "no-std"))]
type ConfiguredTime = std::time::Instant;
#[cfg(feature = "no-std")]
use time_utils;
#[cfg(feature = "no-std")]
type ConfiguredTime = time_utils::Eternity;
/// (C-not exported) generally all users should use the [`InvoicePayer`] type alias.
pub struct InvoicePayerUsingTime<P: Deref, R: Router, L: Deref, E: EventHandler, T: Time>
where
P::Target: Payer,
L::Target: Logger,
{
payer: P,
router: R,
logger: L,
event_handler: E,
/// Caches the overall attempts at making a payment, which is updated prior to retrying.
payment_cache: Mutex<HashMap<PaymentHash, PaymentInfo<T>>>,
retry: Retry,
}
/// Used by [`InvoicePayerUsingTime::payment_cache`] to track the payments that are either
/// currently being made, or have outstanding paths that need retrying.
struct PaymentInfo<T: Time> {
attempts: PaymentAttempts<T>,
paths: Vec<Vec<RouteHop>>,
}
impl<T: Time> PaymentInfo<T> {
fn new() -> Self {
PaymentInfo {
attempts: PaymentAttempts::new(),
paths: vec![],
}
}
}
/// Storing minimal payment attempts information required for determining if a outbound payment can
/// be retried.
#[derive(Clone, Copy)]
struct PaymentAttempts<T: Time> {
/// This count will be incremented only after the result of the attempt is known. When it's 0,
/// it means the result of the first attempt is now known yet.
count: usize,
/// This field is only used when retry is [`Retry::Timeout`] which is only build with feature std
first_attempted_at: T
}
impl<T: Time> PaymentAttempts<T> {
fn new() -> Self {
PaymentAttempts {
count: 0,
first_attempted_at: T::now()
}
}
}
impl<T: Time> Display for PaymentAttempts<T> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
#[cfg(feature = "no-std")]
return write!( f, "attempts: {}", self.count);
#[cfg(not(feature = "no-std"))]
return write!(
f,
"attempts: {}, duration: {}s",
self.count,
T::now().duration_since(self.first_attempted_at).as_secs()
);
}
}
/// A trait defining behavior of an [`Invoice`] payer.
pub trait Payer {
/// Returns the payer's node id.
fn node_id(&self) -> PublicKey;
/// Returns the payer's channels.
fn first_hops(&self) -> Vec<ChannelDetails>;
/// Sends a payment over the Lightning Network using the given [`Route`].
fn send_payment(
&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>
) -> Result<PaymentId, PaymentSendFailure>;
/// Sends a spontaneous payment over the Lightning Network using the given [`Route`].
fn send_spontaneous_payment(
&self, route: &Route, payment_preimage: PaymentPreimage
) -> Result<PaymentId, PaymentSendFailure>;
/// Retries a failed payment path for the [`PaymentId`] using the given [`Route`].
fn retry_payment(&self, route: &Route, payment_id: PaymentId) -> Result<(), PaymentSendFailure>;
/// Signals that no further retries for the given payment will occur.
fn abandon_payment(&self, payment_id: PaymentId);
}
/// A trait defining behavior for routing an [`Invoice`] payment.
pub trait Router {
/// Finds a [`Route`] between `payer` and `payee` for a payment with the given values.
fn find_route(
&self, payer: &PublicKey, route_params: &RouteParameters, payment_hash: &PaymentHash,
first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: InFlightHtlcs
) -> Result<Route, LightningError>;
/// Lets the router know that payment through a specific path has failed.
fn notify_payment_path_failed(&self, path: &[&RouteHop], short_channel_id: u64);
/// Lets the router know that payment through a specific path was successful.
fn notify_payment_path_successful(&self, path: &[&RouteHop]);
/// Lets the router know that a payment probe was successful.
fn notify_payment_probe_successful(&self, path: &[&RouteHop]);
/// Lets the router know that a payment probe failed.
fn notify_payment_probe_failed(&self, path: &[&RouteHop], short_channel_id: u64);
}
/// Strategies available to retry payment path failures for an [`Invoice`].
///
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Retry {
/// Max number of attempts to retry payment.
///
/// Note that this is the number of *path* failures, not full payment retries. For multi-path
/// payments, if this is less than the total number of paths, we will never even retry all of the
/// payment's paths.
Attempts(usize),
#[cfg(feature = "std")]
/// Time elapsed before abandoning retries for a payment.
Timeout(Duration),
}
impl Retry {
fn is_retryable_now<T: Time>(&self, attempts: &PaymentAttempts<T>) -> bool {
match (self, attempts) {
(Retry::Attempts(max_retry_count), PaymentAttempts { count, .. }) => {
max_retry_count >= &count
},
#[cfg(feature = "std")]
(Retry::Timeout(max_duration), PaymentAttempts { first_attempted_at, .. } ) =>
*max_duration >= T::now().duration_since(*first_attempted_at),
}
}
}
/// An error that may occur when making a payment.
#[derive(Clone, Debug)]
pub enum PaymentError {
/// An error resulting from the provided [`Invoice`] or payment hash.
Invoice(&'static str),
/// An error occurring when finding a route.
Routing(LightningError),
/// An error occurring when sending a payment.
Sending(PaymentSendFailure),
}
impl<P: Deref, R: Router, L: Deref, E: EventHandler, T: Time> InvoicePayerUsingTime<P, R, L, E, T>
where
P::Target: Payer,
L::Target: Logger,
{
/// Creates an invoice payer that retries failed payment paths.
///
/// Will forward any [`Event::PaymentPathFailed`] events to the decorated `event_handler` once
/// `retry` has been exceeded for a given [`Invoice`].
pub fn new(
payer: P, router: R, logger: L, event_handler: E, retry: Retry
) -> Self {
Self {
payer,
router,
logger,
event_handler,
payment_cache: Mutex::new(HashMap::new()),
retry,
}
}
/// Pays the given [`Invoice`], caching it for later use in case a retry is needed.
///
/// You should ensure that the `invoice.payment_hash()` is unique and the same payment_hash has
/// never been paid before. Because [`InvoicePayer`] is stateless no effort is made to do so
/// for you.
pub fn pay_invoice(&self, invoice: &Invoice) -> Result<PaymentId, PaymentError> {
if invoice.amount_milli_satoshis().is_none() {
Err(PaymentError::Invoice("amount missing"))
} else {
self.pay_invoice_using_amount(invoice, None)
}
}
/// Pays the given zero-value [`Invoice`] using the given amount, caching it for later use in
/// case a retry is needed.
///
/// You should ensure that the `invoice.payment_hash()` is unique and the same payment_hash has
/// never been paid before. Because [`InvoicePayer`] is stateless no effort is made to do so
/// for you.
pub fn pay_zero_value_invoice(
&self, invoice: &Invoice, amount_msats: u64
) -> Result<PaymentId, PaymentError> {
if invoice.amount_milli_satoshis().is_some() {
Err(PaymentError::Invoice("amount unexpected"))
} else {
self.pay_invoice_using_amount(invoice, Some(amount_msats))
}
}
fn pay_invoice_using_amount(
&self, invoice: &Invoice, amount_msats: Option<u64>
) -> Result<PaymentId, PaymentError> {
debug_assert!(invoice.amount_milli_satoshis().is_some() ^ amount_msats.is_some());
let payment_hash = PaymentHash(invoice.payment_hash().clone().into_inner());
match self.payment_cache.lock().unwrap().entry(payment_hash) {
hash_map::Entry::Occupied(_) => return Err(PaymentError::Invoice("payment pending")),
hash_map::Entry::Vacant(entry) => entry.insert(PaymentInfo::new()),
};
let payment_secret = Some(invoice.payment_secret().clone());
let mut payment_params = PaymentParameters::from_node_id(invoice.recover_payee_pub_key())
.with_expiry_time(expiry_time_from_unix_epoch(&invoice).as_secs())
.with_route_hints(invoice.route_hints());
if let Some(features) = invoice.features() {
payment_params = payment_params.with_features(features.clone());
}
let route_params = RouteParameters {
payment_params,
final_value_msat: invoice.amount_milli_satoshis().or(amount_msats).unwrap(),
final_cltv_expiry_delta: invoice.min_final_cltv_expiry() as u32,
};
let send_payment = |route: &Route| {
self.payer.send_payment(route, payment_hash, &payment_secret)
};
self.pay_internal(&route_params, payment_hash, send_payment)
.map_err(|e| { self.payment_cache.lock().unwrap().remove(&payment_hash); e })
}
/// Pays `pubkey` an amount using the hash of the given preimage, caching it for later use in
/// case a retry is needed.
///
/// You should ensure that `payment_preimage` is unique and that its `payment_hash` has never
/// been paid before. Because [`InvoicePayer`] is stateless no effort is made to do so for you.
pub fn pay_pubkey(
&self, pubkey: PublicKey, payment_preimage: PaymentPreimage, amount_msats: u64,
final_cltv_expiry_delta: u32
) -> Result<PaymentId, PaymentError> {
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
match self.payment_cache.lock().unwrap().entry(payment_hash) {
hash_map::Entry::Occupied(_) => return Err(PaymentError::Invoice("payment pending")),
hash_map::Entry::Vacant(entry) => entry.insert(PaymentInfo::new()),
};
let route_params = RouteParameters {
payment_params: PaymentParameters::for_keysend(pubkey),
final_value_msat: amount_msats,
final_cltv_expiry_delta,
};
let send_payment = |route: &Route| {
self.payer.send_spontaneous_payment(route, payment_preimage)
};
self.pay_internal(&route_params, payment_hash, send_payment)
.map_err(|e| { self.payment_cache.lock().unwrap().remove(&payment_hash); e })
}
fn pay_internal<F: FnOnce(&Route) -> Result<PaymentId, PaymentSendFailure> + Copy>(
&self, params: &RouteParameters, payment_hash: PaymentHash, send_payment: F,
) -> Result<PaymentId, PaymentError> {
#[cfg(feature = "std")] {
if has_expired(params) {
log_trace!(self.logger, "Invoice expired prior to send for payment {}", log_bytes!(payment_hash.0));
return Err(PaymentError::Invoice("Invoice expired prior to send"));
}
}
let payer = self.payer.node_id();
let first_hops = self.payer.first_hops();
let inflight_htlcs = self.create_inflight_map();
let route = self.router.find_route(
&payer, ¶ms, &payment_hash, Some(&first_hops.iter().collect::<Vec<_>>()),
inflight_htlcs
).map_err(|e| PaymentError::Routing(e))?;
match send_payment(&route) {
Ok(payment_id) => {
for path in route.paths {
self.process_path_inflight_htlcs(payment_hash, path);
}
Ok(payment_id)
},
Err(e) => match e {
PaymentSendFailure::ParameterError(_) => Err(e),
PaymentSendFailure::PathParameterError(_) => Err(e),
PaymentSendFailure::AllFailedRetrySafe(_) => {
let mut payment_cache = self.payment_cache.lock().unwrap();
let payment_info = payment_cache.get_mut(&payment_hash).unwrap();
payment_info.attempts.count += 1;
if self.retry.is_retryable_now(&payment_info.attempts) {
core::mem::drop(payment_cache);
Ok(self.pay_internal(params, payment_hash, send_payment)?)
} else {
Err(e)
}
},
PaymentSendFailure::PartialFailure { failed_paths_retry, payment_id, results } => {
// If a `PartialFailure` event returns a result that is an `Ok()`, it means that
// part of our payment is retried. When we receive `MonitorUpdateFailed`, it
// means that we are still waiting for our channel monitor update to be completed.
for (result, path) in results.iter().zip(route.paths.into_iter()) {
match result {
Ok(_) | Err(APIError::MonitorUpdateFailed) => {
self.process_path_inflight_htlcs(payment_hash, path);
},
_ => {},
}
}
if let Some(retry_data) = failed_paths_retry {
// Some paths were sent, even if we failed to send the full MPP value our
// recipient may misbehave and claim the funds, at which point we have to
// consider the payment sent, so return `Ok()` here, ignoring any retry
// errors.
let _ = self.retry_payment(payment_id, payment_hash, &retry_data);
Ok(payment_id)
} else {
// This may happen if we send a payment and some paths fail, but
// only due to a temporary monitor failure or the like, implying
// they're really in-flight, but we haven't sent the initial
// HTLC-Add messages yet.
Ok(payment_id)
}
},
},
}.map_err(|e| PaymentError::Sending(e))
}
// Takes in a path to have its information stored in `payment_cache`. This is done for paths
// that are pending retry.
fn process_path_inflight_htlcs(&self, payment_hash: PaymentHash, path: Vec<RouteHop>) {
self.payment_cache.lock().unwrap().entry(payment_hash)
.or_insert_with(|| PaymentInfo::new())
.paths.push(path);
}
// Find the path we want to remove in `payment_cache`. If it doesn't exist, do nothing.
fn remove_path_inflight_htlcs(&self, payment_hash: PaymentHash, path: &Vec<RouteHop>) {
self.payment_cache.lock().unwrap().entry(payment_hash)
.and_modify(|payment_info| {
if let Some(idx) = payment_info.paths.iter().position(|p| p == path) {
payment_info.paths.swap_remove(idx);
}
});
}
fn retry_payment(
&self, payment_id: PaymentId, payment_hash: PaymentHash, params: &RouteParameters
) -> Result<(), ()> {
let attempts = self.payment_cache.lock().unwrap().entry(payment_hash)
.and_modify(|info| info.attempts.count += 1 )
.or_insert_with(|| PaymentInfo {
attempts: PaymentAttempts {
count: 1,
first_attempted_at: T::now(),
},
paths: vec![],
}).attempts;
if !self.retry.is_retryable_now(&attempts) {
log_trace!(self.logger, "Payment {} exceeded maximum attempts; not retrying ({})", log_bytes!(payment_hash.0), attempts);
return Err(());
}
#[cfg(feature = "std")] {
if has_expired(params) {
log_trace!(self.logger, "Invoice expired for payment {}; not retrying ({:})", log_bytes!(payment_hash.0), attempts);
return Err(());
}
}
let payer = self.payer.node_id();
let first_hops = self.payer.first_hops();
let inflight_htlcs = self.create_inflight_map();
let route = self.router.find_route(
&payer, ¶ms, &payment_hash, Some(&first_hops.iter().collect::<Vec<_>>()),
inflight_htlcs
);
if route.is_err() {
log_trace!(self.logger, "Failed to find a route for payment {}; not retrying ({:})", log_bytes!(payment_hash.0), attempts);
return Err(());
}
match self.payer.retry_payment(&route.as_ref().unwrap(), payment_id) {
Ok(()) => {
for path in route.unwrap().paths.into_iter() {
self.process_path_inflight_htlcs(payment_hash, path);
}
Ok(())
},
Err(PaymentSendFailure::ParameterError(_)) |
Err(PaymentSendFailure::PathParameterError(_)) => {
log_trace!(self.logger, "Failed to retry for payment {} due to bogus route/payment data, not retrying.", log_bytes!(payment_hash.0));
Err(())
},
Err(PaymentSendFailure::AllFailedRetrySafe(_)) => {
self.retry_payment(payment_id, payment_hash, params)
},
Err(PaymentSendFailure::PartialFailure { failed_paths_retry, results, .. }) => {
// If a `PartialFailure` error contains a result that is an `Ok()`, it means that
// part of our payment is retried. When we receive `MonitorUpdateFailed`, it
// means that we are still waiting for our channel monitor update to complete.
for (result, path) in results.iter().zip(route.unwrap().paths.into_iter()) {
match result {
Ok(_) | Err(APIError::MonitorUpdateFailed) => {
self.process_path_inflight_htlcs(payment_hash, path);
},
_ => {},
}
}
if let Some(retry) = failed_paths_retry {
// Always return Ok for the same reason as noted in pay_internal.
let _ = self.retry_payment(payment_id, payment_hash, &retry);
}
Ok(())
},
}
}
/// Removes the payment cached by the given payment hash.
///
/// Should be called once a payment has failed or succeeded if not using [`InvoicePayer`] as an
/// [`EventHandler`]. Otherwise, calling this method is unnecessary.
pub fn remove_cached_payment(&self, payment_hash: &PaymentHash) {
self.payment_cache.lock().unwrap().remove(payment_hash);
}
/// Given a [`PaymentHash`], this function looks up inflight path attempts in the payment_cache.
/// Then, it uses the path information inside the cache to construct a HashMap mapping a channel's
/// short channel id and direction to the amount being sent through it.
///
/// This function should be called whenever we need information about currently used up liquidity
/// across payments.
fn create_inflight_map(&self) -> InFlightHtlcs {
let mut total_inflight_map: HashMap<(u64, bool), u64> = HashMap::new();
// Make an attempt at finding existing payment information from `payment_cache`. If it
// does not exist, it probably is a fresh payment and we can just return an empty
// HashMap.
for payment_info in self.payment_cache.lock().unwrap().values() {
for path in &payment_info.paths {
if path.is_empty() { break };
// total_inflight_map needs to be direction-sensitive when keeping track of the HTLC value
// that is held up. However, the `hops` array, which is a path returned by `find_route` in
// the router excludes the payer node. In the following lines, the payer's information is
// hardcoded with an inflight value of 0 so that we can correctly represent the first hop
// in our sliding window of two.
let our_node_id: PublicKey = self.payer.node_id();
let reversed_hops_with_payer = path.iter().rev().skip(1)
.map(|hop| hop.pubkey)
.chain(core::iter::once(our_node_id));
let mut cumulative_msat = 0;
// Taking the reversed vector from above, we zip it with just the reversed hops list to
// work "backwards" of the given path, since the last hop's `fee_msat` actually represents
// the total amount sent.
for (next_hop, prev_hop) in path.iter().rev().zip(reversed_hops_with_payer) {
cumulative_msat += next_hop.fee_msat;
total_inflight_map
.entry((next_hop.short_channel_id, NodeId::from_pubkey(&prev_hop) < NodeId::from_pubkey(&next_hop.pubkey)))
.and_modify(|used_liquidity_msat| *used_liquidity_msat += cumulative_msat)
.or_insert(cumulative_msat);
}
}
}
InFlightHtlcs(total_inflight_map)
}
}
fn expiry_time_from_unix_epoch(invoice: &Invoice) -> Duration {
invoice.signed_invoice.raw_invoice.data.timestamp.0 + invoice.expiry_time()
}
#[cfg(feature = "std")]
fn has_expired(route_params: &RouteParameters) -> bool {
if let Some(expiry_time) = route_params.payment_params.expiry_time {
Invoice::is_expired_from_epoch(&SystemTime::UNIX_EPOCH, Duration::from_secs(expiry_time))
} else { false }
}
impl<P: Deref, R: Router, L: Deref, E: EventHandler, T: Time> EventHandler for InvoicePayerUsingTime<P, R, L, E, T>
where
P::Target: Payer,
L::Target: Logger,
{
fn handle_event(&self, event: &Event) {
match event {
Event::PaymentPathFailed { payment_hash, path, .. }
| Event::PaymentPathSuccessful { path, payment_hash: Some(payment_hash), .. }
| Event::ProbeSuccessful { payment_hash, path, .. }
| Event::ProbeFailed { payment_hash, path, .. } => {
self.remove_path_inflight_htlcs(*payment_hash, path);
},
_ => {},
}
match event {
Event::PaymentPathFailed {
payment_id, payment_hash, payment_failed_permanently, path, short_channel_id, retry, ..
} => {
if let Some(short_channel_id) = short_channel_id {
let path = path.iter().collect::<Vec<_>>();
self.router.notify_payment_path_failed(&path, *short_channel_id)
}
if payment_id.is_none() {
log_trace!(self.logger, "Payment {} has no id; not retrying", log_bytes!(payment_hash.0));
} else if *payment_failed_permanently {
log_trace!(self.logger, "Payment {} rejected by destination; not retrying", log_bytes!(payment_hash.0));
self.payer.abandon_payment(payment_id.unwrap());
} else if retry.is_none() {
log_trace!(self.logger, "Payment {} missing retry params; not retrying", log_bytes!(payment_hash.0));
self.payer.abandon_payment(payment_id.unwrap());
} else if self.retry_payment(payment_id.unwrap(), *payment_hash, retry.as_ref().unwrap()).is_ok() {
// We retried at least somewhat, don't provide the PaymentPathFailed event to the user.
return;
} else {
self.payer.abandon_payment(payment_id.unwrap());
}
},
Event::PaymentFailed { payment_hash, .. } => {
self.remove_cached_payment(&payment_hash);
},
Event::PaymentPathSuccessful { path, .. } => {
let path = path.iter().collect::<Vec<_>>();
self.router.notify_payment_path_successful(&path);
},
Event::PaymentSent { payment_hash, .. } => {
let mut payment_cache = self.payment_cache.lock().unwrap();
let attempts = payment_cache
.remove(payment_hash)
.map_or(1, |payment_info| payment_info.attempts.count + 1);
log_trace!(self.logger, "Payment {} succeeded (attempts: {})", log_bytes!(payment_hash.0), attempts);
},
Event::ProbeSuccessful { payment_hash, path, .. } => {
log_trace!(self.logger, "Probe payment {} of {}msat was successful", log_bytes!(payment_hash.0), path.last().unwrap().fee_msat);
let path = path.iter().collect::<Vec<_>>();
self.router.notify_payment_probe_successful(&path);
},
Event::ProbeFailed { payment_hash, path, short_channel_id, .. } => {
if let Some(short_channel_id) = short_channel_id {
log_trace!(self.logger, "Probe payment {} of {}msat failed at channel {}", log_bytes!(payment_hash.0), path.last().unwrap().fee_msat, *short_channel_id);
let path = path.iter().collect::<Vec<_>>();
self.router.notify_payment_probe_failed(&path, *short_channel_id);
}
},
_ => {},
}
// Delegate to the decorated event handler unless the payment is retried.
self.event_handler.handle_event(event)
}
}
/// A map with liquidity value (in msat) keyed by a short channel id and the direction the HTLC
/// is traveling in. The direction boolean is determined by checking if the HTLC source's public
/// key is less than its destination. See [`InFlightHtlcs::used_liquidity_msat`] for more
/// details.
pub struct InFlightHtlcs(HashMap<(u64, bool), u64>);
impl InFlightHtlcs {
/// Returns liquidity in msat given the public key of the HTLC source, target, and short channel
/// id.
pub fn used_liquidity_msat(&self, source: &NodeId, target: &NodeId, channel_scid: u64) -> Option<u64> {
self.0.get(&(channel_scid, source < target)).map(|v| *v)
}
}
impl Writeable for InFlightHtlcs {
fn write<W: lightning::util::ser::Writer>(&self, writer: &mut W) -> Result<(), io::Error> { self.0.write(writer) }
}
impl lightning::util::ser::Readable for InFlightHtlcs {
fn read<R: io::Read>(reader: &mut R) -> Result<Self, lightning::ln::msgs::DecodeError> {
let infight_map: HashMap<(u64, bool), u64> = lightning::util::ser::Readable::read(reader)?;
Ok(Self(infight_map))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{InvoiceBuilder, Currency};
use utils::{ScorerAccountingForInFlightHtlcs, create_invoice_from_channelmanager_and_duration_since_epoch};
use bitcoin_hashes::sha256::Hash as Sha256;
use lightning::ln::PaymentPreimage;
use lightning::ln::channelmanager;
use lightning::ln::features::{ChannelFeatures, NodeFeatures};
use lightning::ln::functional_test_utils::*;
use lightning::ln::msgs::{ChannelMessageHandler, ErrorAction, LightningError};
use lightning::routing::gossip::{EffectiveCapacity, NodeId};
use lightning::routing::router::{PaymentParameters, Route, RouteHop};
use lightning::routing::scoring::{ChannelUsage, LockableScore, Score};
use lightning::util::test_utils::TestLogger;
use lightning::util::errors::APIError;
use lightning::util::events::{Event, EventsProvider, MessageSendEvent, MessageSendEventsProvider};
use secp256k1::{SecretKey, PublicKey, Secp256k1};
use std::cell::RefCell;
use std::collections::VecDeque;
use std::ops::DerefMut;
use std::time::{SystemTime, Duration};
use time_utils::tests::SinceEpoch;
use DEFAULT_EXPIRY_TIME;
use lightning::util::errors::APIError::{ChannelUnavailable, MonitorUpdateFailed};
fn invoice(payment_preimage: PaymentPreimage) -> Invoice {
let payment_hash = Sha256::hash(&payment_preimage.0);
let private_key = SecretKey::from_slice(&[42; 32]).unwrap();
InvoiceBuilder::new(Currency::Bitcoin)
.description("test".into())
.payment_hash(payment_hash)
.payment_secret(PaymentSecret([0; 32]))
.duration_since_epoch(duration_since_epoch())
.min_final_cltv_expiry(144)
.amount_milli_satoshis(128)
.build_signed(|hash| {
Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key)
})
.unwrap()
}
fn duration_since_epoch() -> Duration {
#[cfg(feature = "std")]
let duration_since_epoch =
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
#[cfg(not(feature = "std"))]
let duration_since_epoch = Duration::from_secs(1234567);
duration_since_epoch
}
fn zero_value_invoice(payment_preimage: PaymentPreimage) -> Invoice {
let payment_hash = Sha256::hash(&payment_preimage.0);
let private_key = SecretKey::from_slice(&[42; 32]).unwrap();
InvoiceBuilder::new(Currency::Bitcoin)
.description("test".into())
.payment_hash(payment_hash)
.payment_secret(PaymentSecret([0; 32]))
.duration_since_epoch(duration_since_epoch())
.min_final_cltv_expiry(144)
.build_signed(|hash| {
Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key)
})
.unwrap()
}
#[cfg(feature = "std")]
fn expired_invoice(payment_preimage: PaymentPreimage) -> Invoice {
let payment_hash = Sha256::hash(&payment_preimage.0);
let private_key = SecretKey::from_slice(&[42; 32]).unwrap();
let duration = duration_since_epoch()
.checked_sub(Duration::from_secs(DEFAULT_EXPIRY_TIME * 2))
.unwrap();
InvoiceBuilder::new(Currency::Bitcoin)
.description("test".into())
.payment_hash(payment_hash)
.payment_secret(PaymentSecret([0; 32]))
.duration_since_epoch(duration)
.min_final_cltv_expiry(144)
.amount_milli_satoshis(128)
.build_signed(|hash| {
Secp256k1::new().sign_ecdsa_recoverable(hash, &private_key)
})
.unwrap()
}
fn pubkey() -> PublicKey {
PublicKey::from_slice(&hex::decode("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()[..]).unwrap()
}
#[test]
fn pays_invoice_on_first_attempt() {
let event_handled = core::cell::RefCell::new(false);
let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
let payment_preimage = PaymentPreimage([1; 32]);
let invoice = invoice(payment_preimage);
let payment_hash = PaymentHash(invoice.payment_hash().clone().into_inner());
let final_value_msat = invoice.amount_milli_satoshis().unwrap();
let payer = TestPayer::new().expect_send(Amount::ForInvoice(final_value_msat));
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(0));
let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
assert_eq!(*payer.attempts.borrow(), 1);
invoice_payer.handle_event(&Event::PaymentSent {
payment_id, payment_preimage, payment_hash, fee_paid_msat: None
});
assert_eq!(*event_handled.borrow(), true);
assert_eq!(*payer.attempts.borrow(), 1);
}
#[test]
fn pays_invoice_on_retry() {
let event_handled = core::cell::RefCell::new(false);
let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
let payment_preimage = PaymentPreimage([1; 32]);
let invoice = invoice(payment_preimage);
let payment_hash = PaymentHash(invoice.payment_hash().clone().into_inner());
let final_value_msat = invoice.amount_milli_satoshis().unwrap();
let payer = TestPayer::new()
.expect_send(Amount::ForInvoice(final_value_msat))
.expect_send(Amount::OnRetry(final_value_msat / 2));
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
let payment_id = Some(invoice_payer.pay_invoice(&invoice).unwrap());
assert_eq!(*payer.attempts.borrow(), 1);
let event = Event::PaymentPathFailed {
payment_id,
payment_hash,
network_update: None,
payment_failed_permanently: false,
all_paths_failed: false,
path: TestRouter::path_for_value(final_value_msat),
short_channel_id: None,
retry: Some(TestRouter::retry_for_invoice(&invoice)),
};
invoice_payer.handle_event(&event);
assert_eq!(*event_handled.borrow(), false);
assert_eq!(*payer.attempts.borrow(), 2);
invoice_payer.handle_event(&Event::PaymentSent {
payment_id, payment_preimage, payment_hash, fee_paid_msat: None
});
assert_eq!(*event_handled.borrow(), true);
assert_eq!(*payer.attempts.borrow(), 2);
}
#[test]
fn pays_invoice_on_partial_failure() {
let event_handler = |_: &_| { panic!() };
let payment_preimage = PaymentPreimage([1; 32]);
let invoice = invoice(payment_preimage);
let retry = TestRouter::retry_for_invoice(&invoice);
let final_value_msat = invoice.amount_milli_satoshis().unwrap();
let payer = TestPayer::new()
.fails_with_partial_failure(retry.clone(), OnAttempt(1), None)
.fails_with_partial_failure(retry, OnAttempt(2), None)
.expect_send(Amount::ForInvoice(final_value_msat))
.expect_send(Amount::OnRetry(final_value_msat / 2))
.expect_send(Amount::OnRetry(final_value_msat / 2));
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
assert!(invoice_payer.pay_invoice(&invoice).is_ok());
}
#[test]
fn retries_payment_path_for_unknown_payment() {
let event_handled = core::cell::RefCell::new(false);
let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
let payment_preimage = PaymentPreimage([1; 32]);
let invoice = invoice(payment_preimage);
let payment_hash = PaymentHash(invoice.payment_hash().clone().into_inner());
let final_value_msat = invoice.amount_milli_satoshis().unwrap();
let payer = TestPayer::new()
.expect_send(Amount::OnRetry(final_value_msat / 2))
.expect_send(Amount::OnRetry(final_value_msat / 2));
let router = TestRouter::new(TestScorer::new());
let logger = TestLogger::new();
let invoice_payer =
InvoicePayer::new(&payer, router, &logger, event_handler, Retry::Attempts(2));
let payment_id = Some(PaymentId([1; 32]));
let event = Event::PaymentPathFailed {
payment_id,
payment_hash,
network_update: None,
payment_failed_permanently: false,
all_paths_failed: false,
path: TestRouter::path_for_value(final_value_msat),
short_channel_id: None,
retry: Some(TestRouter::retry_for_invoice(&invoice)),
};
invoice_payer.handle_event(&event);
assert_eq!(*event_handled.borrow(), false);
assert_eq!(*payer.attempts.borrow(), 1);
invoice_payer.handle_event(&event);
assert_eq!(*event_handled.borrow(), false);
assert_eq!(*payer.attempts.borrow(), 2);
invoice_payer.handle_event(&Event::PaymentSent {
payment_id, payment_preimage, payment_hash, fee_paid_msat: None
});
assert_eq!(*event_handled.borrow(), true);
assert_eq!(*payer.attempts.borrow(), 2);
}
#[test]
fn fails_paying_invoice_after_max_retry_counts() {
let event_handled = core::cell::RefCell::new(false);
let event_handler = |_: &_| { *event_handled.borrow_mut() = true; };
let payment_preimage = PaymentPreimage([1; 32]);
let invoice = invoice(payment_preimage);
let final_value_msat = invoice.amount_milli_satoshis().unwrap();
let payer = TestPayer::new()
.expect_send(Amount::ForInvoice(final_value_msat))
.expect_send(Amount::OnRetry(final_value_msat / 2))
.expect_send(Amount::OnRetry(final_value_msat / 2));