Skip to content

Commit 00ddb22

Browse files
channelmanager: Add retry data to pending_outbound_payments
1 parent 6b3b25f commit 00ddb22

File tree

1 file changed

+128
-25
lines changed

1 file changed

+128
-25
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 128 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,63 @@ struct PendingInboundPayment {
400400
min_value_msat: Option<u64>,
401401
}
402402

403+
/// Stores the session_priv for each part of a payment that is still pending. For versions 0.0.102
404+
/// and later, also stores information for retrying the payment.
405+
enum PendingOutboundPayment {
406+
Legacy {
407+
session_privs: HashSet<[u8; 32]>,
408+
},
409+
Retryable {
410+
session_privs: HashSet<[u8; 32]>,
411+
payment_hash: PaymentHash,
412+
payment_secret: Option<PaymentSecret>,
413+
pending_amt_msat: u64,
414+
/// The total payment amount across all paths, used to verify that a retry is not overpaying.
415+
total_msat: u64,
416+
},
417+
}
418+
419+
impl PendingOutboundPayment {
420+
fn remove(&mut self, session_priv: &[u8; 32], part_amt_msat: u64) -> bool {
421+
let remove_res = match self {
422+
PendingOutboundPayment::Legacy { session_privs } |
423+
PendingOutboundPayment::Retryable { session_privs, .. } => {
424+
session_privs.remove(session_priv)
425+
}
426+
};
427+
if remove_res {
428+
if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, .. } = self {
429+
*pending_amt_msat -= part_amt_msat;
430+
}
431+
}
432+
remove_res
433+
}
434+
435+
fn insert(&mut self, session_priv: [u8; 32], part_amt_msat: u64) -> bool {
436+
let insert_res = match self {
437+
PendingOutboundPayment::Legacy { session_privs } |
438+
PendingOutboundPayment::Retryable { session_privs, .. } => {
439+
session_privs.insert(session_priv)
440+
}
441+
};
442+
if insert_res {
443+
if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, .. } = self {
444+
*pending_amt_msat += part_amt_msat;
445+
}
446+
}
447+
insert_res
448+
}
449+
450+
fn remaining_parts(&self) -> usize {
451+
match self {
452+
PendingOutboundPayment::Legacy { session_privs } |
453+
PendingOutboundPayment::Retryable { session_privs, .. } => {
454+
session_privs.len()
455+
}
456+
}
457+
}
458+
}
459+
403460
/// SimpleArcChannelManager is useful when you need a ChannelManager with a static lifetime, e.g.
404461
/// when you're using lightning-net-tokio (since tokio::spawn requires parameters with static
405462
/// lifetimes). Other times you can afford a reference, which is more efficient, in which case
@@ -486,7 +543,7 @@ pub struct ChannelManager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref,
486543
/// Locked *after* channel_state.
487544
pending_inbound_payments: Mutex<HashMap<PaymentHash, PendingInboundPayment>>,
488545

489-
/// The session_priv bytes of outbound payments which are pending resolution.
546+
/// The session_priv bytes and retry metadata of outbound payments which are pending resolution.
490547
/// The authoritative state of these HTLCs resides either within Channels or ChannelMonitors
491548
/// (if the channel has been force-closed), however we track them here to prevent duplicative
492549
/// PaymentSent/PaymentPathFailed events. Specifically, in the case of a duplicative
@@ -495,11 +552,10 @@ pub struct ChannelManager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref,
495552
/// which may generate a claim event, we may receive similar duplicate claim/fail MonitorEvents
496553
/// after reloading from disk while replaying blocks against ChannelMonitors.
497554
///
498-
/// Each payment has each of its MPP part's session_priv bytes in the HashSet of the map (even
499-
/// payments over a single path).
555+
/// See `PendingOutboundPayment` documentation for more info.
500556
///
501557
/// Locked *after* channel_state.
502-
pending_outbound_payments: Mutex<HashMap<PaymentId, HashSet<[u8; 32]>>>,
558+
pending_outbound_payments: Mutex<HashMap<PaymentId, PendingOutboundPayment>>,
503559

504560
our_network_key: SecretKey,
505561
our_network_pubkey: PublicKey,
@@ -1894,8 +1950,14 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
18941950

18951951
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
18961952
let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
1897-
let sessions = pending_outbounds.entry(payment_id).or_insert(HashSet::new());
1898-
assert!(sessions.insert(session_priv_bytes));
1953+
let payment = pending_outbounds.entry(payment_id).or_insert_with(|| PendingOutboundPayment::Retryable {
1954+
session_privs: HashSet::new(),
1955+
pending_amt_msat: 0,
1956+
payment_hash: *payment_hash,
1957+
payment_secret: *payment_secret,
1958+
total_msat: total_value,
1959+
});
1960+
payment.insert(session_priv_bytes, path.last().unwrap().fee_msat);
18991961

19001962
let err: Result<(), _> = loop {
19011963
let mut channel_lock = self.channel_state.lock().unwrap();
@@ -2883,23 +2945,23 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
28832945
let mut session_priv_bytes = [0; 32];
28842946
session_priv_bytes.copy_from_slice(&session_priv[..]);
28852947
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
2886-
if let hash_map::Entry::Occupied(mut sessions) = outbounds.entry(payment_id) {
2887-
if sessions.get_mut().remove(&session_priv_bytes) {
2948+
if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
2949+
if payment.get_mut().remove(&session_priv_bytes, path.last().unwrap().fee_msat) {
28882950
self.pending_events.lock().unwrap().push(
28892951
events::Event::PaymentPathFailed {
28902952
payment_hash,
28912953
rejected_by_dest: false,
28922954
network_update: None,
2893-
all_paths_failed: sessions.get().len() == 0,
2955+
all_paths_failed: payment.get().remaining_parts() == 0,
28942956
path: path.clone(),
28952957
#[cfg(test)]
28962958
error_code: None,
28972959
#[cfg(test)]
28982960
error_data: None,
28992961
}
29002962
);
2901-
if sessions.get().len() == 0 {
2902-
sessions.remove();
2963+
if payment.get().remaining_parts() == 0 {
2964+
payment.remove();
29032965
}
29042966
}
29052967
} else {
@@ -2932,11 +2994,11 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
29322994
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
29332995
let mut all_paths_failed = false;
29342996
if let hash_map::Entry::Occupied(mut sessions) = outbounds.entry(payment_id) {
2935-
if !sessions.get_mut().remove(&session_priv_bytes) {
2997+
if !sessions.get_mut().remove(&session_priv_bytes, path.last().unwrap().fee_msat) {
29362998
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
29372999
return;
29383000
}
2939-
if sessions.get().len() == 0 {
3001+
if sessions.get().remaining_parts() == 0 {
29403002
all_paths_failed = true;
29413003
sessions.remove();
29423004
}
@@ -3185,13 +3247,13 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
31853247

31863248
fn claim_funds_internal(&self, mut channel_state_lock: MutexGuard<ChannelHolder<Signer>>, source: HTLCSource, payment_preimage: PaymentPreimage, forwarded_htlc_value_msat: Option<u64>, from_onchain: bool) {
31873249
match source {
3188-
HTLCSource::OutboundRoute { session_priv, payment_id, .. } => {
3250+
HTLCSource::OutboundRoute { session_priv, payment_id, path, .. } => {
31893251
mem::drop(channel_state_lock);
31903252
let mut session_priv_bytes = [0; 32];
31913253
session_priv_bytes.copy_from_slice(&session_priv[..]);
31923254
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
31933255
let found_payment = if let Some(mut sessions) = outbounds.remove(&payment_id) {
3194-
sessions.remove(&session_priv_bytes)
3256+
sessions.remove(&session_priv_bytes, path.last().unwrap().fee_msat)
31953257
} else { false };
31963258
if found_payment {
31973259
self.pending_events.lock().unwrap().push(
@@ -5161,6 +5223,19 @@ impl_writeable_tlv_based!(PendingInboundPayment, {
51615223
(8, min_value_msat, required),
51625224
});
51635225

5226+
impl_writeable_tlv_based_enum!(PendingOutboundPayment,
5227+
(0, Legacy) => {
5228+
(0, session_privs, required),
5229+
},
5230+
(2, Retryable) => {
5231+
(0, session_privs, required),
5232+
(2, payment_hash, required),
5233+
(4, payment_secret, option),
5234+
(6, total_msat, required),
5235+
(8, pending_amt_msat, required),
5236+
},
5237+
;);
5238+
51645239
impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable for ChannelManager<Signer, M, T, K, F, L>
51655240
where M::Target: chain::Watch<Signer>,
51665241
T::Target: BroadcasterInterface,
@@ -5251,18 +5326,34 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
52515326
let pending_outbound_payments = self.pending_outbound_payments.lock().unwrap();
52525327
// For backwards compat, write the session privs and their total length.
52535328
let mut num_pending_outbounds_compat: u64 = 0;
5254-
for (_, outbounds) in pending_outbound_payments.iter() {
5255-
num_pending_outbounds_compat += outbounds.len() as u64;
5329+
for (_, outbound) in pending_outbound_payments.iter() {
5330+
num_pending_outbounds_compat += outbound.remaining_parts() as u64;
52565331
}
52575332
num_pending_outbounds_compat.write(writer)?;
5258-
for (_, outbounds) in pending_outbound_payments.iter() {
5259-
for outbound in outbounds.iter() {
5260-
outbound.write(writer)?;
5333+
for (_, outbound) in pending_outbound_payments.iter() {
5334+
match outbound {
5335+
PendingOutboundPayment::Legacy { session_privs } |
5336+
PendingOutboundPayment::Retryable { session_privs, .. } => {
5337+
for session_priv in session_privs.iter() {
5338+
session_priv.write(writer)?;
5339+
}
5340+
}
52615341
}
52625342
}
52635343

5344+
// Encode without retry info for 0.0.101 compatibility.
5345+
let mut pending_outbound_payments_no_retry: HashMap<PaymentId, HashSet<[u8; 32]>> = HashMap::new();
5346+
for (id, outbound) in pending_outbound_payments.iter() {
5347+
match outbound {
5348+
PendingOutboundPayment::Legacy { session_privs } |
5349+
PendingOutboundPayment::Retryable { session_privs, .. } => {
5350+
pending_outbound_payments_no_retry.insert(*id, session_privs.clone());
5351+
}
5352+
}
5353+
}
52645354
write_tlv_fields!(writer, {
5265-
(1, pending_outbound_payments, required),
5355+
(1, pending_outbound_payments_no_retry, required),
5356+
(3, pending_outbound_payments, required),
52665357
});
52675358

52685359
Ok(())
@@ -5522,21 +5613,33 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
55225613
}
55235614

55245615
let pending_outbound_payments_count_compat: u64 = Readable::read(reader)?;
5525-
let mut pending_outbound_payments_compat: HashMap<PaymentId, HashSet<[u8; 32]>> =
5616+
let mut pending_outbound_payments_compat: HashMap<PaymentId, PendingOutboundPayment> =
55265617
HashMap::with_capacity(cmp::min(pending_outbound_payments_count_compat as usize, MAX_ALLOC_SIZE/32));
55275618
for _ in 0..pending_outbound_payments_count_compat {
55285619
let session_priv = Readable::read(reader)?;
5529-
if pending_outbound_payments_compat.insert(PaymentId(session_priv), [session_priv].iter().cloned().collect()).is_some() {
5620+
let payment = PendingOutboundPayment::Legacy {
5621+
session_privs: [session_priv].iter().cloned().collect()
5622+
};
5623+
if pending_outbound_payments_compat.insert(PaymentId(session_priv), payment).is_some() {
55305624
return Err(DecodeError::InvalidValue)
55315625
};
55325626
}
55335627

5628+
// pending_outbound_payments_no_retry is for compatibility with 0.0.101 clients.
5629+
let mut pending_outbound_payments_no_retry: Option<HashMap<PaymentId, HashSet<[u8; 32]>>> = None;
55345630
let mut pending_outbound_payments = None;
55355631
read_tlv_fields!(reader, {
5536-
(1, pending_outbound_payments, option),
5632+
(1, pending_outbound_payments_no_retry, option),
5633+
(3, pending_outbound_payments, option),
55375634
});
5538-
if pending_outbound_payments.is_none() {
5635+
if pending_outbound_payments.is_none() && pending_outbound_payments_no_retry.is_none() {
55395636
pending_outbound_payments = Some(pending_outbound_payments_compat);
5637+
} else if pending_outbound_payments.is_none() {
5638+
let mut outbounds = HashMap::new();
5639+
for (id, session_privs) in pending_outbound_payments_no_retry.unwrap().drain() {
5640+
outbounds.insert(id, PendingOutboundPayment::Legacy { session_privs });
5641+
}
5642+
pending_outbound_payments = Some(outbounds);
55405643
}
55415644

55425645
let mut secp_ctx = Secp256k1::new();

0 commit comments

Comments
 (0)