Track the amount spent on fees as payments are retried
[rust-lightning] / lightning / src / ln / channelmanager.rs
index 97ae7a50438e178cc67574026ef9bd39295b65af..763a293fa5bcae790e21c7e2b5206c1e0d31ad76 100644 (file)
@@ -45,7 +45,7 @@ use chain::transaction::{OutPoint, TransactionData};
 use ln::{PaymentHash, PaymentPreimage, PaymentSecret};
 use ln::channel::{Channel, ChannelError, ChannelUpdateStatus, UpdateFulfillCommitFetch};
 use ln::features::{InitFeatures, NodeFeatures};
-use routing::router::{Payee, Route, RouteHop, RouteParameters};
+use routing::router::{Payee, Route, RouteHop, RoutePath, RouteParameters};
 use ln::msgs;
 use ln::msgs::NetAddress;
 use ln::onion_utils;
@@ -436,6 +436,8 @@ pub(crate) enum PendingOutboundPayment {
                payment_hash: PaymentHash,
                payment_secret: Option<PaymentSecret>,
                pending_amt_msat: u64,
+               /// Used to track the fee paid. Only present if the payment was serialized on 0.0.103+.
+               pending_fee_msat: Option<u64>,
                /// The total payment amount across all paths, used to verify that a retry is not overpaying.
                total_msat: u64,
                /// Our best known block height at the time this payment was initiated.
@@ -462,6 +464,12 @@ impl PendingOutboundPayment {
                        _ => false,
                }
        }
+       fn get_pending_fee_msat(&self) -> Option<u64> {
+               match self {
+                       PendingOutboundPayment::Retryable { pending_fee_msat, .. } => pending_fee_msat.clone(),
+                       _ => None,
+               }
+       }
 
        fn mark_fulfilled(&mut self) {
                let mut session_privs = HashSet::new();
@@ -484,10 +492,13 @@ impl PendingOutboundPayment {
                        }
                };
                if remove_res {
-                       if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, .. } = self {
+                       if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, ref mut pending_fee_msat, .. } = self {
                                let path = path.expect("Fulfilling a payment should always come with a path");
                                let path_last_hop = path.last().expect("Outbound payments must have had a valid path");
                                *pending_amt_msat -= path_last_hop.fee_msat;
+                               if let Some(fee_msat) = pending_fee_msat.as_mut() {
+                                       *fee_msat -= path.get_path_fees();
+                               }
                        }
                }
                remove_res
@@ -502,9 +513,12 @@ impl PendingOutboundPayment {
                        PendingOutboundPayment::Fulfilled { .. } => false
                };
                if insert_res {
-                       if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, .. } = self {
+                       if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, ref mut pending_fee_msat, .. } = self {
                                let path_last_hop = path.last().expect("Outbound payments must have had a valid path");
                                *pending_amt_msat += path_last_hop.fee_msat;
+                               if let Some(fee_msat) = pending_fee_msat.as_mut() {
+                                       *fee_msat += path.get_path_fees();
+                               }
                        }
                }
                insert_res
@@ -2084,6 +2098,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                        let payment = payment_entry.or_insert_with(|| PendingOutboundPayment::Retryable {
                                                session_privs: HashSet::new(),
                                                pending_amt_msat: 0,
+                                               pending_fee_msat: Some(0),
                                                payment_hash: *payment_hash,
                                                payment_secret: *payment_secret,
                                                starting_block_height: self.best_block.read().unwrap().height(),
@@ -3458,8 +3473,9 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                let mut session_priv_bytes = [0; 32];
                                session_priv_bytes.copy_from_slice(&session_priv[..]);
                                let mut outbounds = self.pending_outbound_payments.lock().unwrap();
-                               let found_payment = if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
+                               if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
                                        let found_payment = !payment.get().is_fulfilled();
+                                       let fee_paid_msat = payment.get().get_pending_fee_msat();
                                        payment.get_mut().mark_fulfilled();
                                        if from_onchain {
                                                // We currently immediately remove HTLCs which were fulfilled on-chain.
@@ -3473,17 +3489,17 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                                        payment.remove();
                                                }
                                        }
-                                       found_payment
-                               } else { false };
-                               if found_payment {
-                                       let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
-                                       self.pending_events.lock().unwrap().push(
-                                               events::Event::PaymentSent {
-                                                       payment_id: Some(payment_id),
-                                                       payment_preimage,
-                                                       payment_hash: payment_hash
-                                               }
-                                       );
+                                       if found_payment {
+                                               let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
+                                               self.pending_events.lock().unwrap().push(
+                                                       events::Event::PaymentSent {
+                                                               payment_id: Some(payment_id),
+                                                               payment_preimage,
+                                                               payment_hash: payment_hash,
+                                                               fee_paid_msat,
+                                                       }
+                                               );
+                                       }
                                } else {
                                        log_trace!(self.logger, "Received duplicative fulfill for HTLC with payment_preimage {}", log_bytes!(payment_preimage.0));
                                }
@@ -5496,6 +5512,7 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
        },
        (2, Retryable) => {
                (0, session_privs, required),
+               (1, pending_fee_msat, option),
                (2, payment_hash, required),
                (4, payment_secret, option),
                (6, total_msat, required),
@@ -5957,11 +5974,13 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
                                                                                if newly_added { "Added" } else { "Had" }, path_amt, log_bytes!(session_priv_bytes), log_bytes!(htlc.payment_hash.0));
                                                                },
                                                                hash_map::Entry::Vacant(entry) => {
+                                                                       let path_fee = path.get_path_fees();
                                                                        entry.insert(PendingOutboundPayment::Retryable {
                                                                                session_privs: [session_priv_bytes].iter().map(|a| *a).collect(),
                                                                                payment_hash: htlc.payment_hash,
                                                                                payment_secret,
                                                                                pending_amt_msat: path_amt,
+                                                                               pending_fee_msat: Some(path_fee),
                                                                                total_msat: path_amt,
                                                                                starting_block_height: best_block_height,
                                                                        });
@@ -6260,7 +6279,7 @@ mod tests {
                // further events will be generated for subsequence path successes.
                let events = nodes[0].node.get_and_clear_pending_events();
                match events[0] {
-                       Event::PaymentSent { payment_id: ref id, payment_preimage: ref preimage, payment_hash: ref hash } => {
+                       Event::PaymentSent { payment_id: ref id, payment_preimage: ref preimage, payment_hash: ref hash, .. } => {
                                assert_eq!(Some(payment_id), *id);
                                assert_eq!(payment_preimage, *preimage);
                                assert_eq!(our_payment_hash, *hash);