Skip to content

Commit 4cd40d9

Browse files
TheBlueMattAditya Sharma
authored andcommitted
Add a PaymentId for inbound payments
We expect our users to have fully idempotent `Event` handling as we may replay events on restart for one of a number of reasons. This isn't a big deal as long as all our events have some kind of identifier users can use to check if the `Event` has already been handled. For outbound payments, this is the `PaymentId` they provide in the send methods, however for inbound payments we don't have a great option. `PaymentHash` largely suffices - users can simply always claim in response to a `PaymentClaimable` of sufficient value and treat a `PaymentClaimed` event as duplicate any time they see a second one for the same `PaymentHash`. This mostly works, but may result in accepting duplicative payments if someone (incorrectly) pays twice for the same `PaymentHash`. Users could also fail for duplicative `PaymentClaimable` events of the same `PaymentHash`, but doing so may result in spuriously failing a payment if the `PaymentClaimable` event is a replay and they never saw a corresponding `PaymentClaimed` event. While none of this will result in spuriously thinking they've been paid when they have not, it does result in some pretty awkward semantics which we'd rather avoid our users having to deal with. Instead, here, we add a new `PaymentId` which is simply an HMAC of the HTLCs (as Channel ID, HTLC ID pairs) which were included in the payment.
1 parent 9680447 commit 4cd40d9

File tree

3 files changed

+95
-12
lines changed

3 files changed

+95
-12
lines changed

lightning/src/events/mod.rs

Lines changed: 29 additions & 2 deletions
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

Lines changed: 65 additions & 8 deletions
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
@@ -5773,10 +5819,9 @@ where
57735819
} else {
57745820
claimable_payment.onion_fields = Some(onion_fields);
57755821
}
5776-
let ref mut htlcs = &mut claimable_payment.htlcs;
57775822
let mut total_value = claimable_htlc.sender_intended_value;
57785823
let mut earliest_expiry = claimable_htlc.cltv_expiry;
5779-
for htlc in htlcs.iter() {
5824+
for htlc in claimable_payment.htlcs.iter() {
57805825
total_value += htlc.sender_intended_value;
57815826
earliest_expiry = cmp::min(earliest_expiry, htlc.cltv_expiry);
57825827
if htlc.total_msat != claimable_htlc.total_msat {
@@ -5798,13 +5843,18 @@ where
57985843
#[allow(unused_assignments)] {
57995844
committed_to_claimable = true;
58005845
}
5801-
htlcs.push(claimable_htlc);
5802-
let amount_msat = htlcs.iter().map(|htlc| htlc.value).sum();
5803-
htlcs.iter_mut().for_each(|htlc| htlc.total_value_received = Some(amount_msat));
5804-
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()
58055852
.map(|htlc| htlc.counterparty_skimmed_fee_msat.unwrap_or(0)).sum();
58065853
debug_assert!(total_value.saturating_sub(amount_msat) <=
58075854
counterparty_skimmed_fee_msat);
5855+
claimable_payment.htlcs.sort();
5856+
let payment_id =
5857+
claimable_payment.inbound_payment_id(&self.inbound_payment_id_secret);
58085858
new_events.push_back((events::Event::PaymentClaimable {
58095859
receiver_node_id: Some(receiver_node_id),
58105860
payment_hash,
@@ -5815,13 +5865,14 @@ where
58155865
via_user_channel_id: Some(prev_user_channel_id),
58165866
claim_deadline: Some(earliest_expiry - HTLC_FAIL_BACK_BUFFER),
58175867
onion_fields: claimable_payment.onion_fields.clone(),
5868+
payment_id: Some(payment_id),
58185869
}, None));
58195870
payment_claimable_generated = true;
58205871
} else {
58215872
// Nothing to do - we haven't reached the total
58225873
// payment value yet, wait until we receive more
58235874
// MPP parts.
5824-
htlcs.push(claimable_htlc);
5875+
claimable_payment.htlcs.push(claimable_htlc);
58255876
#[allow(unused_assignments)] {
58265877
committed_to_claimable = true;
58275878
}
@@ -6618,6 +6669,7 @@ where
66186669
}
66196670
}
66206671

6672+
let payment_id = payment.inbound_payment_id(&self.inbound_payment_id_secret);
66216673
let claiming_payment = claimable_payments.pending_claiming_payments
66226674
.entry(payment_hash)
66236675
.and_modify(|_| {
@@ -6635,6 +6687,7 @@ where
66356687
htlcs,
66366688
sender_intended_value,
66376689
onion_fields: payment.onion_fields,
6690+
payment_id: Some(payment_id),
66386691
}
66396692
});
66406693

@@ -7152,6 +7205,7 @@ where
71527205
htlcs,
71537206
sender_intended_value: sender_intended_total_msat,
71547207
onion_fields,
7208+
payment_id,
71557209
}) = payment {
71567210
self.pending_events.lock().unwrap().push_back((events::Event::PaymentClaimed {
71577211
payment_hash,
@@ -7161,6 +7215,7 @@ where
71617215
htlcs,
71627216
sender_intended_total_msat,
71637217
onion_fields,
7218+
payment_id,
71647219
}, None));
71657220
}
71667221
},
@@ -12910,6 +12965,7 @@ where
1291012965
previous_hop_monitor.provide_payment_preimage(&payment_hash, &payment_preimage, &args.tx_broadcaster, &bounded_fee_estimator, &args.logger);
1291112966
}
1291212967
}
12968+
let payment_id = payment.inbound_payment_id(&inbound_payment_id_secret.unwrap());
1291312969
pending_events_read.push_back((events::Event::PaymentClaimed {
1291412970
receiver_node_id,
1291512971
payment_hash,
@@ -12918,6 +12974,7 @@ where
1291812974
htlcs: payment.htlcs.iter().map(events::ClaimedHTLC::from).collect(),
1291912975
sender_intended_total_msat: payment.htlcs.first().map(|htlc| htlc.total_msat),
1292012976
onion_fields: payment.onion_fields,
12977+
payment_id: Some(payment_id),
1292112978
}, None));
1292212979
}
1292312980
}

lightning/src/ln/msgs.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1729,8 +1729,7 @@ pub trait OnionMessageHandler {
17291729
fn provided_init_features(&self, their_node_id: PublicKey) -> InitFeatures;
17301730
}
17311731

1732-
#[derive(Clone)]
1733-
#[cfg_attr(test, derive(Debug, PartialEq))]
1732+
#[derive(Clone, Debug, PartialEq, Eq)]
17341733
/// Information communicated in the onion to the recipient for multi-part tracking and proof that
17351734
/// the payment is associated with an invoice.
17361735
pub struct FinalOnionHopData {

0 commit comments

Comments
 (0)