Skip to content

Commit a0d0f02

Browse files
authored
Merge pull request #3303 from TheBlueMatt/2024-09-inbound-payment-id
Add a `PaymentId` for inbound payments
2 parents 23109b6 + aaf529d commit a0d0f02

File tree

6 files changed

+128
-24
lines changed

6 files changed

+128
-24
lines changed

fuzz/src/full_stack.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,7 @@ pub fn do_test(mut data: &[u8], logger: &Arc<dyn Logger>) {
666666
// Adding new calls to `EntropySource::get_secure_random_bytes` during startup can change all the
667667
// keys subsequently generated in this test. Rather than regenerating all the messages manually,
668668
// it's easier to just increment the counter here so the keys don't change.
669-
keys_manager.counter.fetch_sub(3, Ordering::AcqRel);
669+
keys_manager.counter.fetch_sub(4, Ordering::AcqRel);
670670
let network_graph = Arc::new(NetworkGraph::new(network, Arc::clone(&logger)));
671671
let gossip_sync =
672672
Arc::new(P2PGossipSync::new(Arc::clone(&network_graph), None, Arc::clone(&logger)));

lightning/src/events/mod.rs

+29-2
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,14 @@ pub enum Event {
749749
///
750750
/// [`ChannelManager::claim_funds`]: crate::ln::channelmanager::ChannelManager::claim_funds
751751
claim_deadline: Option<u32>,
752+
/// A unique ID describing this payment (derived from the list of HTLCs in the payment).
753+
///
754+
/// Payers may pay for the same [`PaymentHash`] multiple times (though this is unsafe and
755+
/// an intermediary node may steal the funds). Thus, in order to accurately track when
756+
/// payments are received and claimed, you should use this identifier.
757+
///
758+
/// Only filled in for payments received on LDK versions 0.1 and higher.
759+
payment_id: Option<PaymentId>,
752760
},
753761
/// Indicates a payment has been claimed and we've received money!
754762
///
@@ -796,6 +804,14 @@ pub enum Event {
796804
///
797805
/// Payments received on LDK versions prior to 0.0.124 will have this field unset.
798806
onion_fields: Option<RecipientOnionFields>,
807+
/// A unique ID describing this payment (derived from the list of HTLCs in the payment).
808+
///
809+
/// Payers may pay for the same [`PaymentHash`] multiple times (though this is unsafe and
810+
/// an intermediary node may steal the funds). Thus, in order to accurately track when
811+
/// payments are received and claimed, you should use this identifier.
812+
///
813+
/// Only filled in for payments received on LDK versions 0.1 and higher.
814+
payment_id: Option<PaymentId>,
799815
},
800816
/// Indicates that a peer connection with a node is needed in order to send an [`OnionMessage`].
801817
///
@@ -1432,7 +1448,7 @@ impl Writeable for Event {
14321448
},
14331449
&Event::PaymentClaimable { ref payment_hash, ref amount_msat, counterparty_skimmed_fee_msat,
14341450
ref purpose, ref receiver_node_id, ref via_channel_id, ref via_user_channel_id,
1435-
ref claim_deadline, ref onion_fields
1451+
ref claim_deadline, ref onion_fields, ref payment_id,
14361452
} => {
14371453
1u8.write(writer)?;
14381454
let mut payment_secret = None;
@@ -1478,6 +1494,7 @@ impl Writeable for Event {
14781494
(9, onion_fields, option),
14791495
(10, skimmed_fee_opt, option),
14801496
(11, payment_context, option),
1497+
(13, payment_id, option),
14811498
});
14821499
},
14831500
&Event::PaymentSent { ref payment_id, ref payment_preimage, ref payment_hash, ref fee_paid_msat } => {
@@ -1634,7 +1651,10 @@ impl Writeable for Event {
16341651
// We never write the OpenChannelRequest events as, upon disconnection, peers
16351652
// drop any channels which have not yet exchanged funding_signed.
16361653
},
1637-
&Event::PaymentClaimed { ref payment_hash, ref amount_msat, ref purpose, ref receiver_node_id, ref htlcs, ref sender_intended_total_msat, ref onion_fields } => {
1654+
&Event::PaymentClaimed { ref payment_hash, ref amount_msat, ref purpose,
1655+
ref receiver_node_id, ref htlcs, ref sender_intended_total_msat, ref onion_fields,
1656+
ref payment_id,
1657+
} => {
16381658
19u8.write(writer)?;
16391659
write_tlv_fields!(writer, {
16401660
(0, payment_hash, required),
@@ -1644,6 +1664,7 @@ impl Writeable for Event {
16441664
(5, *htlcs, optional_vec),
16451665
(7, sender_intended_total_msat, option),
16461666
(9, onion_fields, option),
1667+
(11, payment_id, option),
16471668
});
16481669
},
16491670
&Event::ProbeSuccessful { ref payment_id, ref payment_hash, ref path } => {
@@ -1767,6 +1788,7 @@ impl MaybeReadable for Event {
17671788
let mut via_user_channel_id = None;
17681789
let mut onion_fields = None;
17691790
let mut payment_context = None;
1791+
let mut payment_id = None;
17701792
read_tlv_fields!(reader, {
17711793
(0, payment_hash, required),
17721794
(1, receiver_node_id, option),
@@ -1780,6 +1802,7 @@ impl MaybeReadable for Event {
17801802
(9, onion_fields, option),
17811803
(10, counterparty_skimmed_fee_msat_opt, option),
17821804
(11, payment_context, option),
1805+
(13, payment_id, option),
17831806
});
17841807
let purpose = match payment_secret {
17851808
Some(secret) => PaymentPurpose::from_parts(payment_preimage, secret, payment_context),
@@ -1796,6 +1819,7 @@ impl MaybeReadable for Event {
17961819
via_user_channel_id,
17971820
claim_deadline,
17981821
onion_fields,
1822+
payment_id,
17991823
}))
18001824
};
18011825
f()
@@ -2037,6 +2061,7 @@ impl MaybeReadable for Event {
20372061
let mut htlcs: Option<Vec<ClaimedHTLC>> = Some(vec![]);
20382062
let mut sender_intended_total_msat: Option<u64> = None;
20392063
let mut onion_fields = None;
2064+
let mut payment_id = None;
20402065
read_tlv_fields!(reader, {
20412066
(0, payment_hash, required),
20422067
(1, receiver_node_id, option),
@@ -2045,6 +2070,7 @@ impl MaybeReadable for Event {
20452070
(5, htlcs, optional_vec),
20462071
(7, sender_intended_total_msat, option),
20472072
(9, onion_fields, option),
2073+
(11, payment_id, option),
20482074
});
20492075
Ok(Some(Event::PaymentClaimed {
20502076
receiver_node_id,
@@ -2054,6 +2080,7 @@ impl MaybeReadable for Event {
20542080
htlcs: htlcs.unwrap_or_default(),
20552081
sender_intended_total_msat,
20562082
onion_fields,
2083+
payment_id,
20572084
}))
20582085
};
20592086
f()

lightning/src/ln/channelmanager.rs

+76-8
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use bitcoin::constants::ChainHash;
2323
use bitcoin::key::constants::SECRET_KEY_SIZE;
2424
use bitcoin::network::Network;
2525

26-
use bitcoin::hashes::Hash;
26+
use bitcoin::hashes::{Hash, HashEngine, HmacEngine};
2727
use bitcoin::hashes::hmac::Hmac;
2828
use bitcoin::hashes::sha256::Hash as Sha256;
2929
use bitcoin::hash_types::{BlockHash, Txid};
@@ -369,6 +369,7 @@ pub(crate) struct HTLCPreviousHopData {
369369
counterparty_node_id: Option<PublicKey>,
370370
}
371371

372+
#[derive(PartialEq, Eq)]
372373
enum OnionPayload {
373374
/// Indicates this incoming onion payload is for the purpose of paying an invoice.
374375
Invoice {
@@ -381,6 +382,7 @@ enum OnionPayload {
381382
}
382383

383384
/// HTLCs that are to us and can be failed/claimed by the user
385+
#[derive(PartialEq, Eq)]
384386
struct ClaimableHTLC {
385387
prev_hop: HTLCPreviousHopData,
386388
cltv_expiry: u32,
@@ -412,6 +414,23 @@ impl From<&ClaimableHTLC> for events::ClaimedHTLC {
412414
}
413415
}
414416

417+
impl PartialOrd for ClaimableHTLC {
418+
fn partial_cmp(&self, other: &ClaimableHTLC) -> Option<cmp::Ordering> {
419+
Some(self.cmp(other))
420+
}
421+
}
422+
impl Ord for ClaimableHTLC {
423+
fn cmp(&self, other: &ClaimableHTLC) -> cmp::Ordering {
424+
let res = (self.prev_hop.channel_id, self.prev_hop.htlc_id).cmp(
425+
&(other.prev_hop.channel_id, other.prev_hop.htlc_id)
426+
);
427+
if res.is_eq() {
428+
debug_assert!(self == other, "ClaimableHTLCs from the same source should be identical");
429+
}
430+
res
431+
}
432+
}
433+
415434
/// A trait defining behavior for creating and verifing the HMAC for authenticating a given data.
416435
pub trait Verification {
417436
/// Constructs an HMAC to include in [`OffersContext`] for the data along with the given
@@ -492,6 +511,22 @@ impl Verification for PaymentId {
492511
}
493512
}
494513

514+
impl PaymentId {
515+
fn for_inbound_from_htlcs<I: Iterator<Item=(ChannelId, u64)>>(key: &[u8; 32], htlcs: I) -> PaymentId {
516+
let mut prev_pair = None;
517+
let mut hasher = HmacEngine::new(key);
518+
for (channel_id, htlc_id) in htlcs {
519+
hasher.input(&channel_id.0);
520+
hasher.input(&htlc_id.to_le_bytes());
521+
if let Some(prev) = prev_pair {
522+
debug_assert!(prev < (channel_id, htlc_id), "HTLCs should be sorted");
523+
}
524+
prev_pair = Some((channel_id, htlc_id));
525+
}
526+
PaymentId(Hmac::<Sha256>::from_engine(hasher).to_byte_array())
527+
}
528+
}
529+
495530
impl Writeable for PaymentId {
496531
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
497532
self.0.write(w)
@@ -765,6 +800,7 @@ struct ClaimingPayment {
765800
htlcs: Vec<events::ClaimedHTLC>,
766801
sender_intended_value: Option<u64>,
767802
onion_fields: Option<RecipientOnionFields>,
803+
payment_id: Option<PaymentId>,
768804
}
769805
impl_writeable_tlv_based!(ClaimingPayment, {
770806
(0, amount_msat, required),
@@ -773,6 +809,7 @@ impl_writeable_tlv_based!(ClaimingPayment, {
773809
(5, htlcs, optional_vec),
774810
(7, sender_intended_value, option),
775811
(9, onion_fields, option),
812+
(11, payment_id, option),
776813
});
777814

778815
struct ClaimablePayment {
@@ -781,6 +818,15 @@ struct ClaimablePayment {
781818
htlcs: Vec<ClaimableHTLC>,
782819
}
783820

821+
impl ClaimablePayment {
822+
fn inbound_payment_id(&self, secret: &[u8; 32]) -> PaymentId {
823+
PaymentId::for_inbound_from_htlcs(
824+
secret,
825+
self.htlcs.iter().map(|htlc| (htlc.prev_hop.channel_id, htlc.prev_hop.htlc_id))
826+
)
827+
}
828+
}
829+
784830
/// Represent the channel funding transaction type.
785831
enum FundingType {
786832
/// This variant is useful when we want LDK to validate the funding transaction and
@@ -2283,6 +2329,9 @@ where
22832329
/// keeping additional state.
22842330
probing_cookie_secret: [u8; 32],
22852331

2332+
/// When generating [`PaymentId`]s for inbound payments, we HMAC the HTLCs with this secret.
2333+
inbound_payment_id_secret: [u8; 32],
2334+
22862335
/// The highest block timestamp we've seen, which is usually a good guess at the current time.
22872336
/// Assuming most miners are generating blocks with reasonable timestamps, this shouldn't be
22882337
/// very far in the past, and can only ever be up to two hours in the future.
@@ -3176,6 +3225,7 @@ where
31763225
fake_scid_rand_bytes: entropy_source.get_secure_random_bytes(),
31773226

31783227
probing_cookie_secret: entropy_source.get_secure_random_bytes(),
3228+
inbound_payment_id_secret: entropy_source.get_secure_random_bytes(),
31793229

31803230
highest_seen_timestamp: AtomicUsize::new(current_timestamp as usize),
31813231

@@ -5769,10 +5819,9 @@ where
57695819
} else {
57705820
claimable_payment.onion_fields = Some(onion_fields);
57715821
}
5772-
let ref mut htlcs = &mut claimable_payment.htlcs;
57735822
let mut total_value = claimable_htlc.sender_intended_value;
57745823
let mut earliest_expiry = claimable_htlc.cltv_expiry;
5775-
for htlc in htlcs.iter() {
5824+
for htlc in claimable_payment.htlcs.iter() {
57765825
total_value += htlc.sender_intended_value;
57775826
earliest_expiry = cmp::min(earliest_expiry, htlc.cltv_expiry);
57785827
if htlc.total_msat != claimable_htlc.total_msat {
@@ -5794,13 +5843,18 @@ where
57945843
#[allow(unused_assignments)] {
57955844
committed_to_claimable = true;
57965845
}
5797-
htlcs.push(claimable_htlc);
5798-
let amount_msat = htlcs.iter().map(|htlc| htlc.value).sum();
5799-
htlcs.iter_mut().for_each(|htlc| htlc.total_value_received = Some(amount_msat));
5800-
let counterparty_skimmed_fee_msat = htlcs.iter()
5846+
claimable_payment.htlcs.push(claimable_htlc);
5847+
let amount_msat =
5848+
claimable_payment.htlcs.iter().map(|htlc| htlc.value).sum();
5849+
claimable_payment.htlcs.iter_mut()
5850+
.for_each(|htlc| htlc.total_value_received = Some(amount_msat));
5851+
let counterparty_skimmed_fee_msat = claimable_payment.htlcs.iter()
58015852
.map(|htlc| htlc.counterparty_skimmed_fee_msat.unwrap_or(0)).sum();
58025853
debug_assert!(total_value.saturating_sub(amount_msat) <=
58035854
counterparty_skimmed_fee_msat);
5855+
claimable_payment.htlcs.sort();
5856+
let payment_id =
5857+
claimable_payment.inbound_payment_id(&self.inbound_payment_id_secret);
58045858
new_events.push_back((events::Event::PaymentClaimable {
58055859
receiver_node_id: Some(receiver_node_id),
58065860
payment_hash,
@@ -5811,13 +5865,14 @@ where
58115865
via_user_channel_id: Some(prev_user_channel_id),
58125866
claim_deadline: Some(earliest_expiry - HTLC_FAIL_BACK_BUFFER),
58135867
onion_fields: claimable_payment.onion_fields.clone(),
5868+
payment_id: Some(payment_id),
58145869
}, None));
58155870
payment_claimable_generated = true;
58165871
} else {
58175872
// Nothing to do - we haven't reached the total
58185873
// payment value yet, wait until we receive more
58195874
// MPP parts.
5820-
htlcs.push(claimable_htlc);
5875+
claimable_payment.htlcs.push(claimable_htlc);
58215876
#[allow(unused_assignments)] {
58225877
committed_to_claimable = true;
58235878
}
@@ -6614,6 +6669,7 @@ where
66146669
}
66156670
}
66166671

6672+
let payment_id = payment.inbound_payment_id(&self.inbound_payment_id_secret);
66176673
let claiming_payment = claimable_payments.pending_claiming_payments
66186674
.entry(payment_hash)
66196675
.and_modify(|_| {
@@ -6631,6 +6687,7 @@ where
66316687
htlcs,
66326688
sender_intended_value,
66336689
onion_fields: payment.onion_fields,
6690+
payment_id: Some(payment_id),
66346691
}
66356692
});
66366693

@@ -7148,6 +7205,7 @@ where
71487205
htlcs,
71497206
sender_intended_value: sender_intended_total_msat,
71507207
onion_fields,
7208+
payment_id,
71517209
}) = payment {
71527210
self.pending_events.lock().unwrap().push_back((events::Event::PaymentClaimed {
71537211
payment_hash,
@@ -7157,6 +7215,7 @@ where
71577215
htlcs,
71587216
sender_intended_total_msat,
71597217
onion_fields,
7218+
payment_id,
71607219
}, None));
71617220
}
71627221
},
@@ -12428,6 +12487,7 @@ where
1242812487
let mut events_override = None;
1242912488
let mut in_flight_monitor_updates: Option<HashMap<(PublicKey, OutPoint), Vec<ChannelMonitorUpdate>>> = None;
1243012489
let mut decode_update_add_htlcs: Option<HashMap<u64, Vec<msgs::UpdateAddHTLC>>> = None;
12490+
let mut inbound_payment_id_secret = None;
1243112491
read_tlv_fields!(reader, {
1243212492
(1, pending_outbound_payments_no_retry, option),
1243312493
(2, pending_intercepted_htlcs, option),
@@ -12442,6 +12502,7 @@ where
1244212502
(11, probing_cookie_secret, option),
1244312503
(13, claimable_htlc_onion_fields, optional_vec),
1244412504
(14, decode_update_add_htlcs, option),
12505+
(15, inbound_payment_id_secret, option),
1244512506
});
1244612507
let mut decode_update_add_htlcs = decode_update_add_htlcs.unwrap_or_else(|| new_hash_map());
1244712508
if fake_scid_rand_bytes.is_none() {
@@ -12452,6 +12513,10 @@ where
1245212513
probing_cookie_secret = Some(args.entropy_source.get_secure_random_bytes());
1245312514
}
1245412515

12516+
if inbound_payment_id_secret.is_none() {
12517+
inbound_payment_id_secret = Some(args.entropy_source.get_secure_random_bytes());
12518+
}
12519+
1245512520
if let Some(events) = events_override {
1245612521
pending_events_read = events;
1245712522
}
@@ -12900,6 +12965,7 @@ where
1290012965
previous_hop_monitor.provide_payment_preimage(&payment_hash, &payment_preimage, &args.tx_broadcaster, &bounded_fee_estimator, &args.logger);
1290112966
}
1290212967
}
12968+
let payment_id = payment.inbound_payment_id(&inbound_payment_id_secret.unwrap());
1290312969
pending_events_read.push_back((events::Event::PaymentClaimed {
1290412970
receiver_node_id,
1290512971
payment_hash,
@@ -12908,6 +12974,7 @@ where
1290812974
htlcs: payment.htlcs.iter().map(events::ClaimedHTLC::from).collect(),
1290912975
sender_intended_total_msat: payment.htlcs.first().map(|htlc| htlc.total_msat),
1291012976
onion_fields: payment.onion_fields,
12977+
payment_id: Some(payment_id),
1291112978
}, None));
1291212979
}
1291312980
}
@@ -12978,6 +13045,7 @@ where
1297813045
fake_scid_rand_bytes: fake_scid_rand_bytes.unwrap(),
1297913046

1298013047
probing_cookie_secret: probing_cookie_secret.unwrap(),
13048+
inbound_payment_id_secret: inbound_payment_id_secret.unwrap(),
1298113049

1298213050
our_network_pubkey,
1298313051
secp_ctx,

0 commit comments

Comments
 (0)