Skip to content

Commit bd1e20d

Browse files
committed
Store an events::PaymentPurpose with each claimable payment
In fc77c57 we stopped using the `FInalOnionHopData` in `OnionPayload::Invoice` directly and intend to remove it eventually. However, in the next few commits we need access to the payment secret when claimaing a payment, as we create a new `PaymentPurpose` during the claim process for a new event. In order to get access to a `PaymentPurpose` without having access to the `FinalOnionHopData` we here change the storage of `claimable_htlcs` to store a single `PaymentPurpose` explicitly with each set of claimable HTLCs.
1 parent 2a42595 commit bd1e20d

File tree

2 files changed

+77
-19
lines changed

2 files changed

+77
-19
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 69 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ pub(super) struct ChannelHolder<Signer: Sign> {
418418
/// Note that while this is held in the same mutex as the channels themselves, no consistency
419419
/// guarantees are made about the channels given here actually existing anymore by the time you
420420
/// go to read them!
421-
claimable_htlcs: HashMap<PaymentHash, Vec<ClaimableHTLC>>,
421+
claimable_htlcs: HashMap<PaymentHash, (events::PaymentPurpose, Vec<ClaimableHTLC>)>,
422422
/// Messages to send to peers - pushed to in the same lock that they are generated in (except
423423
/// for broadcast messages, where ordering isn't as strict).
424424
pub(super) pending_msg_events: Vec<MessageSendEvent>,
@@ -3143,8 +3143,14 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
31433143
macro_rules! check_total_value {
31443144
($payment_data: expr, $payment_preimage: expr) => {{
31453145
let mut payment_received_generated = false;
3146-
let htlcs = channel_state.claimable_htlcs.entry(payment_hash)
3147-
.or_insert(Vec::new());
3146+
let purpose = || {
3147+
events::PaymentPurpose::InvoicePayment {
3148+
payment_preimage: $payment_preimage,
3149+
payment_secret: $payment_data.payment_secret,
3150+
}
3151+
};
3152+
let (_, htlcs) = channel_state.claimable_htlcs.entry(payment_hash)
3153+
.or_insert_with(|| (purpose(), Vec::new()));
31483154
if htlcs.len() == 1 {
31493155
if let OnionPayload::Spontaneous(_) = htlcs[0].onion_payload {
31503156
log_trace!(self.logger, "Failing new HTLC with payment_hash {} as we already had an existing keysend HTLC with the same payment hash", log_bytes!(payment_hash.0));
@@ -3175,10 +3181,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
31753181
htlcs.push(claimable_htlc);
31763182
new_events.push(events::Event::PaymentReceived {
31773183
payment_hash,
3178-
purpose: events::PaymentPurpose::InvoicePayment {
3179-
payment_preimage: $payment_preimage,
3180-
payment_secret: $payment_data.payment_secret,
3181-
},
3184+
purpose: purpose(),
31823185
amt: total_value,
31833186
});
31843187
payment_received_generated = true;
@@ -3216,11 +3219,12 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
32163219
OnionPayload::Spontaneous(preimage) => {
32173220
match channel_state.claimable_htlcs.entry(payment_hash) {
32183221
hash_map::Entry::Vacant(e) => {
3219-
e.insert(vec![claimable_htlc]);
3222+
let purpose = events::PaymentPurpose::SpontaneousPayment(preimage);
3223+
e.insert((purpose.clone(), vec![claimable_htlc]));
32203224
new_events.push(events::Event::PaymentReceived {
32213225
payment_hash,
32223226
amt: amt_to_forward,
3223-
purpose: events::PaymentPurpose::SpontaneousPayment(preimage),
3227+
purpose,
32243228
});
32253229
},
32263230
hash_map::Entry::Occupied(_) => {
@@ -3459,7 +3463,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
34593463
true
34603464
});
34613465

3462-
channel_state.claimable_htlcs.retain(|payment_hash, htlcs| {
3466+
channel_state.claimable_htlcs.retain(|payment_hash, (_, htlcs)| {
34633467
if htlcs.is_empty() {
34643468
// This should be unreachable
34653469
debug_assert!(false);
@@ -3503,7 +3507,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
35033507

35043508
let mut channel_state = Some(self.channel_state.lock().unwrap());
35053509
let removed_source = channel_state.as_mut().unwrap().claimable_htlcs.remove(payment_hash);
3506-
if let Some(mut sources) = removed_source {
3510+
if let Some((_, mut sources)) = removed_source {
35073511
for htlc in sources.drain(..) {
35083512
if channel_state.is_none() { channel_state = Some(self.channel_state.lock().unwrap()); }
35093513
let mut htlc_msat_height_data = byte_utils::be64_to_array(htlc.value).to_vec();
@@ -3803,7 +3807,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
38033807

38043808
let mut channel_state = Some(self.channel_state.lock().unwrap());
38053809
let removed_source = channel_state.as_mut().unwrap().claimable_htlcs.remove(&payment_hash);
3806-
if let Some(mut sources) = removed_source {
3810+
if let Some((_, mut sources)) = removed_source {
38073811
assert!(!sources.is_empty());
38083812

38093813
// If we are claiming an MPP payment, we have to take special care to ensure that each
@@ -5540,7 +5544,7 @@ where
55405544
});
55415545

55425546
if let Some(height) = height_opt {
5543-
channel_state.claimable_htlcs.retain(|payment_hash, htlcs| {
5547+
channel_state.claimable_htlcs.retain(|payment_hash, (_, htlcs)| {
55445548
htlcs.retain(|htlc| {
55455549
// If height is approaching the number of blocks we think it takes us to get
55465550
// our commitment transaction confirmed before the HTLC expires, plus the
@@ -6283,13 +6287,15 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
62836287
}
62846288
}
62856289

6290+
let mut htlc_purposes: Vec<&events::PaymentPurpose> = Vec::new();
62866291
(channel_state.claimable_htlcs.len() as u64).write(writer)?;
6287-
for (payment_hash, previous_hops) in channel_state.claimable_htlcs.iter() {
6292+
for (payment_hash, (purpose, previous_hops)) in channel_state.claimable_htlcs.iter() {
62886293
payment_hash.write(writer)?;
62896294
(previous_hops.len() as u64).write(writer)?;
62906295
for htlc in previous_hops.iter() {
62916296
htlc.write(writer)?;
62926297
}
6298+
htlc_purposes.push(purpose);
62936299
}
62946300

62956301
let per_peer_state = self.per_peer_state.write().unwrap();
@@ -6366,6 +6372,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
63666372
(3, pending_outbound_payments, required),
63676373
(5, self.our_network_pubkey, required),
63686374
(7, self.fake_scid_rand_bytes, required),
6375+
(9, htlc_purposes, vec_type),
63696376
});
63706377

63716378
Ok(())
@@ -6584,15 +6591,15 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
65846591
}
65856592

65866593
let claimable_htlcs_count: u64 = Readable::read(reader)?;
6587-
let mut claimable_htlcs = HashMap::with_capacity(cmp::min(claimable_htlcs_count as usize, 128));
6594+
let mut claimable_htlcs_list = Vec::with_capacity(cmp::min(claimable_htlcs_count as usize, 128));
65886595
for _ in 0..claimable_htlcs_count {
65896596
let payment_hash = Readable::read(reader)?;
65906597
let previous_hops_len: u64 = Readable::read(reader)?;
65916598
let mut previous_hops = Vec::with_capacity(cmp::min(previous_hops_len as usize, MAX_ALLOC_SIZE/mem::size_of::<ClaimableHTLC>()));
65926599
for _ in 0..previous_hops_len {
6593-
previous_hops.push(Readable::read(reader)?);
6600+
previous_hops.push(<ClaimableHTLC as Readable>::read(reader)?);
65946601
}
6595-
claimable_htlcs.insert(payment_hash, previous_hops);
6602+
claimable_htlcs_list.push((payment_hash, previous_hops));
65966603
}
65976604

65986605
let peer_count: u64 = Readable::read(reader)?;
@@ -6662,11 +6669,13 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
66626669
let mut pending_outbound_payments = None;
66636670
let mut received_network_pubkey: Option<PublicKey> = None;
66646671
let mut fake_scid_rand_bytes: Option<[u8; 32]> = None;
6672+
let mut claimable_htlc_purposes = None;
66656673
read_tlv_fields!(reader, {
66666674
(1, pending_outbound_payments_no_retry, option),
66676675
(3, pending_outbound_payments, option),
66686676
(5, received_network_pubkey, option),
66696677
(7, fake_scid_rand_bytes, option),
6678+
(9, claimable_htlc_purposes, vec_type),
66706679
});
66716680
if fake_scid_rand_bytes.is_none() {
66726681
fake_scid_rand_bytes = Some(args.keys_manager.get_secure_random_bytes());
@@ -6727,6 +6736,49 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
67276736
}
67286737
}
67296738

6739+
let inbound_pmt_key_material = args.keys_manager.get_inbound_payment_key_material();
6740+
let expanded_inbound_key = inbound_payment::ExpandedKey::new(&inbound_pmt_key_material);
6741+
6742+
let mut claimable_htlcs = HashMap::with_capacity(claimable_htlcs_list.len());
6743+
if let Some(mut purposes) = claimable_htlc_purposes {
6744+
if purposes.len() != claimable_htlcs_list.len() {
6745+
return Err(DecodeError::InvalidValue);
6746+
}
6747+
for (purpose, (payment_hash, previous_hops)) in purposes.drain(..).zip(claimable_htlcs_list.drain(..)) {
6748+
claimable_htlcs.insert(payment_hash, (purpose, previous_hops));
6749+
}
6750+
} else {
6751+
// LDK versions prior to 0.0.107 did not write a `pending_htlc_purposes`, but do
6752+
// include a `_legacy_hop_data` in the `OnionPayload`.
6753+
for (payment_hash, previous_hops) in claimable_htlcs_list.drain(..) {
6754+
if previous_hops.is_empty() {
6755+
return Err(DecodeError::InvalidValue);
6756+
}
6757+
let purpose = match &previous_hops[0].onion_payload {
6758+
OnionPayload::Invoice { _legacy_hop_data } => {
6759+
if let Some(hop_data) = _legacy_hop_data {
6760+
events::PaymentPurpose::InvoicePayment {
6761+
payment_preimage: match pending_inbound_payments.get(&payment_hash) {
6762+
Some(inbound_payment) => inbound_payment.payment_preimage,
6763+
None => match inbound_payment::verify(payment_hash, &hop_data, 0, &expanded_inbound_key, &args.logger) {
6764+
Ok(payment_preimage) => payment_preimage,
6765+
Err(()) => {
6766+
log_error!(args.logger, "Failed to read claimable payment data for HTLC with payment hash {} - was not a pending inbound payment and didn't match our payment key", log_bytes!(payment_hash.0));
6767+
return Err(DecodeError::InvalidValue);
6768+
}
6769+
}
6770+
},
6771+
payment_secret: hop_data.payment_secret,
6772+
}
6773+
} else { return Err(DecodeError::InvalidValue); }
6774+
},
6775+
OnionPayload::Spontaneous(payment_preimage) =>
6776+
events::PaymentPurpose::SpontaneousPayment(*payment_preimage),
6777+
};
6778+
claimable_htlcs.insert(payment_hash, (purpose, previous_hops));
6779+
}
6780+
}
6781+
67306782
let mut secp_ctx = Secp256k1::new();
67316783
secp_ctx.seeded_randomize(&args.keys_manager.get_secure_random_bytes());
67326784

@@ -6772,8 +6824,6 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
67726824
}
67736825
}
67746826

6775-
let inbound_pmt_key_material = args.keys_manager.get_inbound_payment_key_material();
6776-
let expanded_inbound_key = inbound_payment::ExpandedKey::new(&inbound_pmt_key_material);
67776827
let channel_manager = ChannelManager {
67786828
genesis_hash,
67796829
fee_estimator: args.fee_estimator,

lightning/src/util/events.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ pub enum PaymentPurpose {
6666
SpontaneousPayment(PaymentPreimage),
6767
}
6868

69+
impl_writeable_tlv_based_enum!(PaymentPurpose,
70+
(0, InvoicePayment) => {
71+
(0, payment_preimage, option),
72+
(2, payment_secret, required),
73+
};
74+
(2, SpontaneousPayment)
75+
);
76+
6977
#[derive(Clone, Debug, PartialEq)]
7078
/// The reason the channel was closed. See individual variants more details.
7179
pub enum ClosureReason {

0 commit comments

Comments
 (0)