Skip to content

Commit 8d44e80

Browse files
authored
Merge pull request #3719 from vincenzopalazzo/macros/pop-with-persistance
[RFC] move the bolt12 invoice inside HTLCSource::OutboundRoute
2 parents c9fb114 + d6d1c2f commit 8d44e80

File tree

7 files changed

+57
-25
lines changed

7 files changed

+57
-25
lines changed

fuzz/src/process_onion_failure.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,13 @@ fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
114114

115115
let path = Path { hops, blinded_tail };
116116

117-
let htlc_source =
118-
HTLCSource::OutboundRoute { path, session_priv, first_hop_htlc_msat: 0, payment_id };
117+
let htlc_source = HTLCSource::OutboundRoute {
118+
path,
119+
session_priv,
120+
first_hop_htlc_msat: 0,
121+
payment_id,
122+
bolt12_invoice: None,
123+
};
119124

120125
let failure_len = get_u16!();
121126
let failure_data = get_slice!(failure_len);

lightning/src/events/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2518,7 +2518,7 @@ impl<T: EventHandler> EventHandler for Arc<T> {
25182518
}
25192519

25202520
/// The BOLT 12 invoice that was paid, surfaced in [`Event::PaymentSent::bolt12_invoice`].
2521-
#[derive(Clone, Debug, PartialEq, Eq)]
2521+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
25222522
pub enum PaidBolt12Invoice {
25232523
/// The BOLT 12 invoice specified by the BOLT 12 specification,
25242524
/// allowing the user to perform proof of payment.

lightning/src/ln/channel.rs

+2
Original file line numberDiff line numberDiff line change
@@ -11832,6 +11832,7 @@ mod tests {
1183211832
session_priv: SecretKey::from_slice(&<Vec<u8>>::from_hex("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(),
1183311833
first_hop_htlc_msat: 548,
1183411834
payment_id: PaymentId([42; 32]),
11835+
bolt12_invoice: None,
1183511836
},
1183611837
skimmed_fee_msat: None,
1183711838
blinding_point: None,
@@ -12210,6 +12211,7 @@ mod tests {
1221012211
session_priv: test_utils::privkey(42),
1221112212
first_hop_htlc_msat: 0,
1221212213
payment_id: PaymentId([42; 32]),
12214+
bolt12_invoice: None,
1221312215
};
1221412216
let dummy_outbound_output = OutboundHTLCOutput {
1221512217
htlc_id: 0,

lightning/src/ln/channelmanager.rs

+20-9
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use bitcoin::{secp256k1, Sequence};
3434
#[cfg(splicing)]
3535
use bitcoin::{TxIn, Weight};
3636

37-
use crate::events::FundingInfo;
37+
use crate::events::{FundingInfo, PaidBolt12Invoice};
3838
use crate::blinded_path::message::{AsyncPaymentsContext, MessageContext, OffersContext};
3939
use crate::blinded_path::NodeIdLookUp;
4040
use crate::blinded_path::message::{BlindedMessagePath, MessageForwardNode};
@@ -668,6 +668,10 @@ mod fuzzy_channelmanager {
668668
/// doing a double-pass on route when we get a failure back
669669
first_hop_htlc_msat: u64,
670670
payment_id: PaymentId,
671+
/// The BOLT12 invoice associated with this payment, if any. This is stored here to ensure
672+
/// we can provide proof-of-payment details in payment claim events even after a restart
673+
/// with a stale ChannelManager state.
674+
bolt12_invoice: Option<PaidBolt12Invoice>,
671675
},
672676
}
673677

@@ -705,12 +709,13 @@ impl core::hash::Hash for HTLCSource {
705709
0u8.hash(hasher);
706710
prev_hop_data.hash(hasher);
707711
},
708-
HTLCSource::OutboundRoute { path, session_priv, payment_id, first_hop_htlc_msat } => {
712+
HTLCSource::OutboundRoute { path, session_priv, payment_id, first_hop_htlc_msat, bolt12_invoice } => {
709713
1u8.hash(hasher);
710714
path.hash(hasher);
711715
session_priv[..].hash(hasher);
712716
payment_id.hash(hasher);
713717
first_hop_htlc_msat.hash(hasher);
718+
bolt12_invoice.hash(hasher);
714719
},
715720
}
716721
}
@@ -723,6 +728,7 @@ impl HTLCSource {
723728
session_priv: SecretKey::from_slice(&[1; 32]).unwrap(),
724729
first_hop_htlc_msat: 0,
725730
payment_id: PaymentId([2; 32]),
731+
bolt12_invoice: None,
726732
}
727733
}
728734

@@ -4630,14 +4636,14 @@ where
46304636
let _lck = self.total_consistency_lock.read().unwrap();
46314637
self.send_payment_along_path(SendAlongPathArgs {
46324638
path, payment_hash, recipient_onion: &recipient_onion, total_value,
4633-
cur_height, payment_id, keysend_preimage, invoice_request: None, session_priv_bytes
4639+
cur_height, payment_id, keysend_preimage, invoice_request: None, bolt12_invoice: None, session_priv_bytes
46344640
})
46354641
}
46364642

46374643
fn send_payment_along_path(&self, args: SendAlongPathArgs) -> Result<(), APIError> {
46384644
let SendAlongPathArgs {
46394645
path, payment_hash, recipient_onion, total_value, cur_height, payment_id, keysend_preimage,
4640-
invoice_request, session_priv_bytes
4646+
invoice_request, bolt12_invoice, session_priv_bytes
46414647
} = args;
46424648
// The top-level caller should hold the total_consistency_lock read lock.
46434649
debug_assert!(self.total_consistency_lock.try_write().is_err());
@@ -4687,6 +4693,7 @@ where
46874693
session_priv: session_priv.clone(),
46884694
first_hop_htlc_msat: htlc_msat,
46894695
payment_id,
4696+
bolt12_invoice: bolt12_invoice.cloned(),
46904697
}, onion_packet, None, &self.fee_estimator, &&logger);
46914698
match break_channel_entry!(self, peer_state, send_res, chan_entry) {
46924699
Some(monitor_update) => {
@@ -7451,15 +7458,15 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
74517458
next_channel_outpoint: OutPoint, next_channel_id: ChannelId, next_user_channel_id: Option<u128>,
74527459
) {
74537460
match source {
7454-
HTLCSource::OutboundRoute { session_priv, payment_id, path, .. } => {
7461+
HTLCSource::OutboundRoute { session_priv, payment_id, path, bolt12_invoice, .. } => {
74557462
debug_assert!(self.background_events_processed_since_startup.load(Ordering::Acquire),
74567463
"We don't support claim_htlc claims during startup - monitors may not be available yet");
74577464
debug_assert_eq!(next_channel_counterparty_node_id, path.hops[0].pubkey);
74587465
let ev_completion_action = EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
74597466
channel_funding_outpoint: next_channel_outpoint, channel_id: next_channel_id,
74607467
counterparty_node_id: path.hops[0].pubkey,
74617468
};
7462-
self.pending_outbound_payments.claim_htlc(payment_id, payment_preimage,
7469+
self.pending_outbound_payments.claim_htlc(payment_id, payment_preimage, bolt12_invoice,
74637470
session_priv, path, from_onchain, ev_completion_action, &self.pending_events,
74647471
&self.logger);
74657472
},
@@ -13151,13 +13158,15 @@ impl Readable for HTLCSource {
1315113158
let mut payment_id = None;
1315213159
let mut payment_params: Option<PaymentParameters> = None;
1315313160
let mut blinded_tail: Option<BlindedTail> = None;
13161+
let mut bolt12_invoice: Option<PaidBolt12Invoice> = None;
1315413162
read_tlv_fields!(reader, {
1315513163
(0, session_priv, required),
1315613164
(1, payment_id, option),
1315713165
(2, first_hop_htlc_msat, required),
1315813166
(4, path_hops, required_vec),
1315913167
(5, payment_params, (option: ReadableArgs, 0)),
1316013168
(6, blinded_tail, option),
13169+
(7, bolt12_invoice, option),
1316113170
});
1316213171
if payment_id.is_none() {
1316313172
// For backwards compat, if there was no payment_id written, use the session_priv bytes
@@ -13180,6 +13189,7 @@ impl Readable for HTLCSource {
1318013189
first_hop_htlc_msat,
1318113190
path,
1318213191
payment_id: payment_id.unwrap(),
13192+
bolt12_invoice,
1318313193
})
1318413194
}
1318513195
1 => Ok(HTLCSource::PreviousHopData(Readable::read(reader)?)),
@@ -13191,7 +13201,7 @@ impl Readable for HTLCSource {
1319113201
impl Writeable for HTLCSource {
1319213202
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), crate::io::Error> {
1319313203
match self {
13194-
HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id } => {
13204+
HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id, bolt12_invoice } => {
1319513205
0u8.write(writer)?;
1319613206
let payment_id_opt = Some(payment_id);
1319713207
write_tlv_fields!(writer, {
@@ -13202,6 +13212,7 @@ impl Writeable for HTLCSource {
1320213212
(4, path.hops, required_vec),
1320313213
(5, None::<PaymentParameters>, option), // payment_params in LDK versions prior to 0.0.115
1320413214
(6, path.blinded_tail, option),
13215+
(7, bolt12_invoice, option),
1320513216
});
1320613217
}
1320713218
HTLCSource::PreviousHopData(ref field) => {
@@ -14388,7 +14399,7 @@ where
1438814399
} else { true }
1438914400
});
1439014401
},
14391-
HTLCSource::OutboundRoute { payment_id, session_priv, path, .. } => {
14402+
HTLCSource::OutboundRoute { payment_id, session_priv, path, bolt12_invoice, .. } => {
1439214403
if let Some(preimage) = preimage_opt {
1439314404
let pending_events = Mutex::new(pending_events_read);
1439414405
// Note that we set `from_onchain` to "false" here,
@@ -14405,7 +14416,7 @@ where
1440514416
channel_id: monitor.channel_id(),
1440614417
counterparty_node_id: path.hops[0].pubkey,
1440714418
};
14408-
pending_outbounds.claim_htlc(payment_id, preimage, session_priv,
14419+
pending_outbounds.claim_htlc(payment_id, preimage, bolt12_invoice, session_priv,
1440914420
path, false, compl_action, &pending_events, &&logger);
1441014421
pending_events_read = pending_events.into_inner().unwrap();
1441114422
}

lightning/src/ln/onion_utils.rs

+4
Original file line numberDiff line numberDiff line change
@@ -3270,6 +3270,7 @@ mod tests {
32703270
session_priv: get_test_session_key(),
32713271
first_hop_htlc_msat: 0,
32723272
payment_id: PaymentId([1; 32]),
3273+
bolt12_invoice: None,
32733274
};
32743275

32753276
process_onion_failure(&ctx_full, &logger, &htlc_source, onion_error)
@@ -3397,6 +3398,7 @@ mod tests {
33973398
session_priv,
33983399
first_hop_htlc_msat: dummy_amt_msat,
33993400
payment_id: PaymentId([1; 32]),
3401+
bolt12_invoice: None,
34003402
};
34013403

34023404
{
@@ -3584,6 +3586,7 @@ mod tests {
35843586
session_priv: session_key,
35853587
first_hop_htlc_msat: 0,
35863588
payment_id: PaymentId([1; 32]),
3589+
bolt12_invoice: None,
35873590
};
35883591

35893592
// Iterate over all possible failure positions and check that the cases that can be attributed are.
@@ -3692,6 +3695,7 @@ mod tests {
36923695
session_priv: get_test_session_key(),
36933696
first_hop_htlc_msat: 0,
36943697
payment_id: PaymentId([1; 32]),
3698+
bolt12_invoice: None,
36953699
};
36963700

36973701
let decrypted_failure = process_onion_failure(&ctx_full, &logger, &htlc_source, packet);

lightning/src/ln/outbound_payment.rs

+17-13
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ impl PendingOutboundPayment {
163163
_ => None,
164164
}
165165
}
166+
166167
fn increment_attempts(&mut self) {
167168
if let PendingOutboundPayment::Retryable { attempts, .. } = self {
168169
attempts.count += 1;
@@ -797,6 +798,7 @@ pub(super) struct SendAlongPathArgs<'a> {
797798
pub payment_id: PaymentId,
798799
pub keysend_preimage: &'a Option<PaymentPreimage>,
799800
pub invoice_request: Option<&'a InvoiceRequest>,
801+
pub bolt12_invoice: Option<&'a PaidBolt12Invoice>,
800802
pub session_priv_bytes: [u8; 32],
801803
}
802804

@@ -1025,7 +1027,7 @@ impl OutboundPayments {
10251027
hash_map::Entry::Occupied(entry) => match entry.get() {
10261028
PendingOutboundPayment::InvoiceReceived { .. } => {
10271029
let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
1028-
payment_hash, recipient_onion.clone(), keysend_preimage, None, Some(bolt12_invoice), &route,
1030+
payment_hash, recipient_onion.clone(), keysend_preimage, None, Some(bolt12_invoice.clone()), &route,
10291031
Some(retry_strategy), payment_params, entropy_source, best_block_height,
10301032
);
10311033
*entry.into_mut() = retryable_payment;
@@ -1036,7 +1038,7 @@ impl OutboundPayments {
10361038
invoice_request
10371039
} else { unreachable!() };
10381040
let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
1039-
payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), Some(bolt12_invoice), &route,
1041+
payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), Some(bolt12_invoice.clone()), &route,
10401042
Some(retry_strategy), payment_params, entropy_source, best_block_height
10411043
);
10421044
outbounds.insert(payment_id, retryable_payment);
@@ -1049,7 +1051,7 @@ impl OutboundPayments {
10491051
core::mem::drop(outbounds);
10501052

10511053
let result = self.pay_route_internal(
1052-
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, payment_id,
1054+
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, Some(&bolt12_invoice), payment_id,
10531055
Some(route_params.final_value_msat), &onion_session_privs, node_signer, best_block_height,
10541056
&send_payment_along_path
10551057
);
@@ -1342,7 +1344,7 @@ impl OutboundPayments {
13421344
})?;
13431345

13441346
let res = self.pay_route_internal(&route, payment_hash, &recipient_onion,
1345-
keysend_preimage, None, payment_id, None, &onion_session_privs, node_signer,
1347+
keysend_preimage, None, None, payment_id, None, &onion_session_privs, node_signer,
13461348
best_block_height, &send_payment_along_path);
13471349
log_info!(logger, "Sending payment with id {} and hash {} returned {:?}",
13481350
payment_id, payment_hash, res);
@@ -1420,7 +1422,7 @@ impl OutboundPayments {
14201422
}
14211423
}
14221424
}
1423-
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request) = {
1425+
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice) = {
14241426
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
14251427
match outbounds.entry(payment_id) {
14261428
hash_map::Entry::Occupied(mut payment) => {
@@ -1462,8 +1464,9 @@ impl OutboundPayments {
14621464
}
14631465

14641466
payment.get_mut().increment_attempts();
1467+
let bolt12_invoice = payment.get().bolt12_invoice();
14651468

1466-
(total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request)
1469+
(total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice.cloned())
14671470
},
14681471
PendingOutboundPayment::Legacy { .. } => {
14691472
log_error!(logger, "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102");
@@ -1503,7 +1506,7 @@ impl OutboundPayments {
15031506
}
15041507
};
15051508
let res = self.pay_route_internal(&route, payment_hash, &recipient_onion, keysend_preimage,
1506-
invoice_request.as_ref(), payment_id, Some(total_msat), &onion_session_privs, node_signer,
1509+
invoice_request.as_ref(), bolt12_invoice.as_ref(), payment_id, Some(total_msat), &onion_session_privs, node_signer,
15071510
best_block_height, &send_payment_along_path);
15081511
log_info!(logger, "Result retrying payment id {}: {:?}", &payment_id, res);
15091512
if let Err(e) = res {
@@ -1656,7 +1659,7 @@ impl OutboundPayments {
16561659

16571660
let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
16581661
match self.pay_route_internal(&route, payment_hash, &recipient_onion_fields,
1659-
None, None, payment_id, None, &onion_session_privs, node_signer, best_block_height,
1662+
None, None, None, payment_id, None, &onion_session_privs, node_signer, best_block_height,
16601663
&send_payment_along_path
16611664
) {
16621665
Ok(()) => Ok((payment_hash, payment_id)),
@@ -1893,7 +1896,7 @@ impl OutboundPayments {
18931896

18941897
fn pay_route_internal<NS: Deref, F>(
18951898
&self, route: &Route, payment_hash: PaymentHash, recipient_onion: &RecipientOnionFields,
1896-
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>,
1899+
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>, bolt12_invoice: Option<&PaidBolt12Invoice>,
18971900
payment_id: PaymentId, recv_value_msat: Option<u64>, onion_session_privs: &Vec<[u8; 32]>,
18981901
node_signer: &NS, best_block_height: u32, send_payment_along_path: &F
18991902
) -> Result<(), PaymentSendFailure>
@@ -1949,6 +1952,7 @@ impl OutboundPayments {
19491952
let path_res = send_payment_along_path(SendAlongPathArgs {
19501953
path: &path, payment_hash: &payment_hash, recipient_onion, total_value,
19511954
cur_height, payment_id, keysend_preimage: &keysend_preimage, invoice_request,
1955+
bolt12_invoice,
19521956
session_priv_bytes: *session_priv_bytes
19531957
});
19541958
results.push(path_res);
@@ -2015,7 +2019,7 @@ impl OutboundPayments {
20152019
F: Fn(SendAlongPathArgs) -> Result<(), APIError>,
20162020
{
20172021
self.pay_route_internal(route, payment_hash, &recipient_onion,
2018-
keysend_preimage, None, payment_id, recv_value_msat, &onion_session_privs,
2022+
keysend_preimage, None, None, payment_id, recv_value_msat, &onion_session_privs,
20192023
node_signer, best_block_height, &send_payment_along_path)
20202024
.map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
20212025
}
@@ -2036,8 +2040,8 @@ impl OutboundPayments {
20362040
}
20372041

20382042
pub(super) fn claim_htlc<L: Deref>(
2039-
&self, payment_id: PaymentId, payment_preimage: PaymentPreimage, session_priv: SecretKey,
2040-
path: Path, from_onchain: bool, ev_completion_action: EventCompletionAction,
2043+
&self, payment_id: PaymentId, payment_preimage: PaymentPreimage, bolt12_invoice: Option<PaidBolt12Invoice>,
2044+
session_priv: SecretKey, path: Path, from_onchain: bool, ev_completion_action: EventCompletionAction,
20412045
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
20422046
logger: &L,
20432047
) where L::Target: Logger {
@@ -2057,7 +2061,7 @@ impl OutboundPayments {
20572061
payment_hash,
20582062
amount_msat,
20592063
fee_paid_msat,
2060-
bolt12_invoice: payment.get().bolt12_invoice().cloned(),
2064+
bolt12_invoice: bolt12_invoice,
20612065
}, Some(ev_completion_action.clone())));
20622066
payment.get_mut().mark_fulfilled();
20632067
}

lightning/src/offers/static_invoice.rs

+6
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ impl PartialEq for StaticInvoice {
8080

8181
impl Eq for StaticInvoice {}
8282

83+
impl core::hash::Hash for StaticInvoice {
84+
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
85+
self.bytes.hash(state);
86+
}
87+
}
88+
8389
/// The contents of a [`StaticInvoice`] for responding to an [`Offer`].
8490
///
8591
/// [`Offer`]: crate::offers::offer::Offer

0 commit comments

Comments
 (0)