Move PendingOutboundPayment to new outbound_payment module
[rust-lightning] / lightning / src / ln / outbound_payment.rs
1 // This file is Copyright its original authors, visible in version control
2 // history.
3 //
4 // This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5 // or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7 // You may not use this file except in accordance with one or both of these
8 // licenses.
9
10 //! Utilities to send payments and manage outbound payment information.
11
12 use crate::ln::{PaymentHash, PaymentSecret};
13 use crate::ln::msgs::DecodeError;
14 use crate::routing::router::{RouteHop, RoutePath};
15 use crate::prelude::*;
16
17 /// Stores the session_priv for each part of a payment that is still pending. For versions 0.0.102
18 /// and later, also stores information for retrying the payment.
19 pub(crate) enum PendingOutboundPayment {
20         Legacy {
21                 session_privs: HashSet<[u8; 32]>,
22         },
23         Retryable {
24                 session_privs: HashSet<[u8; 32]>,
25                 payment_hash: PaymentHash,
26                 payment_secret: Option<PaymentSecret>,
27                 pending_amt_msat: u64,
28                 /// Used to track the fee paid. Only present if the payment was serialized on 0.0.103+.
29                 pending_fee_msat: Option<u64>,
30                 /// The total payment amount across all paths, used to verify that a retry is not overpaying.
31                 total_msat: u64,
32                 /// Our best known block height at the time this payment was initiated.
33                 starting_block_height: u32,
34         },
35         /// When a pending payment is fulfilled, we continue tracking it until all pending HTLCs have
36         /// been resolved. This ensures we don't look up pending payments in ChannelMonitors on restart
37         /// and add a pending payment that was already fulfilled.
38         Fulfilled {
39                 session_privs: HashSet<[u8; 32]>,
40                 payment_hash: Option<PaymentHash>,
41                 timer_ticks_without_htlcs: u8,
42         },
43         /// When a payer gives up trying to retry a payment, they inform us, letting us generate a
44         /// `PaymentFailed` event when all HTLCs have irrevocably failed. This avoids a number of race
45         /// conditions in MPP-aware payment retriers (1), where the possibility of multiple
46         /// `PaymentPathFailed` events with `all_paths_failed` can be pending at once, confusing a
47         /// downstream event handler as to when a payment has actually failed.
48         ///
49         /// (1) https://github.com/lightningdevkit/rust-lightning/issues/1164
50         Abandoned {
51                 session_privs: HashSet<[u8; 32]>,
52                 payment_hash: PaymentHash,
53         },
54 }
55
56 impl PendingOutboundPayment {
57         pub(super) fn is_fulfilled(&self) -> bool {
58                 match self {
59                         PendingOutboundPayment::Fulfilled { .. } => true,
60                         _ => false,
61                 }
62         }
63         pub(super) fn abandoned(&self) -> bool {
64                 match self {
65                         PendingOutboundPayment::Abandoned { .. } => true,
66                         _ => false,
67                 }
68         }
69         pub(super) fn get_pending_fee_msat(&self) -> Option<u64> {
70                 match self {
71                         PendingOutboundPayment::Retryable { pending_fee_msat, .. } => pending_fee_msat.clone(),
72                         _ => None,
73                 }
74         }
75
76         pub(super) fn payment_hash(&self) -> Option<PaymentHash> {
77                 match self {
78                         PendingOutboundPayment::Legacy { .. } => None,
79                         PendingOutboundPayment::Retryable { payment_hash, .. } => Some(*payment_hash),
80                         PendingOutboundPayment::Fulfilled { payment_hash, .. } => *payment_hash,
81                         PendingOutboundPayment::Abandoned { payment_hash, .. } => Some(*payment_hash),
82                 }
83         }
84
85         pub(super) fn mark_fulfilled(&mut self) {
86                 let mut session_privs = HashSet::new();
87                 core::mem::swap(&mut session_privs, match self {
88                         PendingOutboundPayment::Legacy { session_privs } |
89                                 PendingOutboundPayment::Retryable { session_privs, .. } |
90                                 PendingOutboundPayment::Fulfilled { session_privs, .. } |
91                                 PendingOutboundPayment::Abandoned { session_privs, .. }
92                         => session_privs,
93                 });
94                 let payment_hash = self.payment_hash();
95                 *self = PendingOutboundPayment::Fulfilled { session_privs, payment_hash, timer_ticks_without_htlcs: 0 };
96         }
97
98         pub(super) fn mark_abandoned(&mut self) -> Result<(), ()> {
99                 let mut session_privs = HashSet::new();
100                 let our_payment_hash;
101                 core::mem::swap(&mut session_privs, match self {
102                         PendingOutboundPayment::Legacy { .. } |
103                                 PendingOutboundPayment::Fulfilled { .. } =>
104                                 return Err(()),
105                                 PendingOutboundPayment::Retryable { session_privs, payment_hash, .. } |
106                                         PendingOutboundPayment::Abandoned { session_privs, payment_hash, .. } => {
107                                                 our_payment_hash = *payment_hash;
108                                                 session_privs
109                                         },
110                 });
111                 *self = PendingOutboundPayment::Abandoned { session_privs, payment_hash: our_payment_hash };
112                 Ok(())
113         }
114
115         /// panics if path is None and !self.is_fulfilled
116         pub(super) fn remove(&mut self, session_priv: &[u8; 32], path: Option<&Vec<RouteHop>>) -> bool {
117                 let remove_res = match self {
118                         PendingOutboundPayment::Legacy { session_privs } |
119                                 PendingOutboundPayment::Retryable { session_privs, .. } |
120                                 PendingOutboundPayment::Fulfilled { session_privs, .. } |
121                                 PendingOutboundPayment::Abandoned { session_privs, .. } => {
122                                         session_privs.remove(session_priv)
123                                 }
124                 };
125                 if remove_res {
126                         if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, ref mut pending_fee_msat, .. } = self {
127                                 let path = path.expect("Fulfilling a payment should always come with a path");
128                                 let path_last_hop = path.last().expect("Outbound payments must have had a valid path");
129                                 *pending_amt_msat -= path_last_hop.fee_msat;
130                                 if let Some(fee_msat) = pending_fee_msat.as_mut() {
131                                         *fee_msat -= path.get_path_fees();
132                                 }
133                         }
134                 }
135                 remove_res
136         }
137
138         pub(super) fn insert(&mut self, session_priv: [u8; 32], path: &Vec<RouteHop>) -> bool {
139                 let insert_res = match self {
140                         PendingOutboundPayment::Legacy { session_privs } |
141                                 PendingOutboundPayment::Retryable { session_privs, .. } => {
142                                         session_privs.insert(session_priv)
143                                 }
144                         PendingOutboundPayment::Fulfilled { .. } => false,
145                         PendingOutboundPayment::Abandoned { .. } => false,
146                 };
147                 if insert_res {
148                         if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, ref mut pending_fee_msat, .. } = self {
149                                 let path_last_hop = path.last().expect("Outbound payments must have had a valid path");
150                                 *pending_amt_msat += path_last_hop.fee_msat;
151                                 if let Some(fee_msat) = pending_fee_msat.as_mut() {
152                                         *fee_msat += path.get_path_fees();
153                                 }
154                         }
155                 }
156                 insert_res
157         }
158
159         pub(super) fn remaining_parts(&self) -> usize {
160                 match self {
161                         PendingOutboundPayment::Legacy { session_privs } |
162                                 PendingOutboundPayment::Retryable { session_privs, .. } |
163                                 PendingOutboundPayment::Fulfilled { session_privs, .. } |
164                                 PendingOutboundPayment::Abandoned { session_privs, .. } => {
165                                         session_privs.len()
166                                 }
167                 }
168         }
169 }
170
171 impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
172         (0, Legacy) => {
173                 (0, session_privs, required),
174         },
175         (1, Fulfilled) => {
176                 (0, session_privs, required),
177                 (1, payment_hash, option),
178                 (3, timer_ticks_without_htlcs, (default_value, 0)),
179         },
180         (2, Retryable) => {
181                 (0, session_privs, required),
182                 (1, pending_fee_msat, option),
183                 (2, payment_hash, required),
184                 (4, payment_secret, option),
185                 (6, total_msat, required),
186                 (8, pending_amt_msat, required),
187                 (10, starting_block_height, required),
188         },
189         (3, Abandoned) => {
190                 (0, session_privs, required),
191                 (2, payment_hash, required),
192         },
193 );