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;
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.
_ => 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();
*self = PendingOutboundPayment::Fulfilled { session_privs };
}
- /// panics if part_amt_msat is None and !self.is_fulfilled
- fn remove(&mut self, session_priv: &[u8; 32], part_amt_msat: Option<u64>) -> bool {
+ /// panics if path is None and !self.is_fulfilled
+ fn remove(&mut self, session_priv: &[u8; 32], path: Option<&Vec<RouteHop>>) -> bool {
let remove_res = match self {
PendingOutboundPayment::Legacy { session_privs } |
PendingOutboundPayment::Retryable { session_privs, .. } |
}
};
if remove_res {
- if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, .. } = self {
- *pending_amt_msat -= part_amt_msat.expect("We must only not provide an amount if the payment was already fulfilled");
+ 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
}
- fn insert(&mut self, session_priv: [u8; 32], part_amt_msat: u64) -> bool {
+ fn insert(&mut self, session_priv: [u8; 32], path: &Vec<RouteHop>) -> bool {
let insert_res = match self {
PendingOutboundPayment::Legacy { session_privs } |
PendingOutboundPayment::Retryable { session_privs, .. } => {
PendingOutboundPayment::Fulfilled { .. } => false
};
if insert_res {
- if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, .. } = self {
- *pending_amt_msat += part_amt_msat;
+ 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
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(),
total_msat: total_value,
});
- assert!(payment.insert(session_priv_bytes, path.last().unwrap().fee_msat));
+ assert!(payment.insert(session_priv_bytes, path));
send_res
} {
session_priv_bytes.copy_from_slice(&session_priv[..]);
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
- let path_last_hop = path.last().expect("Outbound payments must have had a valid path");
- if payment.get_mut().remove(&session_priv_bytes, Some(path_last_hop.fee_msat)) &&
- !payment.get().is_fulfilled()
- {
+ if payment.get_mut().remove(&session_priv_bytes, Some(&path)) && !payment.get().is_fulfilled() {
let retry = if let Some(payee_data) = payee {
+ let path_last_hop = path.last().expect("Outbound payments must have had a valid path");
Some(RouteParameters {
payee: payee_data,
final_value_msat: path_last_hop.fee_msat,
session_priv_bytes.copy_from_slice(&session_priv[..]);
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
let mut all_paths_failed = false;
- let path_last_hop = path.last().expect("Outbound payments must have had a valid path");
if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
- if !payment.get_mut().remove(&session_priv_bytes, Some(path_last_hop.fee_msat)) {
+ if !payment.get_mut().remove(&session_priv_bytes, Some(&path)) {
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
return;
}
}
mem::drop(channel_state_lock);
let retry = if let Some(payee_data) = payee {
+ let path_last_hop = path.last().expect("Outbound payments must have had a valid path");
Some(RouteParameters {
payee: payee_data.clone(),
final_value_msat: path_last_hop.fee_msat,
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.
// restart.
// TODO: We should have a second monitor event that informs us of payments
// irrevocably fulfilled.
- payment.get_mut().remove(&session_priv_bytes, Some(path.last().unwrap().fee_msat));
+ payment.get_mut().remove(&session_priv_bytes, Some(&path));
if payment.get().remaining_parts() == 0 {
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));
}
},
(2, Retryable) => {
(0, session_privs, required),
+ (1, pending_fee_msat, option),
(2, payment_hash, required),
(4, payment_secret, option),
(6, total_msat, required),
session_priv_bytes[..].copy_from_slice(&session_priv[..]);
match pending_outbound_payments.as_mut().unwrap().entry(payment_id) {
hash_map::Entry::Occupied(mut entry) => {
- let newly_added = entry.get_mut().insert(session_priv_bytes, path_amt);
+ let newly_added = entry.get_mut().insert(session_priv_bytes, &path);
log_info!(args.logger, "{} a pending payment path for {} msat for session priv {} on an existing pending payment with payment hash {}",
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,
});
// 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);