Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions fuzz/src/process_onion_failure.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -114,8 +114,13 @@ fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {

let path = Path { hops, blinded_tail };

let htlc_source =
HTLCSource::OutboundRoute { path, session_priv, first_hop_htlc_msat: 0, payment_id };
let htlc_source = HTLCSource::OutboundRoute {
path,
session_priv,
first_hop_htlc_msat: 0,
payment_id,
bolt12_invoice: None,
};

let failure_len = get_u16!();
let failure_data = get_slice!(failure_len);
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2457,7 +2457,7 @@ impl<T: EventHandler> EventHandler for Arc<T> {
}

/// The BOLT 12 invoice that was paid, surfaced in [`Event::PaymentSent::bolt12_invoice`].
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PaidBolt12Invoice {
/// The BOLT 12 invoice specified by the BOLT 12 specification,
/// allowing the user to perform proof of payment.
Expand Down
2 changes: 2 additions & 0 deletions lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -11836,6 +11836,7 @@ mod tests {
session_priv: SecretKey::from_slice(&<Vec<u8>>::from_hex("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(),
first_hop_htlc_msat: 548,
payment_id: PaymentId([42; 32]),
bolt12_invoice: None,
},
skimmed_fee_msat: None,
blinding_point: None,
Expand DownExpand Up@@ -12214,6 +12215,7 @@ mod tests {
session_priv: test_utils::privkey(42),
first_hop_htlc_msat: 0,
payment_id: PaymentId([42; 32]),
bolt12_invoice: None,
};
let dummy_outbound_output = OutboundHTLCOutput {
htlc_id: 0,
Expand Down
29 changes: 20 additions & 9 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,7 +34,7 @@ use bitcoin::{secp256k1, Sequence};
#[cfg(splicing)]
use bitcoin::{TxIn, Weight};

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

Expand DownExpand Up@@ -705,12 +709,13 @@ impl core::hash::Hash for HTLCSource {
0u8.hash(hasher);
prev_hop_data.hash(hasher);
},
HTLCSource::OutboundRoute { path, session_priv, payment_id, first_hop_htlc_msat } => {
HTLCSource::OutboundRoute { path, session_priv, payment_id, first_hop_htlc_msat, bolt12_invoice } => {
1u8.hash(hasher);
path.hash(hasher);
session_priv[..].hash(hasher);
payment_id.hash(hasher);
first_hop_htlc_msat.hash(hasher);
bolt12_invoice.hash(hasher);
},
}
}
Expand All@@ -723,6 +728,7 @@ impl HTLCSource {
session_priv: SecretKey::from_slice(&[1; 32]).unwrap(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([2; 32]),
bolt12_invoice: None,
}
}

Expand DownExpand Up@@ -4629,14 +4635,14 @@ where
let _lck = self.total_consistency_lock.read().unwrap();
self.send_payment_along_path(SendAlongPathArgs {
path, payment_hash, recipient_onion: &recipient_onion, total_value,
cur_height, payment_id, keysend_preimage, invoice_request: None, session_priv_bytes
cur_height, payment_id, keysend_preimage, invoice_request: None, bolt12_invoice: None, session_priv_bytes
})
}

fn send_payment_along_path(&self, args: SendAlongPathArgs) -> Result<(), APIError> {
let SendAlongPathArgs {
path, payment_hash, recipient_onion, total_value, cur_height, payment_id, keysend_preimage,
invoice_request, session_priv_bytes
invoice_request, bolt12_invoice, session_priv_bytes
} = args;
// The top-level caller should hold the total_consistency_lock read lock.
debug_assert!(self.total_consistency_lock.try_write().is_err());
Expand DownExpand Up@@ -4686,6 +4692,7 @@ where
session_priv: session_priv.clone(),
first_hop_htlc_msat: htlc_msat,
payment_id,
bolt12_invoice: bolt12_invoice.cloned(),
}, onion_packet, None, &self.fee_estimator, &&logger);
match break_channel_entry!(self, peer_state, send_res, chan_entry) {
Some(monitor_update) => {
Expand DownExpand Up@@ -7447,15 +7454,15 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
next_channel_outpoint: OutPoint, next_channel_id: ChannelId, next_user_channel_id: Option<u128>,
) {
match source {
HTLCSource::OutboundRoute { session_priv, payment_id, path, .. } => {
HTLCSource::OutboundRoute { session_priv, payment_id, path, bolt12_invoice, .. } => {
debug_assert!(self.background_events_processed_since_startup.load(Ordering::Acquire),
"We don't support claim_htlc claims during startup - monitors may not be available yet");
debug_assert_eq!(next_channel_counterparty_node_id, path.hops[0].pubkey);
let ev_completion_action = EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
channel_funding_outpoint: next_channel_outpoint, channel_id: next_channel_id,
counterparty_node_id: path.hops[0].pubkey,
};
self.pending_outbound_payments.claim_htlc(payment_id, payment_preimage,
self.pending_outbound_payments.claim_htlc(payment_id, payment_preimage, bolt12_invoice,
session_priv, path, from_onchain, ev_completion_action, &self.pending_events,
&self.logger);
},
Expand DownExpand Up@@ -13131,13 +13138,15 @@ impl Readable for HTLCSource {
let mut payment_id = None;
let mut payment_params: Option<PaymentParameters> = None;
let mut blinded_tail: Option<BlindedTail> = None;
let mut bolt12_invoice: Option<PaidBolt12Invoice> = None;
read_tlv_fields!(reader, {
(0, session_priv, required),
(1, payment_id, option),
(2, first_hop_htlc_msat, required),
(4, path_hops, required_vec),
(5, payment_params, (option: ReadableArgs, 0)),
(6, blinded_tail, option),
(7, bolt12_invoice, option),
Comment thread
wpaulino marked this conversation as resolved.
});
if payment_id.is_none() {
// For backwards compat, if there was no payment_id written, use the session_priv bytes
Expand All@@ -13160,6 +13169,7 @@ impl Readable for HTLCSource {
first_hop_htlc_msat,
path,
payment_id: payment_id.unwrap(),
bolt12_invoice,
})
}
1 => Ok(HTLCSource::PreviousHopData(Readable::read(reader)?)),
Expand All@@ -13171,7 +13181,7 @@ impl Readable for HTLCSource {
impl Writeable for HTLCSource {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), crate::io::Error> {
match self {
HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id } => {
HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id, bolt12_invoice } => {
0u8.write(writer)?;
let payment_id_opt = Some(payment_id);
write_tlv_fields!(writer, {
Expand All@@ -13182,6 +13192,7 @@ impl Writeable for HTLCSource {
(4, path.hops, required_vec),
(5, None::<PaymentParameters>, option), // payment_params in LDK versions prior to 0.0.115
(6, path.blinded_tail, option),
(7, bolt12_invoice, option),
});
}
HTLCSource::PreviousHopData(ref field) => {
Expand DownExpand Up@@ -14368,7 +14379,7 @@ where
} else { true }
});
},
HTLCSource::OutboundRoute { payment_id, session_priv, path, .. } => {
HTLCSource::OutboundRoute { payment_id, session_priv, path, bolt12_invoice, .. } => {
if let Some(preimage) = preimage_opt {
let pending_events = Mutex::new(pending_events_read);
// Note that we set `from_onchain` to "false" here,
Expand All@@ -14385,7 +14396,7 @@ where
channel_id: monitor.channel_id(),
counterparty_node_id: path.hops[0].pubkey,
};
pending_outbounds.claim_htlc(payment_id, preimage, session_priv,
pending_outbounds.claim_htlc(payment_id, preimage, bolt12_invoice, session_priv,
path, false, compl_action, &pending_events, &&logger);
pending_events_read = pending_events.into_inner().unwrap();
}
Expand Down
4 changes: 4 additions & 0 deletions lightning/src/ln/onion_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -3233,6 +3233,7 @@ mod tests {
session_priv: get_test_session_key(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

process_onion_failure(&ctx_full, &logger, &htlc_source, onion_error)
Expand DownExpand Up@@ -3360,6 +3361,7 @@ mod tests {
session_priv,
first_hop_htlc_msat: dummy_amt_msat,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

{
Expand DownExpand Up@@ -3547,6 +3549,7 @@ mod tests {
session_priv: session_key,
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

// Iterate over all possible failure positions and check that the cases that can be attributed are.
Expand DownExpand Up@@ -3655,6 +3658,7 @@ mod tests {
session_priv: get_test_session_key(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

let decrypted_failure = process_onion_failure(&ctx_full, &logger, &htlc_source, packet);
Expand Down
30 changes: 17 additions & 13 deletions lightning/src/ln/outbound_payment.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -163,6 +163,7 @@ impl PendingOutboundPayment {
_ => None,
}
}

fn increment_attempts(&mut self) {
if let PendingOutboundPayment::Retryable { attempts, .. } = self {
attempts.count += 1;
Expand DownExpand Up@@ -797,6 +798,7 @@ pub(super) struct SendAlongPathArgs<'a> {
pub payment_id: PaymentId,
pub keysend_preimage: &'a Option<PaymentPreimage>,
pub invoice_request: Option<&'a InvoiceRequest>,
pub bolt12_invoice: Option<&'a PaidBolt12Invoice>,
pub session_priv_bytes: [u8; 32],
}

Expand DownExpand Up@@ -1042,7 +1044,7 @@ impl OutboundPayments {
hash_map::Entry::Occupied(entry) => match entry.get() {
PendingOutboundPayment::InvoiceReceived { .. } => {
let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, None, Some(bolt12_invoice), &route,
payment_hash, recipient_onion.clone(), keysend_preimage, None, Some(bolt12_invoice.clone()), &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height,
);
*entry.into_mut() = retryable_payment;
Expand All@@ -1053,7 +1055,7 @@ impl OutboundPayments {
invoice_request
} else { unreachable!() };
let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), Some(bolt12_invoice), &route,
payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), Some(bolt12_invoice.clone()), &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height
);
outbounds.insert(payment_id, retryable_payment);
Expand All@@ -1066,7 +1068,7 @@ impl OutboundPayments {
core::mem::drop(outbounds);

let result = self.pay_route_internal(
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, payment_id,
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, Some(&bolt12_invoice), payment_id,
Some(route_params.final_value_msat), &onion_session_privs, node_signer, best_block_height,
&send_payment_along_path
);
Expand DownExpand Up@@ -1359,7 +1361,7 @@ impl OutboundPayments {
})?;

let res = self.pay_route_internal(&route, payment_hash, &recipient_onion,
keysend_preimage, None, payment_id, None, &onion_session_privs, node_signer,
keysend_preimage, None, None, payment_id, None, &onion_session_privs, node_signer,
best_block_height, &send_payment_along_path);
log_info!(logger, "Sending payment with id {} and hash {} returned {:?}",
payment_id, payment_hash, res);
Expand DownExpand Up@@ -1437,7 +1439,7 @@ impl OutboundPayments {
}
}
}
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request) = {
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice) = {
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
match outbounds.entry(payment_id) {
hash_map::Entry::Occupied(mut payment) => {
Expand DownExpand Up@@ -1479,8 +1481,9 @@ impl OutboundPayments {
}

payment.get_mut().increment_attempts();
let bolt12_invoice = payment.get().bolt12_invoice();

(total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request)
(total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice.cloned())
},
PendingOutboundPayment::Legacy { .. } => {
log_error!(logger, "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102");
Expand DownExpand Up@@ -1520,7 +1523,7 @@ impl OutboundPayments {
}
};
let res = self.pay_route_internal(&route, payment_hash, &recipient_onion, keysend_preimage,
invoice_request.as_ref(), payment_id, Some(total_msat), &onion_session_privs, node_signer,
invoice_request.as_ref(), bolt12_invoice.as_ref(), payment_id, Some(total_msat), &onion_session_privs, node_signer,
best_block_height, &send_payment_along_path);
log_info!(logger, "Result retrying payment id {}: {:?}", &payment_id, res);
if let Err(e) = res {
Expand DownExpand Up@@ -1673,7 +1676,7 @@ impl OutboundPayments {

let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
match self.pay_route_internal(&route, payment_hash, &recipient_onion_fields,
None, None, payment_id, None, &onion_session_privs, node_signer, best_block_height,
None, None, None, payment_id, None, &onion_session_privs, node_signer, best_block_height,
&send_payment_along_path
) {
Ok(()) => Ok((payment_hash, payment_id)),
Expand DownExpand Up@@ -1865,7 +1868,7 @@ impl OutboundPayments {

fn pay_route_internal<NS: Deref, F>(
&self, route: &Route, payment_hash: PaymentHash, recipient_onion: &RecipientOnionFields,
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>,
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>, bolt12_invoice: Option<&PaidBolt12Invoice>,
payment_id: PaymentId, recv_value_msat: Option<u64>, onion_session_privs: &Vec<[u8; 32]>,
node_signer: &NS, best_block_height: u32, send_payment_along_path: &F
) -> Result<(), PaymentSendFailure>
Expand DownExpand Up@@ -1921,6 +1924,7 @@ impl OutboundPayments {
let path_res = send_payment_along_path(SendAlongPathArgs {
path: &path, payment_hash: &payment_hash, recipient_onion, total_value,
cur_height, payment_id, keysend_preimage: &keysend_preimage, invoice_request,
bolt12_invoice,
session_priv_bytes: *session_priv_bytes
});
results.push(path_res);
Expand DownExpand Up@@ -1987,7 +1991,7 @@ impl OutboundPayments {
F: Fn(SendAlongPathArgs) -> Result<(), APIError>,
{
self.pay_route_internal(route, payment_hash, &recipient_onion,
keysend_preimage, None, payment_id, recv_value_msat, &onion_session_privs,
keysend_preimage, None, None, payment_id, recv_value_msat, &onion_session_privs,
node_signer, best_block_height, &send_payment_along_path)
.map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
}
Expand All@@ -2008,8 +2012,8 @@ impl OutboundPayments {
}

pub(super) fn claim_htlc<L: Deref>(
&self, payment_id: PaymentId, payment_preimage: PaymentPreimage, session_priv: SecretKey,
path: Path, from_onchain: bool, ev_completion_action: EventCompletionAction,
&self, payment_id: PaymentId, payment_preimage: PaymentPreimage, bolt12_invoice: Option<PaidBolt12Invoice>,
session_priv: SecretKey, path: Path, from_onchain: bool, ev_completion_action: EventCompletionAction,
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
logger: &L,
) where L::Target: Logger {
Expand All@@ -2029,7 +2033,7 @@ impl OutboundPayments {
payment_hash,
amount_msat,
fee_paid_msat,
bolt12_invoice: payment.get().bolt12_invoice().cloned(),
bolt12_invoice: bolt12_invoice,
}, Some(ev_completion_action.clone())));
payment.get_mut().mark_fulfilled();
}
Expand Down
6 changes: 6 additions & 0 deletions lightning/src/offers/static_invoice.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -80,6 +80,12 @@ impl PartialEq for StaticInvoice {

impl Eq for StaticInvoice {}

impl core::hash::Hash for StaticInvoice {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.bytes.hash(state);
}
}

/// The contents of a [`StaticInvoice`] for responding to an [`Offer`].
///
/// [`Offer`]: crate::offers::offer::Offer
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions fuzz/src/process_onion_failure.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -114,8 +114,13 @@ fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {

let path = Path { hops, blinded_tail };

let htlc_source =
HTLCSource::OutboundRoute { path, session_priv, first_hop_htlc_msat: 0, payment_id };
let htlc_source = HTLCSource::OutboundRoute {
path,
session_priv,
first_hop_htlc_msat: 0,
payment_id,
bolt12_invoice: None,
};

let failure_len = get_u16!();
let failure_data = get_slice!(failure_len);
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2457,7 +2457,7 @@ impl<T: EventHandler> EventHandler for Arc<T> {
}

/// The BOLT 12 invoice that was paid, surfaced in [`Event::PaymentSent::bolt12_invoice`].
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PaidBolt12Invoice {
/// The BOLT 12 invoice specified by the BOLT 12 specification,
/// allowing the user to perform proof of payment.
Expand Down
2 changes: 2 additions & 0 deletions lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -11836,6 +11836,7 @@ mod tests {
session_priv: SecretKey::from_slice(&<Vec<u8>>::from_hex("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(),
first_hop_htlc_msat: 548,
payment_id: PaymentId([42; 32]),
bolt12_invoice: None,
},
skimmed_fee_msat: None,
blinding_point: None,
Expand DownExpand Up@@ -12214,6 +12215,7 @@ mod tests {
session_priv: test_utils::privkey(42),
first_hop_htlc_msat: 0,
payment_id: PaymentId([42; 32]),
bolt12_invoice: None,
};
let dummy_outbound_output = OutboundHTLCOutput {
htlc_id: 0,
Expand Down
29 changes: 20 additions & 9 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,7 +34,7 @@ use bitcoin::{secp256k1, Sequence};
#[cfg(splicing)]
use bitcoin::{TxIn, Weight};

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

Expand DownExpand Up@@ -705,12 +709,13 @@ impl core::hash::Hash for HTLCSource {
0u8.hash(hasher);
prev_hop_data.hash(hasher);
},
HTLCSource::OutboundRoute { path, session_priv, payment_id, first_hop_htlc_msat } => {
HTLCSource::OutboundRoute { path, session_priv, payment_id, first_hop_htlc_msat, bolt12_invoice } => {
1u8.hash(hasher);
path.hash(hasher);
session_priv[..].hash(hasher);
payment_id.hash(hasher);
first_hop_htlc_msat.hash(hasher);
bolt12_invoice.hash(hasher);
},
}
}
Expand All@@ -723,6 +728,7 @@ impl HTLCSource {
session_priv: SecretKey::from_slice(&[1; 32]).unwrap(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([2; 32]),
bolt12_invoice: None,
}
}

Expand DownExpand Up@@ -4629,14 +4635,14 @@ where
let _lck = self.total_consistency_lock.read().unwrap();
self.send_payment_along_path(SendAlongPathArgs {
path, payment_hash, recipient_onion: &recipient_onion, total_value,
cur_height, payment_id, keysend_preimage, invoice_request: None, session_priv_bytes
cur_height, payment_id, keysend_preimage, invoice_request: None, bolt12_invoice: None, session_priv_bytes
})
}

fn send_payment_along_path(&self, args: SendAlongPathArgs) -> Result<(), APIError> {
let SendAlongPathArgs {
path, payment_hash, recipient_onion, total_value, cur_height, payment_id, keysend_preimage,
invoice_request, session_priv_bytes
invoice_request, bolt12_invoice, session_priv_bytes
} = args;
// The top-level caller should hold the total_consistency_lock read lock.
debug_assert!(self.total_consistency_lock.try_write().is_err());
Expand DownExpand Up@@ -4686,6 +4692,7 @@ where
session_priv: session_priv.clone(),
first_hop_htlc_msat: htlc_msat,
payment_id,
bolt12_invoice: bolt12_invoice.cloned(),
}, onion_packet, None, &self.fee_estimator, &&logger);
match break_channel_entry!(self, peer_state, send_res, chan_entry) {
Some(monitor_update) => {
Expand DownExpand Up@@ -7447,15 +7454,15 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
next_channel_outpoint: OutPoint, next_channel_id: ChannelId, next_user_channel_id: Option<u128>,
) {
match source {
HTLCSource::OutboundRoute { session_priv, payment_id, path, .. } => {
HTLCSource::OutboundRoute { session_priv, payment_id, path, bolt12_invoice, .. } => {
debug_assert!(self.background_events_processed_since_startup.load(Ordering::Acquire),
"We don't support claim_htlc claims during startup - monitors may not be available yet");
debug_assert_eq!(next_channel_counterparty_node_id, path.hops[0].pubkey);
let ev_completion_action = EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
channel_funding_outpoint: next_channel_outpoint, channel_id: next_channel_id,
counterparty_node_id: path.hops[0].pubkey,
};
self.pending_outbound_payments.claim_htlc(payment_id, payment_preimage,
self.pending_outbound_payments.claim_htlc(payment_id, payment_preimage, bolt12_invoice,
session_priv, path, from_onchain, ev_completion_action, &self.pending_events,
&self.logger);
},
Expand DownExpand Up@@ -13131,13 +13138,15 @@ impl Readable for HTLCSource {
let mut payment_id = None;
let mut payment_params: Option<PaymentParameters> = None;
let mut blinded_tail: Option<BlindedTail> = None;
let mut bolt12_invoice: Option<PaidBolt12Invoice> = None;
read_tlv_fields!(reader, {
(0, session_priv, required),
(1, payment_id, option),
(2, first_hop_htlc_msat, required),
(4, path_hops, required_vec),
(5, payment_params, (option: ReadableArgs, 0)),
(6, blinded_tail, option),
(7, bolt12_invoice, option),
Comment thread
wpaulino marked this conversation as resolved.
});
if payment_id.is_none() {
// For backwards compat, if there was no payment_id written, use the session_priv bytes
Expand All@@ -13160,6 +13169,7 @@ impl Readable for HTLCSource {
first_hop_htlc_msat,
path,
payment_id: payment_id.unwrap(),
bolt12_invoice,
})
}
1 => Ok(HTLCSource::PreviousHopData(Readable::read(reader)?)),
Expand All@@ -13171,7 +13181,7 @@ impl Readable for HTLCSource {
impl Writeable for HTLCSource {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), crate::io::Error> {
match self {
HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id } => {
HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id, bolt12_invoice } => {
0u8.write(writer)?;
let payment_id_opt = Some(payment_id);
write_tlv_fields!(writer, {
Expand All@@ -13182,6 +13192,7 @@ impl Writeable for HTLCSource {
(4, path.hops, required_vec),
(5, None::<PaymentParameters>, option), // payment_params in LDK versions prior to 0.0.115
(6, path.blinded_tail, option),
(7, bolt12_invoice, option),
});
}
HTLCSource::PreviousHopData(ref field) => {
Expand DownExpand Up@@ -14368,7 +14379,7 @@ where
} else { true }
});
},
HTLCSource::OutboundRoute { payment_id, session_priv, path, .. } => {
HTLCSource::OutboundRoute { payment_id, session_priv, path, bolt12_invoice, .. } => {
if let Some(preimage) = preimage_opt {
let pending_events = Mutex::new(pending_events_read);
// Note that we set `from_onchain` to "false" here,
Expand All@@ -14385,7 +14396,7 @@ where
channel_id: monitor.channel_id(),
counterparty_node_id: path.hops[0].pubkey,
};
pending_outbounds.claim_htlc(payment_id, preimage, session_priv,
pending_outbounds.claim_htlc(payment_id, preimage, bolt12_invoice, session_priv,
path, false, compl_action, &pending_events, &&logger);
pending_events_read = pending_events.into_inner().unwrap();
}
Expand Down
4 changes: 4 additions & 0 deletions lightning/src/ln/onion_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -3233,6 +3233,7 @@ mod tests {
session_priv: get_test_session_key(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

process_onion_failure(&ctx_full, &logger, &htlc_source, onion_error)
Expand DownExpand Up@@ -3360,6 +3361,7 @@ mod tests {
session_priv,
first_hop_htlc_msat: dummy_amt_msat,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

{
Expand DownExpand Up@@ -3547,6 +3549,7 @@ mod tests {
session_priv: session_key,
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

// Iterate over all possible failure positions and check that the cases that can be attributed are.
Expand DownExpand Up@@ -3655,6 +3658,7 @@ mod tests {
session_priv: get_test_session_key(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

let decrypted_failure = process_onion_failure(&ctx_full, &logger, &htlc_source, packet);
Expand Down
30 changes: 17 additions & 13 deletions lightning/src/ln/outbound_payment.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -163,6 +163,7 @@ impl PendingOutboundPayment {
_ => None,
}
}

fn increment_attempts(&mut self) {
if let PendingOutboundPayment::Retryable { attempts, .. } = self {
attempts.count += 1;
Expand DownExpand Up@@ -797,6 +798,7 @@ pub(super) struct SendAlongPathArgs<'a> {
pub payment_id: PaymentId,
pub keysend_preimage: &'a Option<PaymentPreimage>,
pub invoice_request: Option<&'a InvoiceRequest>,
pub bolt12_invoice: Option<&'a PaidBolt12Invoice>,
pub session_priv_bytes: [u8; 32],
}

Expand DownExpand Up@@ -1042,7 +1044,7 @@ impl OutboundPayments {
hash_map::Entry::Occupied(entry) => match entry.get() {
PendingOutboundPayment::InvoiceReceived { .. } => {
let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, None, Some(bolt12_invoice), &route,
payment_hash, recipient_onion.clone(), keysend_preimage, None, Some(bolt12_invoice.clone()), &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height,
);
*entry.into_mut() = retryable_payment;
Expand All@@ -1053,7 +1055,7 @@ impl OutboundPayments {
invoice_request
} else { unreachable!() };
let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), Some(bolt12_invoice), &route,
payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), Some(bolt12_invoice.clone()), &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height
);
outbounds.insert(payment_id, retryable_payment);
Expand All@@ -1066,7 +1068,7 @@ impl OutboundPayments {
core::mem::drop(outbounds);

let result = self.pay_route_internal(
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, payment_id,
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, Some(&bolt12_invoice), payment_id,
Some(route_params.final_value_msat), &onion_session_privs, node_signer, best_block_height,
&send_payment_along_path
);
Expand DownExpand Up@@ -1359,7 +1361,7 @@ impl OutboundPayments {
})?;

let res = self.pay_route_internal(&route, payment_hash, &recipient_onion,
keysend_preimage, None, payment_id, None, &onion_session_privs, node_signer,
keysend_preimage, None, None, payment_id, None, &onion_session_privs, node_signer,
best_block_height, &send_payment_along_path);
log_info!(logger, "Sending payment with id {} and hash {} returned {:?}",
payment_id, payment_hash, res);
Expand DownExpand Up@@ -1437,7 +1439,7 @@ impl OutboundPayments {
}
}
}
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request) = {
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice) = {
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
match outbounds.entry(payment_id) {
hash_map::Entry::Occupied(mut payment) => {
Expand DownExpand Up@@ -1479,8 +1481,9 @@ impl OutboundPayments {
}

payment.get_mut().increment_attempts();
let bolt12_invoice = payment.get().bolt12_invoice();

(total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request)
(total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice.cloned())
},
PendingOutboundPayment::Legacy { .. } => {
log_error!(logger, "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102");
Expand DownExpand Up@@ -1520,7 +1523,7 @@ impl OutboundPayments {
}
};
let res = self.pay_route_internal(&route, payment_hash, &recipient_onion, keysend_preimage,
invoice_request.as_ref(), payment_id, Some(total_msat), &onion_session_privs, node_signer,
invoice_request.as_ref(), bolt12_invoice.as_ref(), payment_id, Some(total_msat), &onion_session_privs, node_signer,
best_block_height, &send_payment_along_path);
log_info!(logger, "Result retrying payment id {}: {:?}", &payment_id, res);
if let Err(e) = res {
Expand DownExpand Up@@ -1673,7 +1676,7 @@ impl OutboundPayments {

let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
match self.pay_route_internal(&route, payment_hash, &recipient_onion_fields,
None, None, payment_id, None, &onion_session_privs, node_signer, best_block_height,
None, None, None, payment_id, None, &onion_session_privs, node_signer, best_block_height,
&send_payment_along_path
) {
Ok(()) => Ok((payment_hash, payment_id)),
Expand DownExpand Up@@ -1865,7 +1868,7 @@ impl OutboundPayments {

fn pay_route_internal<NS: Deref, F>(
&self, route: &Route, payment_hash: PaymentHash, recipient_onion: &RecipientOnionFields,
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>,
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>, bolt12_invoice: Option<&PaidBolt12Invoice>,
payment_id: PaymentId, recv_value_msat: Option<u64>, onion_session_privs: &Vec<[u8; 32]>,
node_signer: &NS, best_block_height: u32, send_payment_along_path: &F
) -> Result<(), PaymentSendFailure>
Expand DownExpand Up@@ -1921,6 +1924,7 @@ impl OutboundPayments {
let path_res = send_payment_along_path(SendAlongPathArgs {
path: &path, payment_hash: &payment_hash, recipient_onion, total_value,
cur_height, payment_id, keysend_preimage: &keysend_preimage, invoice_request,
bolt12_invoice,
session_priv_bytes: *session_priv_bytes
});
results.push(path_res);
Expand DownExpand Up@@ -1987,7 +1991,7 @@ impl OutboundPayments {
F: Fn(SendAlongPathArgs) -> Result<(), APIError>,
{
self.pay_route_internal(route, payment_hash, &recipient_onion,
keysend_preimage, None, payment_id, recv_value_msat, &onion_session_privs,
keysend_preimage, None, None, payment_id, recv_value_msat, &onion_session_privs,
node_signer, best_block_height, &send_payment_along_path)
.map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
}
Expand All@@ -2008,8 +2012,8 @@ impl OutboundPayments {
}

pub(super) fn claim_htlc<L: Deref>(
&self, payment_id: PaymentId, payment_preimage: PaymentPreimage, session_priv: SecretKey,
path: Path, from_onchain: bool, ev_completion_action: EventCompletionAction,
&self, payment_id: PaymentId, payment_preimage: PaymentPreimage, bolt12_invoice: Option<PaidBolt12Invoice>,
session_priv: SecretKey, path: Path, from_onchain: bool, ev_completion_action: EventCompletionAction,
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
logger: &L,
) where L::Target: Logger {
Expand All@@ -2029,7 +2033,7 @@ impl OutboundPayments {
payment_hash,
amount_msat,
fee_paid_msat,
bolt12_invoice: payment.get().bolt12_invoice().cloned(),
bolt12_invoice: bolt12_invoice,
}, Some(ev_completion_action.clone())));
payment.get_mut().mark_fulfilled();
}
Expand Down
6 changes: 6 additions & 0 deletions lightning/src/offers/static_invoice.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -80,6 +80,12 @@ impl PartialEq for StaticInvoice {

impl Eq for StaticInvoice {}

impl core::hash::Hash for StaticInvoice {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.bytes.hash(state);
}
}

/// The contents of a [`StaticInvoice`] for responding to an [`Offer`].
///
/// [`Offer`]: crate::offers::offer::Offer
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions fuzz/src/process_onion_failure.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -114,8 +114,13 @@ fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {

let path = Path { hops, blinded_tail };

let htlc_source =
HTLCSource::OutboundRoute { path, session_priv, first_hop_htlc_msat: 0, payment_id };
let htlc_source = HTLCSource::OutboundRoute {
path,
session_priv,
first_hop_htlc_msat: 0,
payment_id,
bolt12_invoice: None,
};

let failure_len = get_u16!();
let failure_data = get_slice!(failure_len);
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2457,7 +2457,7 @@ impl<T: EventHandler> EventHandler for Arc<T> {
}

/// The BOLT 12 invoice that was paid, surfaced in [`Event::PaymentSent::bolt12_invoice`].
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PaidBolt12Invoice {
/// The BOLT 12 invoice specified by the BOLT 12 specification,
/// allowing the user to perform proof of payment.
Expand Down
2 changes: 2 additions & 0 deletions lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -11836,6 +11836,7 @@ mod tests {
session_priv: SecretKey::from_slice(&<Vec<u8>>::from_hex("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(),
first_hop_htlc_msat: 548,
payment_id: PaymentId([42; 32]),
bolt12_invoice: None,
},
skimmed_fee_msat: None,
blinding_point: None,
Expand DownExpand Up@@ -12214,6 +12215,7 @@ mod tests {
session_priv: test_utils::privkey(42),
first_hop_htlc_msat: 0,
payment_id: PaymentId([42; 32]),
bolt12_invoice: None,
};
let dummy_outbound_output = OutboundHTLCOutput {
htlc_id: 0,
Expand Down
29 changes: 20 additions & 9 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,7 +34,7 @@ use bitcoin::{secp256k1, Sequence};
#[cfg(splicing)]
use bitcoin::{TxIn, Weight};

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

Expand DownExpand Up@@ -705,12 +709,13 @@ impl core::hash::Hash for HTLCSource {
0u8.hash(hasher);
prev_hop_data.hash(hasher);
},
HTLCSource::OutboundRoute { path, session_priv, payment_id, first_hop_htlc_msat } => {
HTLCSource::OutboundRoute { path, session_priv, payment_id, first_hop_htlc_msat, bolt12_invoice } => {
1u8.hash(hasher);
path.hash(hasher);
session_priv[..].hash(hasher);
payment_id.hash(hasher);
first_hop_htlc_msat.hash(hasher);
bolt12_invoice.hash(hasher);
},
}
}
Expand All@@ -723,6 +728,7 @@ impl HTLCSource {
session_priv: SecretKey::from_slice(&[1; 32]).unwrap(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([2; 32]),
bolt12_invoice: None,
}
}

Expand DownExpand Up@@ -4629,14 +4635,14 @@ where
let _lck = self.total_consistency_lock.read().unwrap();
self.send_payment_along_path(SendAlongPathArgs {
path, payment_hash, recipient_onion: &recipient_onion, total_value,
cur_height, payment_id, keysend_preimage, invoice_request: None, session_priv_bytes
cur_height, payment_id, keysend_preimage, invoice_request: None, bolt12_invoice: None, session_priv_bytes
})
}

fn send_payment_along_path(&self, args: SendAlongPathArgs) -> Result<(), APIError> {
let SendAlongPathArgs {
path, payment_hash, recipient_onion, total_value, cur_height, payment_id, keysend_preimage,
invoice_request, session_priv_bytes
invoice_request, bolt12_invoice, session_priv_bytes
} = args;
// The top-level caller should hold the total_consistency_lock read lock.
debug_assert!(self.total_consistency_lock.try_write().is_err());
Expand DownExpand Up@@ -4686,6 +4692,7 @@ where
session_priv: session_priv.clone(),
first_hop_htlc_msat: htlc_msat,
payment_id,
bolt12_invoice: bolt12_invoice.cloned(),
}, onion_packet, None, &self.fee_estimator, &&logger);
match break_channel_entry!(self, peer_state, send_res, chan_entry) {
Some(monitor_update) => {
Expand DownExpand Up@@ -7447,15 +7454,15 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
next_channel_outpoint: OutPoint, next_channel_id: ChannelId, next_user_channel_id: Option<u128>,
) {
match source {
HTLCSource::OutboundRoute { session_priv, payment_id, path, .. } => {
HTLCSource::OutboundRoute { session_priv, payment_id, path, bolt12_invoice, .. } => {
debug_assert!(self.background_events_processed_since_startup.load(Ordering::Acquire),
"We don't support claim_htlc claims during startup - monitors may not be available yet");
debug_assert_eq!(next_channel_counterparty_node_id, path.hops[0].pubkey);
let ev_completion_action = EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
channel_funding_outpoint: next_channel_outpoint, channel_id: next_channel_id,
counterparty_node_id: path.hops[0].pubkey,
};
self.pending_outbound_payments.claim_htlc(payment_id, payment_preimage,
self.pending_outbound_payments.claim_htlc(payment_id, payment_preimage, bolt12_invoice,
session_priv, path, from_onchain, ev_completion_action, &self.pending_events,
&self.logger);
},
Expand DownExpand Up@@ -13131,13 +13138,15 @@ impl Readable for HTLCSource {
let mut payment_id = None;
let mut payment_params: Option<PaymentParameters> = None;
let mut blinded_tail: Option<BlindedTail> = None;
let mut bolt12_invoice: Option<PaidBolt12Invoice> = None;
read_tlv_fields!(reader, {
(0, session_priv, required),
(1, payment_id, option),
(2, first_hop_htlc_msat, required),
(4, path_hops, required_vec),
(5, payment_params, (option: ReadableArgs, 0)),
(6, blinded_tail, option),
(7, bolt12_invoice, option),
Comment thread
wpaulino marked this conversation as resolved.
});
if payment_id.is_none() {
// For backwards compat, if there was no payment_id written, use the session_priv bytes
Expand All@@ -13160,6 +13169,7 @@ impl Readable for HTLCSource {
first_hop_htlc_msat,
path,
payment_id: payment_id.unwrap(),
bolt12_invoice,
})
}
1 => Ok(HTLCSource::PreviousHopData(Readable::read(reader)?)),
Expand All@@ -13171,7 +13181,7 @@ impl Readable for HTLCSource {
impl Writeable for HTLCSource {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), crate::io::Error> {
match self {
HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id } => {
HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id, bolt12_invoice } => {
0u8.write(writer)?;
let payment_id_opt = Some(payment_id);
write_tlv_fields!(writer, {
Expand All@@ -13182,6 +13192,7 @@ impl Writeable for HTLCSource {
(4, path.hops, required_vec),
(5, None::<PaymentParameters>, option), // payment_params in LDK versions prior to 0.0.115
(6, path.blinded_tail, option),
(7, bolt12_invoice, option),
});
}
HTLCSource::PreviousHopData(ref field) => {
Expand DownExpand Up@@ -14368,7 +14379,7 @@ where
} else { true }
});
},
HTLCSource::OutboundRoute { payment_id, session_priv, path, .. } => {
HTLCSource::OutboundRoute { payment_id, session_priv, path, bolt12_invoice, .. } => {
if let Some(preimage) = preimage_opt {
let pending_events = Mutex::new(pending_events_read);
// Note that we set `from_onchain` to "false" here,
Expand All@@ -14385,7 +14396,7 @@ where
channel_id: monitor.channel_id(),
counterparty_node_id: path.hops[0].pubkey,
};
pending_outbounds.claim_htlc(payment_id, preimage, session_priv,
pending_outbounds.claim_htlc(payment_id, preimage, bolt12_invoice, session_priv,
path, false, compl_action, &pending_events, &&logger);
pending_events_read = pending_events.into_inner().unwrap();
}
Expand Down
4 changes: 4 additions & 0 deletions lightning/src/ln/onion_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -3233,6 +3233,7 @@ mod tests {
session_priv: get_test_session_key(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

process_onion_failure(&ctx_full, &logger, &htlc_source, onion_error)
Expand DownExpand Up@@ -3360,6 +3361,7 @@ mod tests {
session_priv,
first_hop_htlc_msat: dummy_amt_msat,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

{
Expand DownExpand Up@@ -3547,6 +3549,7 @@ mod tests {
session_priv: session_key,
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

// Iterate over all possible failure positions and check that the cases that can be attributed are.
Expand DownExpand Up@@ -3655,6 +3658,7 @@ mod tests {
session_priv: get_test_session_key(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

let decrypted_failure = process_onion_failure(&ctx_full, &logger, &htlc_source, packet);
Expand Down
30 changes: 17 additions & 13 deletions lightning/src/ln/outbound_payment.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -163,6 +163,7 @@ impl PendingOutboundPayment {
_ => None,
}
}

fn increment_attempts(&mut self) {
if let PendingOutboundPayment::Retryable { attempts, .. } = self {
attempts.count += 1;
Expand DownExpand Up@@ -797,6 +798,7 @@ pub(super) struct SendAlongPathArgs<'a> {
pub payment_id: PaymentId,
pub keysend_preimage: &'a Option<PaymentPreimage>,
pub invoice_request: Option<&'a InvoiceRequest>,
pub bolt12_invoice: Option<&'a PaidBolt12Invoice>,
pub session_priv_bytes: [u8; 32],
}

Expand DownExpand Up@@ -1042,7 +1044,7 @@ impl OutboundPayments {
hash_map::Entry::Occupied(entry) => match entry.get() {
PendingOutboundPayment::InvoiceReceived { .. } => {
let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, None, Some(bolt12_invoice), &route,
payment_hash, recipient_onion.clone(), keysend_preimage, None, Some(bolt12_invoice.clone()), &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height,
);
*entry.into_mut() = retryable_payment;
Expand All@@ -1053,7 +1055,7 @@ impl OutboundPayments {
invoice_request
} else { unreachable!() };
let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), Some(bolt12_invoice), &route,
payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), Some(bolt12_invoice.clone()), &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height
);
outbounds.insert(payment_id, retryable_payment);
Expand All@@ -1066,7 +1068,7 @@ impl OutboundPayments {
core::mem::drop(outbounds);

let result = self.pay_route_internal(
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, payment_id,
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, Some(&bolt12_invoice), payment_id,
Some(route_params.final_value_msat), &onion_session_privs, node_signer, best_block_height,
&send_payment_along_path
);
Expand DownExpand Up@@ -1359,7 +1361,7 @@ impl OutboundPayments {
})?;

let res = self.pay_route_internal(&route, payment_hash, &recipient_onion,
keysend_preimage, None, payment_id, None, &onion_session_privs, node_signer,
keysend_preimage, None, None, payment_id, None, &onion_session_privs, node_signer,
best_block_height, &send_payment_along_path);
log_info!(logger, "Sending payment with id {} and hash {} returned {:?}",
payment_id, payment_hash, res);
Expand DownExpand Up@@ -1437,7 +1439,7 @@ impl OutboundPayments {
}
}
}
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request) = {
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice) = {
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
match outbounds.entry(payment_id) {
hash_map::Entry::Occupied(mut payment) => {
Expand DownExpand Up@@ -1479,8 +1481,9 @@ impl OutboundPayments {
}

payment.get_mut().increment_attempts();
let bolt12_invoice = payment.get().bolt12_invoice();

(total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request)
(total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice.cloned())
},
PendingOutboundPayment::Legacy { .. } => {
log_error!(logger, "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102");
Expand DownExpand Up@@ -1520,7 +1523,7 @@ impl OutboundPayments {
}
};
let res = self.pay_route_internal(&route, payment_hash, &recipient_onion, keysend_preimage,
invoice_request.as_ref(), payment_id, Some(total_msat), &onion_session_privs, node_signer,
invoice_request.as_ref(), bolt12_invoice.as_ref(), payment_id, Some(total_msat), &onion_session_privs, node_signer,
best_block_height, &send_payment_along_path);
log_info!(logger, "Result retrying payment id {}: {:?}", &payment_id, res);
if let Err(e) = res {
Expand DownExpand Up@@ -1673,7 +1676,7 @@ impl OutboundPayments {

let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
match self.pay_route_internal(&route, payment_hash, &recipient_onion_fields,
None, None, payment_id, None, &onion_session_privs, node_signer, best_block_height,
None, None, None, payment_id, None, &onion_session_privs, node_signer, best_block_height,
&send_payment_along_path
) {
Ok(()) => Ok((payment_hash, payment_id)),
Expand DownExpand Up@@ -1865,7 +1868,7 @@ impl OutboundPayments {

fn pay_route_internal<NS: Deref, F>(
&self, route: &Route, payment_hash: PaymentHash, recipient_onion: &RecipientOnionFields,
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>,
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>, bolt12_invoice: Option<&PaidBolt12Invoice>,
payment_id: PaymentId, recv_value_msat: Option<u64>, onion_session_privs: &Vec<[u8; 32]>,
node_signer: &NS, best_block_height: u32, send_payment_along_path: &F
) -> Result<(), PaymentSendFailure>
Expand DownExpand Up@@ -1921,6 +1924,7 @@ impl OutboundPayments {
let path_res = send_payment_along_path(SendAlongPathArgs {
path: &path, payment_hash: &payment_hash, recipient_onion, total_value,
cur_height, payment_id, keysend_preimage: &keysend_preimage, invoice_request,
bolt12_invoice,
session_priv_bytes: *session_priv_bytes
});
results.push(path_res);
Expand DownExpand Up@@ -1987,7 +1991,7 @@ impl OutboundPayments {
F: Fn(SendAlongPathArgs) -> Result<(), APIError>,
{
self.pay_route_internal(route, payment_hash, &recipient_onion,
keysend_preimage, None, payment_id, recv_value_msat, &onion_session_privs,
keysend_preimage, None, None, payment_id, recv_value_msat, &onion_session_privs,
node_signer, best_block_height, &send_payment_along_path)
.map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
}
Expand All@@ -2008,8 +2012,8 @@ impl OutboundPayments {
}

pub(super) fn claim_htlc<L: Deref>(
&self, payment_id: PaymentId, payment_preimage: PaymentPreimage, session_priv: SecretKey,
path: Path, from_onchain: bool, ev_completion_action: EventCompletionAction,
&self, payment_id: PaymentId, payment_preimage: PaymentPreimage, bolt12_invoice: Option<PaidBolt12Invoice>,
session_priv: SecretKey, path: Path, from_onchain: bool, ev_completion_action: EventCompletionAction,
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
logger: &L,
) where L::Target: Logger {
Expand All@@ -2029,7 +2033,7 @@ impl OutboundPayments {
payment_hash,
amount_msat,
fee_paid_msat,
bolt12_invoice: payment.get().bolt12_invoice().cloned(),
bolt12_invoice: bolt12_invoice,
}, Some(ev_completion_action.clone())));
payment.get_mut().mark_fulfilled();
}
Expand Down
6 changes: 6 additions & 0 deletions lightning/src/offers/static_invoice.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -80,6 +80,12 @@ impl PartialEq for StaticInvoice {

impl Eq for StaticInvoice {}

impl core::hash::Hash for StaticInvoice {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.bytes.hash(state);
}
}

/// The contents of a [`StaticInvoice`] for responding to an [`Offer`].
///
/// [`Offer`]: crate::offers::offer::Offer
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions fuzz/src/process_onion_failure.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -114,8 +114,13 @@ fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {

let path = Path { hops, blinded_tail };

let htlc_source =
HTLCSource::OutboundRoute { path, session_priv, first_hop_htlc_msat: 0, payment_id };
let htlc_source = HTLCSource::OutboundRoute {
path,
session_priv,
first_hop_htlc_msat: 0,
payment_id,
bolt12_invoice: None,
};

let failure_len = get_u16!();
let failure_data = get_slice!(failure_len);
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2457,7 +2457,7 @@ impl<T: EventHandler> EventHandler for Arc<T> {
}

/// The BOLT 12 invoice that was paid, surfaced in [`Event::PaymentSent::bolt12_invoice`].
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PaidBolt12Invoice {
/// The BOLT 12 invoice specified by the BOLT 12 specification,
/// allowing the user to perform proof of payment.
Expand Down
2 changes: 2 additions & 0 deletions lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -11836,6 +11836,7 @@ mod tests {
session_priv: SecretKey::from_slice(&<Vec<u8>>::from_hex("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(),
first_hop_htlc_msat: 548,
payment_id: PaymentId([42; 32]),
bolt12_invoice: None,
},
skimmed_fee_msat: None,
blinding_point: None,
Expand DownExpand Up@@ -12214,6 +12215,7 @@ mod tests {
session_priv: test_utils::privkey(42),
first_hop_htlc_msat: 0,
payment_id: PaymentId([42; 32]),
bolt12_invoice: None,
};
let dummy_outbound_output = OutboundHTLCOutput {
htlc_id: 0,
Expand Down
29 changes: 20 additions & 9 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,7 +34,7 @@ use bitcoin::{secp256k1, Sequence};
#[cfg(splicing)]
use bitcoin::{TxIn, Weight};

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

Expand DownExpand Up@@ -705,12 +709,13 @@ impl core::hash::Hash for HTLCSource {
0u8.hash(hasher);
prev_hop_data.hash(hasher);
},
HTLCSource::OutboundRoute { path, session_priv, payment_id, first_hop_htlc_msat } => {
HTLCSource::OutboundRoute { path, session_priv, payment_id, first_hop_htlc_msat, bolt12_invoice } => {
1u8.hash(hasher);
path.hash(hasher);
session_priv[..].hash(hasher);
payment_id.hash(hasher);
first_hop_htlc_msat.hash(hasher);
bolt12_invoice.hash(hasher);
},
}
}
Expand All@@ -723,6 +728,7 @@ impl HTLCSource {
session_priv: SecretKey::from_slice(&[1; 32]).unwrap(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([2; 32]),
bolt12_invoice: None,
}
}

Expand DownExpand Up@@ -4629,14 +4635,14 @@ where
let _lck = self.total_consistency_lock.read().unwrap();
self.send_payment_along_path(SendAlongPathArgs {
path, payment_hash, recipient_onion: &recipient_onion, total_value,
cur_height, payment_id, keysend_preimage, invoice_request: None, session_priv_bytes
cur_height, payment_id, keysend_preimage, invoice_request: None, bolt12_invoice: None, session_priv_bytes
})
}

fn send_payment_along_path(&self, args: SendAlongPathArgs) -> Result<(), APIError> {
let SendAlongPathArgs {
path, payment_hash, recipient_onion, total_value, cur_height, payment_id, keysend_preimage,
invoice_request, session_priv_bytes
invoice_request, bolt12_invoice, session_priv_bytes
} = args;
// The top-level caller should hold the total_consistency_lock read lock.
debug_assert!(self.total_consistency_lock.try_write().is_err());
Expand DownExpand Up@@ -4686,6 +4692,7 @@ where
session_priv: session_priv.clone(),
first_hop_htlc_msat: htlc_msat,
payment_id,
bolt12_invoice: bolt12_invoice.cloned(),
}, onion_packet, None, &self.fee_estimator, &&logger);
match break_channel_entry!(self, peer_state, send_res, chan_entry) {
Some(monitor_update) => {
Expand DownExpand Up@@ -7447,15 +7454,15 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
next_channel_outpoint: OutPoint, next_channel_id: ChannelId, next_user_channel_id: Option<u128>,
) {
match source {
HTLCSource::OutboundRoute { session_priv, payment_id, path, .. } => {
HTLCSource::OutboundRoute { session_priv, payment_id, path, bolt12_invoice, .. } => {
debug_assert!(self.background_events_processed_since_startup.load(Ordering::Acquire),
"We don't support claim_htlc claims during startup - monitors may not be available yet");
debug_assert_eq!(next_channel_counterparty_node_id, path.hops[0].pubkey);
let ev_completion_action = EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
channel_funding_outpoint: next_channel_outpoint, channel_id: next_channel_id,
counterparty_node_id: path.hops[0].pubkey,
};
self.pending_outbound_payments.claim_htlc(payment_id, payment_preimage,
self.pending_outbound_payments.claim_htlc(payment_id, payment_preimage, bolt12_invoice,
session_priv, path, from_onchain, ev_completion_action, &self.pending_events,
&self.logger);
},
Expand DownExpand Up@@ -13131,13 +13138,15 @@ impl Readable for HTLCSource {
let mut payment_id = None;
let mut payment_params: Option<PaymentParameters> = None;
let mut blinded_tail: Option<BlindedTail> = None;
let mut bolt12_invoice: Option<PaidBolt12Invoice> = None;
read_tlv_fields!(reader, {
(0, session_priv, required),
(1, payment_id, option),
(2, first_hop_htlc_msat, required),
(4, path_hops, required_vec),
(5, payment_params, (option: ReadableArgs, 0)),
(6, blinded_tail, option),
(7, bolt12_invoice, option),
Comment thread
wpaulino marked this conversation as resolved.
});
if payment_id.is_none() {
// For backwards compat, if there was no payment_id written, use the session_priv bytes
Expand All@@ -13160,6 +13169,7 @@ impl Readable for HTLCSource {
first_hop_htlc_msat,
path,
payment_id: payment_id.unwrap(),
bolt12_invoice,
})
}
1 => Ok(HTLCSource::PreviousHopData(Readable::read(reader)?)),
Expand All@@ -13171,7 +13181,7 @@ impl Readable for HTLCSource {
impl Writeable for HTLCSource {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), crate::io::Error> {
match self {
HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id } => {
HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id, bolt12_invoice } => {
0u8.write(writer)?;
let payment_id_opt = Some(payment_id);
write_tlv_fields!(writer, {
Expand All@@ -13182,6 +13192,7 @@ impl Writeable for HTLCSource {
(4, path.hops, required_vec),
(5, None::<PaymentParameters>, option), // payment_params in LDK versions prior to 0.0.115
(6, path.blinded_tail, option),
(7, bolt12_invoice, option),
});
}
HTLCSource::PreviousHopData(ref field) => {
Expand DownExpand Up@@ -14368,7 +14379,7 @@ where
} else { true }
});
},
HTLCSource::OutboundRoute { payment_id, session_priv, path, .. } => {
HTLCSource::OutboundRoute { payment_id, session_priv, path, bolt12_invoice, .. } => {
if let Some(preimage) = preimage_opt {
let pending_events = Mutex::new(pending_events_read);
// Note that we set `from_onchain` to "false" here,
Expand All@@ -14385,7 +14396,7 @@ where
channel_id: monitor.channel_id(),
counterparty_node_id: path.hops[0].pubkey,
};
pending_outbounds.claim_htlc(payment_id, preimage, session_priv,
pending_outbounds.claim_htlc(payment_id, preimage, bolt12_invoice, session_priv,
path, false, compl_action, &pending_events, &&logger);
pending_events_read = pending_events.into_inner().unwrap();
}
Expand Down
4 changes: 4 additions & 0 deletions lightning/src/ln/onion_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -3233,6 +3233,7 @@ mod tests {
session_priv: get_test_session_key(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

process_onion_failure(&ctx_full, &logger, &htlc_source, onion_error)
Expand DownExpand Up@@ -3360,6 +3361,7 @@ mod tests {
session_priv,
first_hop_htlc_msat: dummy_amt_msat,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

{
Expand DownExpand Up@@ -3547,6 +3549,7 @@ mod tests {
session_priv: session_key,
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

// Iterate over all possible failure positions and check that the cases that can be attributed are.
Expand DownExpand Up@@ -3655,6 +3658,7 @@ mod tests {
session_priv: get_test_session_key(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

let decrypted_failure = process_onion_failure(&ctx_full, &logger, &htlc_source, packet);
Expand Down
30 changes: 17 additions & 13 deletions lightning/src/ln/outbound_payment.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -163,6 +163,7 @@ impl PendingOutboundPayment {
_ => None,
}
}

fn increment_attempts(&mut self) {
if let PendingOutboundPayment::Retryable { attempts, .. } = self {
attempts.count += 1;
Expand DownExpand Up@@ -797,6 +798,7 @@ pub(super) struct SendAlongPathArgs<'a> {
pub payment_id: PaymentId,
pub keysend_preimage: &'a Option<PaymentPreimage>,
pub invoice_request: Option<&'a InvoiceRequest>,
pub bolt12_invoice: Option<&'a PaidBolt12Invoice>,
pub session_priv_bytes: [u8; 32],
}

Expand DownExpand Up@@ -1042,7 +1044,7 @@ impl OutboundPayments {
hash_map::Entry::Occupied(entry) => match entry.get() {
PendingOutboundPayment::InvoiceReceived { .. } => {
let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, None, Some(bolt12_invoice), &route,
payment_hash, recipient_onion.clone(), keysend_preimage, None, Some(bolt12_invoice.clone()), &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height,
);
*entry.into_mut() = retryable_payment;
Expand All@@ -1053,7 +1055,7 @@ impl OutboundPayments {
invoice_request
} else { unreachable!() };
let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), Some(bolt12_invoice), &route,
payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), Some(bolt12_invoice.clone()), &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height
);
outbounds.insert(payment_id, retryable_payment);
Expand All@@ -1066,7 +1068,7 @@ impl OutboundPayments {
core::mem::drop(outbounds);

let result = self.pay_route_internal(
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, payment_id,
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, Some(&bolt12_invoice), payment_id,
Some(route_params.final_value_msat), &onion_session_privs, node_signer, best_block_height,
&send_payment_along_path
);
Expand DownExpand Up@@ -1359,7 +1361,7 @@ impl OutboundPayments {
})?;

let res = self.pay_route_internal(&route, payment_hash, &recipient_onion,
keysend_preimage, None, payment_id, None, &onion_session_privs, node_signer,
keysend_preimage, None, None, payment_id, None, &onion_session_privs, node_signer,
best_block_height, &send_payment_along_path);
log_info!(logger, "Sending payment with id {} and hash {} returned {:?}",
payment_id, payment_hash, res);
Expand DownExpand Up@@ -1437,7 +1439,7 @@ impl OutboundPayments {
}
}
}
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request) = {
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice) = {
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
match outbounds.entry(payment_id) {
hash_map::Entry::Occupied(mut payment) => {
Expand DownExpand Up@@ -1479,8 +1481,9 @@ impl OutboundPayments {
}

payment.get_mut().increment_attempts();
let bolt12_invoice = payment.get().bolt12_invoice();

(total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request)
(total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice.cloned())
},
PendingOutboundPayment::Legacy { .. } => {
log_error!(logger, "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102");
Expand DownExpand Up@@ -1520,7 +1523,7 @@ impl OutboundPayments {
}
};
let res = self.pay_route_internal(&route, payment_hash, &recipient_onion, keysend_preimage,
invoice_request.as_ref(), payment_id, Some(total_msat), &onion_session_privs, node_signer,
invoice_request.as_ref(), bolt12_invoice.as_ref(), payment_id, Some(total_msat), &onion_session_privs, node_signer,
best_block_height, &send_payment_along_path);
log_info!(logger, "Result retrying payment id {}: {:?}", &payment_id, res);
if let Err(e) = res {
Expand DownExpand Up@@ -1673,7 +1676,7 @@ impl OutboundPayments {

let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
match self.pay_route_internal(&route, payment_hash, &recipient_onion_fields,
None, None, payment_id, None, &onion_session_privs, node_signer, best_block_height,
None, None, None, payment_id, None, &onion_session_privs, node_signer, best_block_height,
&send_payment_along_path
) {
Ok(()) => Ok((payment_hash, payment_id)),
Expand DownExpand Up@@ -1865,7 +1868,7 @@ impl OutboundPayments {

fn pay_route_internal<NS: Deref, F>(
&self, route: &Route, payment_hash: PaymentHash, recipient_onion: &RecipientOnionFields,
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>,
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>, bolt12_invoice: Option<&PaidBolt12Invoice>,
payment_id: PaymentId, recv_value_msat: Option<u64>, onion_session_privs: &Vec<[u8; 32]>,
node_signer: &NS, best_block_height: u32, send_payment_along_path: &F
) -> Result<(), PaymentSendFailure>
Expand DownExpand Up@@ -1921,6 +1924,7 @@ impl OutboundPayments {
let path_res = send_payment_along_path(SendAlongPathArgs {
path: &path, payment_hash: &payment_hash, recipient_onion, total_value,
cur_height, payment_id, keysend_preimage: &keysend_preimage, invoice_request,
bolt12_invoice,
session_priv_bytes: *session_priv_bytes
});
results.push(path_res);
Expand DownExpand Up@@ -1987,7 +1991,7 @@ impl OutboundPayments {
F: Fn(SendAlongPathArgs) -> Result<(), APIError>,
{
self.pay_route_internal(route, payment_hash, &recipient_onion,
keysend_preimage, None, payment_id, recv_value_msat, &onion_session_privs,
keysend_preimage, None, None, payment_id, recv_value_msat, &onion_session_privs,
node_signer, best_block_height, &send_payment_along_path)
.map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
}
Expand All@@ -2008,8 +2012,8 @@ impl OutboundPayments {
}

pub(super) fn claim_htlc<L: Deref>(
&self, payment_id: PaymentId, payment_preimage: PaymentPreimage, session_priv: SecretKey,
path: Path, from_onchain: bool, ev_completion_action: EventCompletionAction,
&self, payment_id: PaymentId, payment_preimage: PaymentPreimage, bolt12_invoice: Option<PaidBolt12Invoice>,
session_priv: SecretKey, path: Path, from_onchain: bool, ev_completion_action: EventCompletionAction,
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
logger: &L,
) where L::Target: Logger {
Expand All@@ -2029,7 +2033,7 @@ impl OutboundPayments {
payment_hash,
amount_msat,
fee_paid_msat,
bolt12_invoice: payment.get().bolt12_invoice().cloned(),
bolt12_invoice: bolt12_invoice,
}, Some(ev_completion_action.clone())));
payment.get_mut().mark_fulfilled();
}
Expand Down
6 changes: 6 additions & 0 deletions lightning/src/offers/static_invoice.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -80,6 +80,12 @@ impl PartialEq for StaticInvoice {

impl Eq for StaticInvoice {}

impl core::hash::Hash for StaticInvoice {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.bytes.hash(state);
}
}

/// The contents of a [`StaticInvoice`] for responding to an [`Offer`].
///
/// [`Offer`]: crate::offers::offer::Offer
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions fuzz/src/process_onion_failure.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -114,8 +114,13 @@ fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {

let path = Path { hops, blinded_tail };

let htlc_source =
HTLCSource::OutboundRoute { path, session_priv, first_hop_htlc_msat: 0, payment_id };
let htlc_source = HTLCSource::OutboundRoute {
path,
session_priv,
first_hop_htlc_msat: 0,
payment_id,
bolt12_invoice: None,
};

let failure_len = get_u16!();
let failure_data = get_slice!(failure_len);
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2457,7 +2457,7 @@ impl<T: EventHandler> EventHandler for Arc<T> {
}

/// The BOLT 12 invoice that was paid, surfaced in [`Event::PaymentSent::bolt12_invoice`].
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PaidBolt12Invoice {
/// The BOLT 12 invoice specified by the BOLT 12 specification,
/// allowing the user to perform proof of payment.
Expand Down
2 changes: 2 additions & 0 deletions lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -11836,6 +11836,7 @@ mod tests {
session_priv: SecretKey::from_slice(&<Vec<u8>>::from_hex("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(),
first_hop_htlc_msat: 548,
payment_id: PaymentId([42; 32]),
bolt12_invoice: None,
},
skimmed_fee_msat: None,
blinding_point: None,
Expand DownExpand Up@@ -12214,6 +12215,7 @@ mod tests {
session_priv: test_utils::privkey(42),
first_hop_htlc_msat: 0,
payment_id: PaymentId([42; 32]),
bolt12_invoice: None,
};
let dummy_outbound_output = OutboundHTLCOutput {
htlc_id: 0,
Expand Down
29 changes: 20 additions & 9 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,7 +34,7 @@ use bitcoin::{secp256k1, Sequence};
#[cfg(splicing)]
use bitcoin::{TxIn, Weight};

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

Expand DownExpand Up@@ -705,12 +709,13 @@ impl core::hash::Hash for HTLCSource {
0u8.hash(hasher);
prev_hop_data.hash(hasher);
},
HTLCSource::OutboundRoute { path, session_priv, payment_id, first_hop_htlc_msat } => {
HTLCSource::OutboundRoute { path, session_priv, payment_id, first_hop_htlc_msat, bolt12_invoice } => {
1u8.hash(hasher);
path.hash(hasher);
session_priv[..].hash(hasher);
payment_id.hash(hasher);
first_hop_htlc_msat.hash(hasher);
bolt12_invoice.hash(hasher);
},
}
}
Expand All@@ -723,6 +728,7 @@ impl HTLCSource {
session_priv: SecretKey::from_slice(&[1; 32]).unwrap(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([2; 32]),
bolt12_invoice: None,
}
}

Expand DownExpand Up@@ -4629,14 +4635,14 @@ where
let _lck = self.total_consistency_lock.read().unwrap();
self.send_payment_along_path(SendAlongPathArgs {
path, payment_hash, recipient_onion: &recipient_onion, total_value,
cur_height, payment_id, keysend_preimage, invoice_request: None, session_priv_bytes
cur_height, payment_id, keysend_preimage, invoice_request: None, bolt12_invoice: None, session_priv_bytes
})
}

fn send_payment_along_path(&self, args: SendAlongPathArgs) -> Result<(), APIError> {
let SendAlongPathArgs {
path, payment_hash, recipient_onion, total_value, cur_height, payment_id, keysend_preimage,
invoice_request, session_priv_bytes
invoice_request, bolt12_invoice, session_priv_bytes
} = args;
// The top-level caller should hold the total_consistency_lock read lock.
debug_assert!(self.total_consistency_lock.try_write().is_err());
Expand DownExpand Up@@ -4686,6 +4692,7 @@ where
session_priv: session_priv.clone(),
first_hop_htlc_msat: htlc_msat,
payment_id,
bolt12_invoice: bolt12_invoice.cloned(),
}, onion_packet, None, &self.fee_estimator, &&logger);
match break_channel_entry!(self, peer_state, send_res, chan_entry) {
Some(monitor_update) => {
Expand DownExpand Up@@ -7447,15 +7454,15 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
next_channel_outpoint: OutPoint, next_channel_id: ChannelId, next_user_channel_id: Option<u128>,
) {
match source {
HTLCSource::OutboundRoute { session_priv, payment_id, path, .. } => {
HTLCSource::OutboundRoute { session_priv, payment_id, path, bolt12_invoice, .. } => {
debug_assert!(self.background_events_processed_since_startup.load(Ordering::Acquire),
"We don't support claim_htlc claims during startup - monitors may not be available yet");
debug_assert_eq!(next_channel_counterparty_node_id, path.hops[0].pubkey);
let ev_completion_action = EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
channel_funding_outpoint: next_channel_outpoint, channel_id: next_channel_id,
counterparty_node_id: path.hops[0].pubkey,
};
self.pending_outbound_payments.claim_htlc(payment_id, payment_preimage,
self.pending_outbound_payments.claim_htlc(payment_id, payment_preimage, bolt12_invoice,
session_priv, path, from_onchain, ev_completion_action, &self.pending_events,
&self.logger);
},
Expand DownExpand Up@@ -13131,13 +13138,15 @@ impl Readable for HTLCSource {
let mut payment_id = None;
let mut payment_params: Option<PaymentParameters> = None;
let mut blinded_tail: Option<BlindedTail> = None;
let mut bolt12_invoice: Option<PaidBolt12Invoice> = None;
read_tlv_fields!(reader, {
(0, session_priv, required),
(1, payment_id, option),
(2, first_hop_htlc_msat, required),
(4, path_hops, required_vec),
(5, payment_params, (option: ReadableArgs, 0)),
(6, blinded_tail, option),
(7, bolt12_invoice, option),
Comment thread
wpaulino marked this conversation as resolved.
});
if payment_id.is_none() {
// For backwards compat, if there was no payment_id written, use the session_priv bytes
Expand All@@ -13160,6 +13169,7 @@ impl Readable for HTLCSource {
first_hop_htlc_msat,
path,
payment_id: payment_id.unwrap(),
bolt12_invoice,
})
}
1 => Ok(HTLCSource::PreviousHopData(Readable::read(reader)?)),
Expand All@@ -13171,7 +13181,7 @@ impl Readable for HTLCSource {
impl Writeable for HTLCSource {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), crate::io::Error> {
match self {
HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id } => {
HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id, bolt12_invoice } => {
0u8.write(writer)?;
let payment_id_opt = Some(payment_id);
write_tlv_fields!(writer, {
Expand All@@ -13182,6 +13192,7 @@ impl Writeable for HTLCSource {
(4, path.hops, required_vec),
(5, None::<PaymentParameters>, option), // payment_params in LDK versions prior to 0.0.115
(6, path.blinded_tail, option),
(7, bolt12_invoice, option),
});
}
HTLCSource::PreviousHopData(ref field) => {
Expand DownExpand Up@@ -14368,7 +14379,7 @@ where
} else { true }
});
},
HTLCSource::OutboundRoute { payment_id, session_priv, path, .. } => {
HTLCSource::OutboundRoute { payment_id, session_priv, path, bolt12_invoice, .. } => {
if let Some(preimage) = preimage_opt {
let pending_events = Mutex::new(pending_events_read);
// Note that we set `from_onchain` to "false" here,
Expand All@@ -14385,7 +14396,7 @@ where
channel_id: monitor.channel_id(),
counterparty_node_id: path.hops[0].pubkey,
};
pending_outbounds.claim_htlc(payment_id, preimage, session_priv,
pending_outbounds.claim_htlc(payment_id, preimage, bolt12_invoice, session_priv,
path, false, compl_action, &pending_events, &&logger);
pending_events_read = pending_events.into_inner().unwrap();
}
Expand Down
4 changes: 4 additions & 0 deletions lightning/src/ln/onion_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -3233,6 +3233,7 @@ mod tests {
session_priv: get_test_session_key(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

process_onion_failure(&ctx_full, &logger, &htlc_source, onion_error)
Expand DownExpand Up@@ -3360,6 +3361,7 @@ mod tests {
session_priv,
first_hop_htlc_msat: dummy_amt_msat,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

{
Expand DownExpand Up@@ -3547,6 +3549,7 @@ mod tests {
session_priv: session_key,
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

// Iterate over all possible failure positions and check that the cases that can be attributed are.
Expand DownExpand Up@@ -3655,6 +3658,7 @@ mod tests {
session_priv: get_test_session_key(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

let decrypted_failure = process_onion_failure(&ctx_full, &logger, &htlc_source, packet);
Expand Down
30 changes: 17 additions & 13 deletions lightning/src/ln/outbound_payment.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -163,6 +163,7 @@ impl PendingOutboundPayment {
_ => None,
}
}

fn increment_attempts(&mut self) {
if let PendingOutboundPayment::Retryable { attempts, .. } = self {
attempts.count += 1;
Expand DownExpand Up@@ -797,6 +798,7 @@ pub(super) struct SendAlongPathArgs<'a> {
pub payment_id: PaymentId,
pub keysend_preimage: &'a Option<PaymentPreimage>,
pub invoice_request: Option<&'a InvoiceRequest>,
pub bolt12_invoice: Option<&'a PaidBolt12Invoice>,
pub session_priv_bytes: [u8; 32],
}

Expand DownExpand Up@@ -1042,7 +1044,7 @@ impl OutboundPayments {
hash_map::Entry::Occupied(entry) => match entry.get() {
PendingOutboundPayment::InvoiceReceived { .. } => {
let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, None, Some(bolt12_invoice), &route,
payment_hash, recipient_onion.clone(), keysend_preimage, None, Some(bolt12_invoice.clone()), &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height,
);
*entry.into_mut() = retryable_payment;
Expand All@@ -1053,7 +1055,7 @@ impl OutboundPayments {
invoice_request
} else { unreachable!() };
let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), Some(bolt12_invoice), &route,
payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), Some(bolt12_invoice.clone()), &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height
);
outbounds.insert(payment_id, retryable_payment);
Expand All@@ -1066,7 +1068,7 @@ impl OutboundPayments {
core::mem::drop(outbounds);

let result = self.pay_route_internal(
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, payment_id,
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, Some(&bolt12_invoice), payment_id,
Some(route_params.final_value_msat), &onion_session_privs, node_signer, best_block_height,
&send_payment_along_path
);
Expand DownExpand Up@@ -1359,7 +1361,7 @@ impl OutboundPayments {
})?;

let res = self.pay_route_internal(&route, payment_hash, &recipient_onion,
keysend_preimage, None, payment_id, None, &onion_session_privs, node_signer,
keysend_preimage, None, None, payment_id, None, &onion_session_privs, node_signer,
best_block_height, &send_payment_along_path);
log_info!(logger, "Sending payment with id {} and hash {} returned {:?}",
payment_id, payment_hash, res);
Expand DownExpand Up@@ -1437,7 +1439,7 @@ impl OutboundPayments {
}
}
}
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request) = {
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice) = {
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
match outbounds.entry(payment_id) {
hash_map::Entry::Occupied(mut payment) => {
Expand DownExpand Up@@ -1479,8 +1481,9 @@ impl OutboundPayments {
}

payment.get_mut().increment_attempts();
let bolt12_invoice = payment.get().bolt12_invoice();

(total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request)
(total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice.cloned())
},
PendingOutboundPayment::Legacy { .. } => {
log_error!(logger, "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102");
Expand DownExpand Up@@ -1520,7 +1523,7 @@ impl OutboundPayments {
}
};
let res = self.pay_route_internal(&route, payment_hash, &recipient_onion, keysend_preimage,
invoice_request.as_ref(), payment_id, Some(total_msat), &onion_session_privs, node_signer,
invoice_request.as_ref(), bolt12_invoice.as_ref(), payment_id, Some(total_msat), &onion_session_privs, node_signer,
best_block_height, &send_payment_along_path);
log_info!(logger, "Result retrying payment id {}: {:?}", &payment_id, res);
if let Err(e) = res {
Expand DownExpand Up@@ -1673,7 +1676,7 @@ impl OutboundPayments {

let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
match self.pay_route_internal(&route, payment_hash, &recipient_onion_fields,
None, None, payment_id, None, &onion_session_privs, node_signer, best_block_height,
None, None, None, payment_id, None, &onion_session_privs, node_signer, best_block_height,
&send_payment_along_path
) {
Ok(()) => Ok((payment_hash, payment_id)),
Expand DownExpand Up@@ -1865,7 +1868,7 @@ impl OutboundPayments {

fn pay_route_internal<NS: Deref, F>(
&self, route: &Route, payment_hash: PaymentHash, recipient_onion: &RecipientOnionFields,
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>,
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>, bolt12_invoice: Option<&PaidBolt12Invoice>,
payment_id: PaymentId, recv_value_msat: Option<u64>, onion_session_privs: &Vec<[u8; 32]>,
node_signer: &NS, best_block_height: u32, send_payment_along_path: &F
) -> Result<(), PaymentSendFailure>
Expand DownExpand Up@@ -1921,6 +1924,7 @@ impl OutboundPayments {
let path_res = send_payment_along_path(SendAlongPathArgs {
path: &path, payment_hash: &payment_hash, recipient_onion, total_value,
cur_height, payment_id, keysend_preimage: &keysend_preimage, invoice_request,
bolt12_invoice,
session_priv_bytes: *session_priv_bytes
});
results.push(path_res);
Expand DownExpand Up@@ -1987,7 +1991,7 @@ impl OutboundPayments {
F: Fn(SendAlongPathArgs) -> Result<(), APIError>,
{
self.pay_route_internal(route, payment_hash, &recipient_onion,
keysend_preimage, None, payment_id, recv_value_msat, &onion_session_privs,
keysend_preimage, None, None, payment_id, recv_value_msat, &onion_session_privs,
node_signer, best_block_height, &send_payment_along_path)
.map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
}
Expand All@@ -2008,8 +2012,8 @@ impl OutboundPayments {
}

pub(super) fn claim_htlc<L: Deref>(
&self, payment_id: PaymentId, payment_preimage: PaymentPreimage, session_priv: SecretKey,
path: Path, from_onchain: bool, ev_completion_action: EventCompletionAction,
&self, payment_id: PaymentId, payment_preimage: PaymentPreimage, bolt12_invoice: Option<PaidBolt12Invoice>,
session_priv: SecretKey, path: Path, from_onchain: bool, ev_completion_action: EventCompletionAction,
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
logger: &L,
) where L::Target: Logger {
Expand All@@ -2029,7 +2033,7 @@ impl OutboundPayments {
payment_hash,
amount_msat,
fee_paid_msat,
bolt12_invoice: payment.get().bolt12_invoice().cloned(),
bolt12_invoice: bolt12_invoice,
}, Some(ev_completion_action.clone())));
payment.get_mut().mark_fulfilled();
}
Expand Down
6 changes: 6 additions & 0 deletions lightning/src/offers/static_invoice.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -80,6 +80,12 @@ impl PartialEq for StaticInvoice {

impl Eq for StaticInvoice {}

impl core::hash::Hash for StaticInvoice {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.bytes.hash(state);
}
}

/// The contents of a [`StaticInvoice`] for responding to an [`Offer`].
///
/// [`Offer`]: crate::offers::offer::Offer
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions fuzz/src/process_onion_failure.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -114,8 +114,13 @@ fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {

let path = Path { hops, blinded_tail };

let htlc_source =
HTLCSource::OutboundRoute { path, session_priv, first_hop_htlc_msat: 0, payment_id };
let htlc_source = HTLCSource::OutboundRoute {
path,
session_priv,
first_hop_htlc_msat: 0,
payment_id,
bolt12_invoice: None,
};

let failure_len = get_u16!();
let failure_data = get_slice!(failure_len);
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2457,7 +2457,7 @@ impl<T: EventHandler> EventHandler for Arc<T> {
}

/// The BOLT 12 invoice that was paid, surfaced in [`Event::PaymentSent::bolt12_invoice`].
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PaidBolt12Invoice {
/// The BOLT 12 invoice specified by the BOLT 12 specification,
/// allowing the user to perform proof of payment.
Expand Down
2 changes: 2 additions & 0 deletions lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -11836,6 +11836,7 @@ mod tests {
session_priv: SecretKey::from_slice(&<Vec<u8>>::from_hex("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(),
first_hop_htlc_msat: 548,
payment_id: PaymentId([42; 32]),
bolt12_invoice: None,
},
skimmed_fee_msat: None,
blinding_point: None,
Expand DownExpand Up@@ -12214,6 +12215,7 @@ mod tests {
session_priv: test_utils::privkey(42),
first_hop_htlc_msat: 0,
payment_id: PaymentId([42; 32]),
bolt12_invoice: None,
};
let dummy_outbound_output = OutboundHTLCOutput {
htlc_id: 0,
Expand Down
29 changes: 20 additions & 9 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,7 +34,7 @@ use bitcoin::{secp256k1, Sequence};
#[cfg(splicing)]
use bitcoin::{TxIn, Weight};

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

Expand DownExpand Up@@ -705,12 +709,13 @@ impl core::hash::Hash for HTLCSource {
0u8.hash(hasher);
prev_hop_data.hash(hasher);
},
HTLCSource::OutboundRoute { path, session_priv, payment_id, first_hop_htlc_msat } => {
HTLCSource::OutboundRoute { path, session_priv, payment_id, first_hop_htlc_msat, bolt12_invoice } => {
1u8.hash(hasher);
path.hash(hasher);
session_priv[..].hash(hasher);
payment_id.hash(hasher);
first_hop_htlc_msat.hash(hasher);
bolt12_invoice.hash(hasher);
},
}
}
Expand All@@ -723,6 +728,7 @@ impl HTLCSource {
session_priv: SecretKey::from_slice(&[1; 32]).unwrap(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([2; 32]),
bolt12_invoice: None,
}
}

Expand DownExpand Up@@ -4629,14 +4635,14 @@ where
let _lck = self.total_consistency_lock.read().unwrap();
self.send_payment_along_path(SendAlongPathArgs {
path, payment_hash, recipient_onion: &recipient_onion, total_value,
cur_height, payment_id, keysend_preimage, invoice_request: None, session_priv_bytes
cur_height, payment_id, keysend_preimage, invoice_request: None, bolt12_invoice: None, session_priv_bytes
})
}

fn send_payment_along_path(&self, args: SendAlongPathArgs) -> Result<(), APIError> {
let SendAlongPathArgs {
path, payment_hash, recipient_onion, total_value, cur_height, payment_id, keysend_preimage,
invoice_request, session_priv_bytes
invoice_request, bolt12_invoice, session_priv_bytes
} = args;
// The top-level caller should hold the total_consistency_lock read lock.
debug_assert!(self.total_consistency_lock.try_write().is_err());
Expand DownExpand Up@@ -4686,6 +4692,7 @@ where
session_priv: session_priv.clone(),
first_hop_htlc_msat: htlc_msat,
payment_id,
bolt12_invoice: bolt12_invoice.cloned(),
}, onion_packet, None, &self.fee_estimator, &&logger);
match break_channel_entry!(self, peer_state, send_res, chan_entry) {
Some(monitor_update) => {
Expand DownExpand Up@@ -7447,15 +7454,15 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
next_channel_outpoint: OutPoint, next_channel_id: ChannelId, next_user_channel_id: Option<u128>,
) {
match source {
HTLCSource::OutboundRoute { session_priv, payment_id, path, .. } => {
HTLCSource::OutboundRoute { session_priv, payment_id, path, bolt12_invoice, .. } => {
debug_assert!(self.background_events_processed_since_startup.load(Ordering::Acquire),
"We don't support claim_htlc claims during startup - monitors may not be available yet");
debug_assert_eq!(next_channel_counterparty_node_id, path.hops[0].pubkey);
let ev_completion_action = EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
channel_funding_outpoint: next_channel_outpoint, channel_id: next_channel_id,
counterparty_node_id: path.hops[0].pubkey,
};
self.pending_outbound_payments.claim_htlc(payment_id, payment_preimage,
self.pending_outbound_payments.claim_htlc(payment_id, payment_preimage, bolt12_invoice,
session_priv, path, from_onchain, ev_completion_action, &self.pending_events,
&self.logger);
},
Expand DownExpand Up@@ -13131,13 +13138,15 @@ impl Readable for HTLCSource {
let mut payment_id = None;
let mut payment_params: Option<PaymentParameters> = None;
let mut blinded_tail: Option<BlindedTail> = None;
let mut bolt12_invoice: Option<PaidBolt12Invoice> = None;
read_tlv_fields!(reader, {
(0, session_priv, required),
(1, payment_id, option),
(2, first_hop_htlc_msat, required),
(4, path_hops, required_vec),
(5, payment_params, (option: ReadableArgs, 0)),
(6, blinded_tail, option),
(7, bolt12_invoice, option),
Comment thread
wpaulino marked this conversation as resolved.
});
if payment_id.is_none() {
// For backwards compat, if there was no payment_id written, use the session_priv bytes
Expand All@@ -13160,6 +13169,7 @@ impl Readable for HTLCSource {
first_hop_htlc_msat,
path,
payment_id: payment_id.unwrap(),
bolt12_invoice,
})
}
1 => Ok(HTLCSource::PreviousHopData(Readable::read(reader)?)),
Expand All@@ -13171,7 +13181,7 @@ impl Readable for HTLCSource {
impl Writeable for HTLCSource {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), crate::io::Error> {
match self {
HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id } => {
HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id, bolt12_invoice } => {
0u8.write(writer)?;
let payment_id_opt = Some(payment_id);
write_tlv_fields!(writer, {
Expand All@@ -13182,6 +13192,7 @@ impl Writeable for HTLCSource {
(4, path.hops, required_vec),
(5, None::<PaymentParameters>, option), // payment_params in LDK versions prior to 0.0.115
(6, path.blinded_tail, option),
(7, bolt12_invoice, option),
});
}
HTLCSource::PreviousHopData(ref field) => {
Expand DownExpand Up@@ -14368,7 +14379,7 @@ where
} else { true }
});
},
HTLCSource::OutboundRoute { payment_id, session_priv, path, .. } => {
HTLCSource::OutboundRoute { payment_id, session_priv, path, bolt12_invoice, .. } => {
if let Some(preimage) = preimage_opt {
let pending_events = Mutex::new(pending_events_read);
// Note that we set `from_onchain` to "false" here,
Expand All@@ -14385,7 +14396,7 @@ where
channel_id: monitor.channel_id(),
counterparty_node_id: path.hops[0].pubkey,
};
pending_outbounds.claim_htlc(payment_id, preimage, session_priv,
pending_outbounds.claim_htlc(payment_id, preimage, bolt12_invoice, session_priv,
path, false, compl_action, &pending_events, &&logger);
pending_events_read = pending_events.into_inner().unwrap();
}
Expand Down
4 changes: 4 additions & 0 deletions lightning/src/ln/onion_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -3233,6 +3233,7 @@ mod tests {
session_priv: get_test_session_key(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

process_onion_failure(&ctx_full, &logger, &htlc_source, onion_error)
Expand DownExpand Up@@ -3360,6 +3361,7 @@ mod tests {
session_priv,
first_hop_htlc_msat: dummy_amt_msat,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

{
Expand DownExpand Up@@ -3547,6 +3549,7 @@ mod tests {
session_priv: session_key,
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

// Iterate over all possible failure positions and check that the cases that can be attributed are.
Expand DownExpand Up@@ -3655,6 +3658,7 @@ mod tests {
session_priv: get_test_session_key(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

let decrypted_failure = process_onion_failure(&ctx_full, &logger, &htlc_source, packet);
Expand Down
30 changes: 17 additions & 13 deletions lightning/src/ln/outbound_payment.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -163,6 +163,7 @@ impl PendingOutboundPayment {
_ => None,
}
}

fn increment_attempts(&mut self) {
if let PendingOutboundPayment::Retryable { attempts, .. } = self {
attempts.count += 1;
Expand DownExpand Up@@ -797,6 +798,7 @@ pub(super) struct SendAlongPathArgs<'a> {
pub payment_id: PaymentId,
pub keysend_preimage: &'a Option<PaymentPreimage>,
pub invoice_request: Option<&'a InvoiceRequest>,
pub bolt12_invoice: Option<&'a PaidBolt12Invoice>,
pub session_priv_bytes: [u8; 32],
}

Expand DownExpand Up@@ -1042,7 +1044,7 @@ impl OutboundPayments {
hash_map::Entry::Occupied(entry) => match entry.get() {
PendingOutboundPayment::InvoiceReceived { .. } => {
let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, None, Some(bolt12_invoice), &route,
payment_hash, recipient_onion.clone(), keysend_preimage, None, Some(bolt12_invoice.clone()), &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height,
);
*entry.into_mut() = retryable_payment;
Expand All@@ -1053,7 +1055,7 @@ impl OutboundPayments {
invoice_request
} else { unreachable!() };
let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), Some(bolt12_invoice), &route,
payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), Some(bolt12_invoice.clone()), &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height
);
outbounds.insert(payment_id, retryable_payment);
Expand All@@ -1066,7 +1068,7 @@ impl OutboundPayments {
core::mem::drop(outbounds);

let result = self.pay_route_internal(
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, payment_id,
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, Some(&bolt12_invoice), payment_id,
Some(route_params.final_value_msat), &onion_session_privs, node_signer, best_block_height,
&send_payment_along_path
);
Expand DownExpand Up@@ -1359,7 +1361,7 @@ impl OutboundPayments {
})?;

let res = self.pay_route_internal(&route, payment_hash, &recipient_onion,
keysend_preimage, None, payment_id, None, &onion_session_privs, node_signer,
keysend_preimage, None, None, payment_id, None, &onion_session_privs, node_signer,
best_block_height, &send_payment_along_path);
log_info!(logger, "Sending payment with id {} and hash {} returned {:?}",
payment_id, payment_hash, res);
Expand DownExpand Up@@ -1437,7 +1439,7 @@ impl OutboundPayments {
}
}
}
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request) = {
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice) = {
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
match outbounds.entry(payment_id) {
hash_map::Entry::Occupied(mut payment) => {
Expand DownExpand Up@@ -1479,8 +1481,9 @@ impl OutboundPayments {
}

payment.get_mut().increment_attempts();
let bolt12_invoice = payment.get().bolt12_invoice();

(total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request)
(total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice.cloned())
},
PendingOutboundPayment::Legacy { .. } => {
log_error!(logger, "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102");
Expand DownExpand Up@@ -1520,7 +1523,7 @@ impl OutboundPayments {
}
};
let res = self.pay_route_internal(&route, payment_hash, &recipient_onion, keysend_preimage,
invoice_request.as_ref(), payment_id, Some(total_msat), &onion_session_privs, node_signer,
invoice_request.as_ref(), bolt12_invoice.as_ref(), payment_id, Some(total_msat), &onion_session_privs, node_signer,
best_block_height, &send_payment_along_path);
log_info!(logger, "Result retrying payment id {}: {:?}", &payment_id, res);
if let Err(e) = res {
Expand DownExpand Up@@ -1673,7 +1676,7 @@ impl OutboundPayments {

let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
match self.pay_route_internal(&route, payment_hash, &recipient_onion_fields,
None, None, payment_id, None, &onion_session_privs, node_signer, best_block_height,
None, None, None, payment_id, None, &onion_session_privs, node_signer, best_block_height,
&send_payment_along_path
) {
Ok(()) => Ok((payment_hash, payment_id)),
Expand DownExpand Up@@ -1865,7 +1868,7 @@ impl OutboundPayments {

fn pay_route_internal<NS: Deref, F>(
&self, route: &Route, payment_hash: PaymentHash, recipient_onion: &RecipientOnionFields,
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>,
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>, bolt12_invoice: Option<&PaidBolt12Invoice>,
payment_id: PaymentId, recv_value_msat: Option<u64>, onion_session_privs: &Vec<[u8; 32]>,
node_signer: &NS, best_block_height: u32, send_payment_along_path: &F
) -> Result<(), PaymentSendFailure>
Expand DownExpand Up@@ -1921,6 +1924,7 @@ impl OutboundPayments {
let path_res = send_payment_along_path(SendAlongPathArgs {
path: &path, payment_hash: &payment_hash, recipient_onion, total_value,
cur_height, payment_id, keysend_preimage: &keysend_preimage, invoice_request,
bolt12_invoice,
session_priv_bytes: *session_priv_bytes
});
results.push(path_res);
Expand DownExpand Up@@ -1987,7 +1991,7 @@ impl OutboundPayments {
F: Fn(SendAlongPathArgs) -> Result<(), APIError>,
{
self.pay_route_internal(route, payment_hash, &recipient_onion,
keysend_preimage, None, payment_id, recv_value_msat, &onion_session_privs,
keysend_preimage, None, None, payment_id, recv_value_msat, &onion_session_privs,
node_signer, best_block_height, &send_payment_along_path)
.map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
}
Expand All@@ -2008,8 +2012,8 @@ impl OutboundPayments {
}

pub(super) fn claim_htlc<L: Deref>(
&self, payment_id: PaymentId, payment_preimage: PaymentPreimage, session_priv: SecretKey,
path: Path, from_onchain: bool, ev_completion_action: EventCompletionAction,
&self, payment_id: PaymentId, payment_preimage: PaymentPreimage, bolt12_invoice: Option<PaidBolt12Invoice>,
session_priv: SecretKey, path: Path, from_onchain: bool, ev_completion_action: EventCompletionAction,
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
logger: &L,
) where L::Target: Logger {
Expand All@@ -2029,7 +2033,7 @@ impl OutboundPayments {
payment_hash,
amount_msat,
fee_paid_msat,
bolt12_invoice: payment.get().bolt12_invoice().cloned(),
bolt12_invoice: bolt12_invoice,
}, Some(ev_completion_action.clone())));
payment.get_mut().mark_fulfilled();
}
Expand Down
6 changes: 6 additions & 0 deletions lightning/src/offers/static_invoice.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -80,6 +80,12 @@ impl PartialEq for StaticInvoice {

impl Eq for StaticInvoice {}

impl core::hash::Hash for StaticInvoice {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.bytes.hash(state);
}
}

/// The contents of a [`StaticInvoice`] for responding to an [`Offer`].
///
/// [`Offer`]: crate::offers::offer::Offer
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions fuzz/src/process_onion_failure.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -114,8 +114,13 @@ fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {

let path = Path { hops, blinded_tail };

let htlc_source =
HTLCSource::OutboundRoute { path, session_priv, first_hop_htlc_msat: 0, payment_id };
let htlc_source = HTLCSource::OutboundRoute {
path,
session_priv,
first_hop_htlc_msat: 0,
payment_id,
bolt12_invoice: None,
};

let failure_len = get_u16!();
let failure_data = get_slice!(failure_len);
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2457,7 +2457,7 @@ impl<T: EventHandler> EventHandler for Arc<T> {
}

/// The BOLT 12 invoice that was paid, surfaced in [`Event::PaymentSent::bolt12_invoice`].
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PaidBolt12Invoice {
/// The BOLT 12 invoice specified by the BOLT 12 specification,
/// allowing the user to perform proof of payment.
Expand Down
2 changes: 2 additions & 0 deletions lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -11836,6 +11836,7 @@ mod tests {
session_priv: SecretKey::from_slice(&<Vec<u8>>::from_hex("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(),
first_hop_htlc_msat: 548,
payment_id: PaymentId([42; 32]),
bolt12_invoice: None,
},
skimmed_fee_msat: None,
blinding_point: None,
Expand DownExpand Up@@ -12214,6 +12215,7 @@ mod tests {
session_priv: test_utils::privkey(42),
first_hop_htlc_msat: 0,
payment_id: PaymentId([42; 32]),
bolt12_invoice: None,
};
let dummy_outbound_output = OutboundHTLCOutput {
htlc_id: 0,
Expand Down
29 changes: 20 additions & 9 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,7 +34,7 @@ use bitcoin::{secp256k1, Sequence};
#[cfg(splicing)]
use bitcoin::{TxIn, Weight};

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

Expand DownExpand Up@@ -705,12 +709,13 @@ impl core::hash::Hash for HTLCSource {
0u8.hash(hasher);
prev_hop_data.hash(hasher);
},
HTLCSource::OutboundRoute { path, session_priv, payment_id, first_hop_htlc_msat } => {
HTLCSource::OutboundRoute { path, session_priv, payment_id, first_hop_htlc_msat, bolt12_invoice } => {
1u8.hash(hasher);
path.hash(hasher);
session_priv[..].hash(hasher);
payment_id.hash(hasher);
first_hop_htlc_msat.hash(hasher);
bolt12_invoice.hash(hasher);
},
}
}
Expand All@@ -723,6 +728,7 @@ impl HTLCSource {
session_priv: SecretKey::from_slice(&[1; 32]).unwrap(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([2; 32]),
bolt12_invoice: None,
}
}

Expand DownExpand Up@@ -4629,14 +4635,14 @@ where
let _lck = self.total_consistency_lock.read().unwrap();
self.send_payment_along_path(SendAlongPathArgs {
path, payment_hash, recipient_onion: &recipient_onion, total_value,
cur_height, payment_id, keysend_preimage, invoice_request: None, session_priv_bytes
cur_height, payment_id, keysend_preimage, invoice_request: None, bolt12_invoice: None, session_priv_bytes
})
}

fn send_payment_along_path(&self, args: SendAlongPathArgs) -> Result<(), APIError> {
let SendAlongPathArgs {
path, payment_hash, recipient_onion, total_value, cur_height, payment_id, keysend_preimage,
invoice_request, session_priv_bytes
invoice_request, bolt12_invoice, session_priv_bytes
} = args;
// The top-level caller should hold the total_consistency_lock read lock.
debug_assert!(self.total_consistency_lock.try_write().is_err());
Expand DownExpand Up@@ -4686,6 +4692,7 @@ where
session_priv: session_priv.clone(),
first_hop_htlc_msat: htlc_msat,
payment_id,
bolt12_invoice: bolt12_invoice.cloned(),
}, onion_packet, None, &self.fee_estimator, &&logger);
match break_channel_entry!(self, peer_state, send_res, chan_entry) {
Some(monitor_update) => {
Expand DownExpand Up@@ -7447,15 +7454,15 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
next_channel_outpoint: OutPoint, next_channel_id: ChannelId, next_user_channel_id: Option<u128>,
) {
match source {
HTLCSource::OutboundRoute { session_priv, payment_id, path, .. } => {
HTLCSource::OutboundRoute { session_priv, payment_id, path, bolt12_invoice, .. } => {
debug_assert!(self.background_events_processed_since_startup.load(Ordering::Acquire),
"We don't support claim_htlc claims during startup - monitors may not be available yet");
debug_assert_eq!(next_channel_counterparty_node_id, path.hops[0].pubkey);
let ev_completion_action = EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
channel_funding_outpoint: next_channel_outpoint, channel_id: next_channel_id,
counterparty_node_id: path.hops[0].pubkey,
};
self.pending_outbound_payments.claim_htlc(payment_id, payment_preimage,
self.pending_outbound_payments.claim_htlc(payment_id, payment_preimage, bolt12_invoice,
session_priv, path, from_onchain, ev_completion_action, &self.pending_events,
&self.logger);
},
Expand DownExpand Up@@ -13131,13 +13138,15 @@ impl Readable for HTLCSource {
let mut payment_id = None;
let mut payment_params: Option<PaymentParameters> = None;
let mut blinded_tail: Option<BlindedTail> = None;
let mut bolt12_invoice: Option<PaidBolt12Invoice> = None;
read_tlv_fields!(reader, {
(0, session_priv, required),
(1, payment_id, option),
(2, first_hop_htlc_msat, required),
(4, path_hops, required_vec),
(5, payment_params, (option: ReadableArgs, 0)),
(6, blinded_tail, option),
(7, bolt12_invoice, option),
Comment thread
wpaulino marked this conversation as resolved.
});
if payment_id.is_none() {
// For backwards compat, if there was no payment_id written, use the session_priv bytes
Expand All@@ -13160,6 +13169,7 @@ impl Readable for HTLCSource {
first_hop_htlc_msat,
path,
payment_id: payment_id.unwrap(),
bolt12_invoice,
})
}
1 => Ok(HTLCSource::PreviousHopData(Readable::read(reader)?)),
Expand All@@ -13171,7 +13181,7 @@ impl Readable for HTLCSource {
impl Writeable for HTLCSource {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), crate::io::Error> {
match self {
HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id } => {
HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id, bolt12_invoice } => {
0u8.write(writer)?;
let payment_id_opt = Some(payment_id);
write_tlv_fields!(writer, {
Expand All@@ -13182,6 +13192,7 @@ impl Writeable for HTLCSource {
(4, path.hops, required_vec),
(5, None::<PaymentParameters>, option), // payment_params in LDK versions prior to 0.0.115
(6, path.blinded_tail, option),
(7, bolt12_invoice, option),
});
}
HTLCSource::PreviousHopData(ref field) => {
Expand DownExpand Up@@ -14368,7 +14379,7 @@ where
} else { true }
});
},
HTLCSource::OutboundRoute { payment_id, session_priv, path, .. } => {
HTLCSource::OutboundRoute { payment_id, session_priv, path, bolt12_invoice, .. } => {
if let Some(preimage) = preimage_opt {
let pending_events = Mutex::new(pending_events_read);
// Note that we set `from_onchain` to "false" here,
Expand All@@ -14385,7 +14396,7 @@ where
channel_id: monitor.channel_id(),
counterparty_node_id: path.hops[0].pubkey,
};
pending_outbounds.claim_htlc(payment_id, preimage, session_priv,
pending_outbounds.claim_htlc(payment_id, preimage, bolt12_invoice, session_priv,
path, false, compl_action, &pending_events, &&logger);
pending_events_read = pending_events.into_inner().unwrap();
}
Expand Down
4 changes: 4 additions & 0 deletions lightning/src/ln/onion_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -3233,6 +3233,7 @@ mod tests {
session_priv: get_test_session_key(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

process_onion_failure(&ctx_full, &logger, &htlc_source, onion_error)
Expand DownExpand Up@@ -3360,6 +3361,7 @@ mod tests {
session_priv,
first_hop_htlc_msat: dummy_amt_msat,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

{
Expand DownExpand Up@@ -3547,6 +3549,7 @@ mod tests {
session_priv: session_key,
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

// Iterate over all possible failure positions and check that the cases that can be attributed are.
Expand DownExpand Up@@ -3655,6 +3658,7 @@ mod tests {
session_priv: get_test_session_key(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

let decrypted_failure = process_onion_failure(&ctx_full, &logger, &htlc_source, packet);
Expand Down
30 changes: 17 additions & 13 deletions lightning/src/ln/outbound_payment.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -163,6 +163,7 @@ impl PendingOutboundPayment {
_ => None,
}
}

fn increment_attempts(&mut self) {
if let PendingOutboundPayment::Retryable { attempts, .. } = self {
attempts.count += 1;
Expand DownExpand Up@@ -797,6 +798,7 @@ pub(super) struct SendAlongPathArgs<'a> {
pub payment_id: PaymentId,
pub keysend_preimage: &'a Option<PaymentPreimage>,
pub invoice_request: Option<&'a InvoiceRequest>,
pub bolt12_invoice: Option<&'a PaidBolt12Invoice>,
pub session_priv_bytes: [u8; 32],
}

Expand DownExpand Up@@ -1042,7 +1044,7 @@ impl OutboundPayments {
hash_map::Entry::Occupied(entry) => match entry.get() {
PendingOutboundPayment::InvoiceReceived { .. } => {
let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, None, Some(bolt12_invoice), &route,
payment_hash, recipient_onion.clone(), keysend_preimage, None, Some(bolt12_invoice.clone()), &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height,
);
*entry.into_mut() = retryable_payment;
Expand All@@ -1053,7 +1055,7 @@ impl OutboundPayments {
invoice_request
} else { unreachable!() };
let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), Some(bolt12_invoice), &route,
payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), Some(bolt12_invoice.clone()), &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height
);
outbounds.insert(payment_id, retryable_payment);
Expand All@@ -1066,7 +1068,7 @@ impl OutboundPayments {
core::mem::drop(outbounds);

let result = self.pay_route_internal(
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, payment_id,
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, Some(&bolt12_invoice), payment_id,
Some(route_params.final_value_msat), &onion_session_privs, node_signer, best_block_height,
&send_payment_along_path
);
Expand DownExpand Up@@ -1359,7 +1361,7 @@ impl OutboundPayments {
})?;

let res = self.pay_route_internal(&route, payment_hash, &recipient_onion,
keysend_preimage, None, payment_id, None, &onion_session_privs, node_signer,
keysend_preimage, None, None, payment_id, None, &onion_session_privs, node_signer,
best_block_height, &send_payment_along_path);
log_info!(logger, "Sending payment with id {} and hash {} returned {:?}",
payment_id, payment_hash, res);
Expand DownExpand Up@@ -1437,7 +1439,7 @@ impl OutboundPayments {
}
}
}
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request) = {
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice) = {
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
match outbounds.entry(payment_id) {
hash_map::Entry::Occupied(mut payment) => {
Expand DownExpand Up@@ -1479,8 +1481,9 @@ impl OutboundPayments {
}

payment.get_mut().increment_attempts();
let bolt12_invoice = payment.get().bolt12_invoice();

(total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request)
(total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice.cloned())
},
PendingOutboundPayment::Legacy { .. } => {
log_error!(logger, "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102");
Expand DownExpand Up@@ -1520,7 +1523,7 @@ impl OutboundPayments {
}
};
let res = self.pay_route_internal(&route, payment_hash, &recipient_onion, keysend_preimage,
invoice_request.as_ref(), payment_id, Some(total_msat), &onion_session_privs, node_signer,
invoice_request.as_ref(), bolt12_invoice.as_ref(), payment_id, Some(total_msat), &onion_session_privs, node_signer,
best_block_height, &send_payment_along_path);
log_info!(logger, "Result retrying payment id {}: {:?}", &payment_id, res);
if let Err(e) = res {
Expand DownExpand Up@@ -1673,7 +1676,7 @@ impl OutboundPayments {

let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
match self.pay_route_internal(&route, payment_hash, &recipient_onion_fields,
None, None, payment_id, None, &onion_session_privs, node_signer, best_block_height,
None, None, None, payment_id, None, &onion_session_privs, node_signer, best_block_height,
&send_payment_along_path
) {
Ok(()) => Ok((payment_hash, payment_id)),
Expand DownExpand Up@@ -1865,7 +1868,7 @@ impl OutboundPayments {

fn pay_route_internal<NS: Deref, F>(
&self, route: &Route, payment_hash: PaymentHash, recipient_onion: &RecipientOnionFields,
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>,
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>, bolt12_invoice: Option<&PaidBolt12Invoice>,
payment_id: PaymentId, recv_value_msat: Option<u64>, onion_session_privs: &Vec<[u8; 32]>,
node_signer: &NS, best_block_height: u32, send_payment_along_path: &F
) -> Result<(), PaymentSendFailure>
Expand DownExpand Up@@ -1921,6 +1924,7 @@ impl OutboundPayments {
let path_res = send_payment_along_path(SendAlongPathArgs {
path: &path, payment_hash: &payment_hash, recipient_onion, total_value,
cur_height, payment_id, keysend_preimage: &keysend_preimage, invoice_request,
bolt12_invoice,
session_priv_bytes: *session_priv_bytes
});
results.push(path_res);
Expand DownExpand Up@@ -1987,7 +1991,7 @@ impl OutboundPayments {
F: Fn(SendAlongPathArgs) -> Result<(), APIError>,
{
self.pay_route_internal(route, payment_hash, &recipient_onion,
keysend_preimage, None, payment_id, recv_value_msat, &onion_session_privs,
keysend_preimage, None, None, payment_id, recv_value_msat, &onion_session_privs,
node_signer, best_block_height, &send_payment_along_path)
.map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
}
Expand All@@ -2008,8 +2012,8 @@ impl OutboundPayments {
}

pub(super) fn claim_htlc<L: Deref>(
&self, payment_id: PaymentId, payment_preimage: PaymentPreimage, session_priv: SecretKey,
path: Path, from_onchain: bool, ev_completion_action: EventCompletionAction,
&self, payment_id: PaymentId, payment_preimage: PaymentPreimage, bolt12_invoice: Option<PaidBolt12Invoice>,
session_priv: SecretKey, path: Path, from_onchain: bool, ev_completion_action: EventCompletionAction,
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
logger: &L,
) where L::Target: Logger {
Expand All@@ -2029,7 +2033,7 @@ impl OutboundPayments {
payment_hash,
amount_msat,
fee_paid_msat,
bolt12_invoice: payment.get().bolt12_invoice().cloned(),
bolt12_invoice: bolt12_invoice,
}, Some(ev_completion_action.clone())));
payment.get_mut().mark_fulfilled();
}
Expand Down
6 changes: 6 additions & 0 deletions lightning/src/offers/static_invoice.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -80,6 +80,12 @@ impl PartialEq for StaticInvoice {

impl Eq for StaticInvoice {}

impl core::hash::Hash for StaticInvoice {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.bytes.hash(state);
}
}

/// The contents of a [`StaticInvoice`] for responding to an [`Offer`].
///
/// [`Offer`]: crate::offers::offer::Offer
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions fuzz/src/process_onion_failure.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -114,8 +114,13 @@ fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {

let path = Path { hops, blinded_tail };

let htlc_source =
HTLCSource::OutboundRoute { path, session_priv, first_hop_htlc_msat: 0, payment_id };
let htlc_source = HTLCSource::OutboundRoute {
path,
session_priv,
first_hop_htlc_msat: 0,
payment_id,
bolt12_invoice: None,
};

let failure_len = get_u16!();
let failure_data = get_slice!(failure_len);
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2457,7 +2457,7 @@ impl<T: EventHandler> EventHandler for Arc<T> {
}

/// The BOLT 12 invoice that was paid, surfaced in [`Event::PaymentSent::bolt12_invoice`].
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PaidBolt12Invoice {
/// The BOLT 12 invoice specified by the BOLT 12 specification,
/// allowing the user to perform proof of payment.
Expand Down
2 changes: 2 additions & 0 deletions lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -11836,6 +11836,7 @@ mod tests {
session_priv: SecretKey::from_slice(&<Vec<u8>>::from_hex("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(),
first_hop_htlc_msat: 548,
payment_id: PaymentId([42; 32]),
bolt12_invoice: None,
},
skimmed_fee_msat: None,
blinding_point: None,
Expand DownExpand Up@@ -12214,6 +12215,7 @@ mod tests {
session_priv: test_utils::privkey(42),
first_hop_htlc_msat: 0,
payment_id: PaymentId([42; 32]),
bolt12_invoice: None,
};
let dummy_outbound_output = OutboundHTLCOutput {
htlc_id: 0,
Expand Down
29 changes: 20 additions & 9 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,7 +34,7 @@ use bitcoin::{secp256k1, Sequence};
#[cfg(splicing)]
use bitcoin::{TxIn, Weight};

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

Expand DownExpand Up@@ -705,12 +709,13 @@ impl core::hash::Hash for HTLCSource {
0u8.hash(hasher);
prev_hop_data.hash(hasher);
},
HTLCSource::OutboundRoute { path, session_priv, payment_id, first_hop_htlc_msat } => {
HTLCSource::OutboundRoute { path, session_priv, payment_id, first_hop_htlc_msat, bolt12_invoice } => {
1u8.hash(hasher);
path.hash(hasher);
session_priv[..].hash(hasher);
payment_id.hash(hasher);
first_hop_htlc_msat.hash(hasher);
bolt12_invoice.hash(hasher);
},
}
}
Expand All@@ -723,6 +728,7 @@ impl HTLCSource {
session_priv: SecretKey::from_slice(&[1; 32]).unwrap(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([2; 32]),
bolt12_invoice: None,
}
}

Expand DownExpand Up@@ -4629,14 +4635,14 @@ where
let _lck = self.total_consistency_lock.read().unwrap();
self.send_payment_along_path(SendAlongPathArgs {
path, payment_hash, recipient_onion: &recipient_onion, total_value,
cur_height, payment_id, keysend_preimage, invoice_request: None, session_priv_bytes
cur_height, payment_id, keysend_preimage, invoice_request: None, bolt12_invoice: None, session_priv_bytes
})
}

fn send_payment_along_path(&self, args: SendAlongPathArgs) -> Result<(), APIError> {
let SendAlongPathArgs {
path, payment_hash, recipient_onion, total_value, cur_height, payment_id, keysend_preimage,
invoice_request, session_priv_bytes
invoice_request, bolt12_invoice, session_priv_bytes
} = args;
// The top-level caller should hold the total_consistency_lock read lock.
debug_assert!(self.total_consistency_lock.try_write().is_err());
Expand DownExpand Up@@ -4686,6 +4692,7 @@ where
session_priv: session_priv.clone(),
first_hop_htlc_msat: htlc_msat,
payment_id,
bolt12_invoice: bolt12_invoice.cloned(),
}, onion_packet, None, &self.fee_estimator, &&logger);
match break_channel_entry!(self, peer_state, send_res, chan_entry) {
Some(monitor_update) => {
Expand DownExpand Up@@ -7447,15 +7454,15 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
next_channel_outpoint: OutPoint, next_channel_id: ChannelId, next_user_channel_id: Option<u128>,
) {
match source {
HTLCSource::OutboundRoute { session_priv, payment_id, path, .. } => {
HTLCSource::OutboundRoute { session_priv, payment_id, path, bolt12_invoice, .. } => {
debug_assert!(self.background_events_processed_since_startup.load(Ordering::Acquire),
"We don't support claim_htlc claims during startup - monitors may not be available yet");
debug_assert_eq!(next_channel_counterparty_node_id, path.hops[0].pubkey);
let ev_completion_action = EventCompletionAction::ReleaseRAAChannelMonitorUpdate {
channel_funding_outpoint: next_channel_outpoint, channel_id: next_channel_id,
counterparty_node_id: path.hops[0].pubkey,
};
self.pending_outbound_payments.claim_htlc(payment_id, payment_preimage,
self.pending_outbound_payments.claim_htlc(payment_id, payment_preimage, bolt12_invoice,
session_priv, path, from_onchain, ev_completion_action, &self.pending_events,
&self.logger);
},
Expand DownExpand Up@@ -13131,13 +13138,15 @@ impl Readable for HTLCSource {
let mut payment_id = None;
let mut payment_params: Option<PaymentParameters> = None;
let mut blinded_tail: Option<BlindedTail> = None;
let mut bolt12_invoice: Option<PaidBolt12Invoice> = None;
read_tlv_fields!(reader, {
(0, session_priv, required),
(1, payment_id, option),
(2, first_hop_htlc_msat, required),
(4, path_hops, required_vec),
(5, payment_params, (option: ReadableArgs, 0)),
(6, blinded_tail, option),
(7, bolt12_invoice, option),
Comment thread
wpaulino marked this conversation as resolved.
});
if payment_id.is_none() {
// For backwards compat, if there was no payment_id written, use the session_priv bytes
Expand All@@ -13160,6 +13169,7 @@ impl Readable for HTLCSource {
first_hop_htlc_msat,
path,
payment_id: payment_id.unwrap(),
bolt12_invoice,
})
}
1 => Ok(HTLCSource::PreviousHopData(Readable::read(reader)?)),
Expand All@@ -13171,7 +13181,7 @@ impl Readable for HTLCSource {
impl Writeable for HTLCSource {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), crate::io::Error> {
match self {
HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id } => {
HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, payment_id, bolt12_invoice } => {
0u8.write(writer)?;
let payment_id_opt = Some(payment_id);
write_tlv_fields!(writer, {
Expand All@@ -13182,6 +13192,7 @@ impl Writeable for HTLCSource {
(4, path.hops, required_vec),
(5, None::<PaymentParameters>, option), // payment_params in LDK versions prior to 0.0.115
(6, path.blinded_tail, option),
(7, bolt12_invoice, option),
});
}
HTLCSource::PreviousHopData(ref field) => {
Expand DownExpand Up@@ -14368,7 +14379,7 @@ where
} else { true }
});
},
HTLCSource::OutboundRoute { payment_id, session_priv, path, .. } => {
HTLCSource::OutboundRoute { payment_id, session_priv, path, bolt12_invoice, .. } => {
if let Some(preimage) = preimage_opt {
let pending_events = Mutex::new(pending_events_read);
// Note that we set `from_onchain` to "false" here,
Expand All@@ -14385,7 +14396,7 @@ where
channel_id: monitor.channel_id(),
counterparty_node_id: path.hops[0].pubkey,
};
pending_outbounds.claim_htlc(payment_id, preimage, session_priv,
pending_outbounds.claim_htlc(payment_id, preimage, bolt12_invoice, session_priv,
path, false, compl_action, &pending_events, &&logger);
pending_events_read = pending_events.into_inner().unwrap();
}
Expand Down
4 changes: 4 additions & 0 deletions lightning/src/ln/onion_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -3233,6 +3233,7 @@ mod tests {
session_priv: get_test_session_key(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

process_onion_failure(&ctx_full, &logger, &htlc_source, onion_error)
Expand DownExpand Up@@ -3360,6 +3361,7 @@ mod tests {
session_priv,
first_hop_htlc_msat: dummy_amt_msat,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

{
Expand DownExpand Up@@ -3547,6 +3549,7 @@ mod tests {
session_priv: session_key,
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

// Iterate over all possible failure positions and check that the cases that can be attributed are.
Expand DownExpand Up@@ -3655,6 +3658,7 @@ mod tests {
session_priv: get_test_session_key(),
first_hop_htlc_msat: 0,
payment_id: PaymentId([1; 32]),
bolt12_invoice: None,
};

let decrypted_failure = process_onion_failure(&ctx_full, &logger, &htlc_source, packet);
Expand Down
30 changes: 17 additions & 13 deletions lightning/src/ln/outbound_payment.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -163,6 +163,7 @@ impl PendingOutboundPayment {
_ => None,
}
}

fn increment_attempts(&mut self) {
if let PendingOutboundPayment::Retryable { attempts, .. } = self {
attempts.count += 1;
Expand DownExpand Up@@ -797,6 +798,7 @@ pub(super) struct SendAlongPathArgs<'a> {
pub payment_id: PaymentId,
pub keysend_preimage: &'a Option<PaymentPreimage>,
pub invoice_request: Option<&'a InvoiceRequest>,
pub bolt12_invoice: Option<&'a PaidBolt12Invoice>,
pub session_priv_bytes: [u8; 32],
}

Expand DownExpand Up@@ -1042,7 +1044,7 @@ impl OutboundPayments {
hash_map::Entry::Occupied(entry) => match entry.get() {
PendingOutboundPayment::InvoiceReceived { .. } => {
let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, None, Some(bolt12_invoice), &route,
payment_hash, recipient_onion.clone(), keysend_preimage, None, Some(bolt12_invoice.clone()), &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height,
);
*entry.into_mut() = retryable_payment;
Expand All@@ -1053,7 +1055,7 @@ impl OutboundPayments {
invoice_request
} else { unreachable!() };
let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), Some(bolt12_invoice), &route,
payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), Some(bolt12_invoice.clone()), &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height
);
outbounds.insert(payment_id, retryable_payment);
Expand All@@ -1066,7 +1068,7 @@ impl OutboundPayments {
core::mem::drop(outbounds);

let result = self.pay_route_internal(
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, payment_id,
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, Some(&bolt12_invoice), payment_id,
Some(route_params.final_value_msat), &onion_session_privs, node_signer, best_block_height,
&send_payment_along_path
);
Expand DownExpand Up@@ -1359,7 +1361,7 @@ impl OutboundPayments {
})?;

let res = self.pay_route_internal(&route, payment_hash, &recipient_onion,
keysend_preimage, None, payment_id, None, &onion_session_privs, node_signer,
keysend_preimage, None, None, payment_id, None, &onion_session_privs, node_signer,
best_block_height, &send_payment_along_path);
log_info!(logger, "Sending payment with id {} and hash {} returned {:?}",
payment_id, payment_hash, res);
Expand DownExpand Up@@ -1437,7 +1439,7 @@ impl OutboundPayments {
}
}
}
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request) = {
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice) = {
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
match outbounds.entry(payment_id) {
hash_map::Entry::Occupied(mut payment) => {
Expand DownExpand Up@@ -1479,8 +1481,9 @@ impl OutboundPayments {
}

payment.get_mut().increment_attempts();
let bolt12_invoice = payment.get().bolt12_invoice();

(total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request)
(total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request, bolt12_invoice.cloned())
},
PendingOutboundPayment::Legacy { .. } => {
log_error!(logger, "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102");
Expand DownExpand Up@@ -1520,7 +1523,7 @@ impl OutboundPayments {
}
};
let res = self.pay_route_internal(&route, payment_hash, &recipient_onion, keysend_preimage,
invoice_request.as_ref(), payment_id, Some(total_msat), &onion_session_privs, node_signer,
invoice_request.as_ref(), bolt12_invoice.as_ref(), payment_id, Some(total_msat), &onion_session_privs, node_signer,
best_block_height, &send_payment_along_path);
log_info!(logger, "Result retrying payment id {}: {:?}", &payment_id, res);
if let Err(e) = res {
Expand DownExpand Up@@ -1673,7 +1676,7 @@ impl OutboundPayments {

let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
match self.pay_route_internal(&route, payment_hash, &recipient_onion_fields,
None, None, payment_id, None, &onion_session_privs, node_signer, best_block_height,
None, None, None, payment_id, None, &onion_session_privs, node_signer, best_block_height,
&send_payment_along_path
) {
Ok(()) => Ok((payment_hash, payment_id)),
Expand DownExpand Up@@ -1865,7 +1868,7 @@ impl OutboundPayments {

fn pay_route_internal<NS: Deref, F>(
&self, route: &Route, payment_hash: PaymentHash, recipient_onion: &RecipientOnionFields,
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>,
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>, bolt12_invoice: Option<&PaidBolt12Invoice>,
payment_id: PaymentId, recv_value_msat: Option<u64>, onion_session_privs: &Vec<[u8; 32]>,
node_signer: &NS, best_block_height: u32, send_payment_along_path: &F
) -> Result<(), PaymentSendFailure>
Expand DownExpand Up@@ -1921,6 +1924,7 @@ impl OutboundPayments {
let path_res = send_payment_along_path(SendAlongPathArgs {
path: &path, payment_hash: &payment_hash, recipient_onion, total_value,
cur_height, payment_id, keysend_preimage: &keysend_preimage, invoice_request,
bolt12_invoice,
session_priv_bytes: *session_priv_bytes
});
results.push(path_res);
Expand DownExpand Up@@ -1987,7 +1991,7 @@ impl OutboundPayments {
F: Fn(SendAlongPathArgs) -> Result<(), APIError>,
{
self.pay_route_internal(route, payment_hash, &recipient_onion,
keysend_preimage, None, payment_id, recv_value_msat, &onion_session_privs,
keysend_preimage, None, None, payment_id, recv_value_msat, &onion_session_privs,
node_signer, best_block_height, &send_payment_along_path)
.map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
}
Expand All@@ -2008,8 +2012,8 @@ impl OutboundPayments {
}

pub(super) fn claim_htlc<L: Deref>(
&self, payment_id: PaymentId, payment_preimage: PaymentPreimage, session_priv: SecretKey,
path: Path, from_onchain: bool, ev_completion_action: EventCompletionAction,
&self, payment_id: PaymentId, payment_preimage: PaymentPreimage, bolt12_invoice: Option<PaidBolt12Invoice>,
session_priv: SecretKey, path: Path, from_onchain: bool, ev_completion_action: EventCompletionAction,
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
logger: &L,
) where L::Target: Logger {
Expand All@@ -2029,7 +2033,7 @@ impl OutboundPayments {
payment_hash,
amount_msat,
fee_paid_msat,
bolt12_invoice: payment.get().bolt12_invoice().cloned(),
bolt12_invoice: bolt12_invoice,
}, Some(ev_completion_action.clone())));
payment.get_mut().mark_fulfilled();
}
Expand Down
6 changes: 6 additions & 0 deletions lightning/src/offers/static_invoice.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -80,6 +80,12 @@ impl PartialEq for StaticInvoice {

impl Eq for StaticInvoice {}

impl core::hash::Hash for StaticInvoice {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.bytes.hash(state);
}
}

/// The contents of a [`StaticInvoice`] for responding to an [`Offer`].
///
/// [`Offer`]: crate::offers::offer::Offer
Expand Down