Merge pull request #3207 from valentinewallace/2024-07-invreq-in-onion

Include invoice requests in async payment onions
This commit is contained in:
Matt Corallo 2024-11-01 14:52:03 +00:00 committed by GitHub
commit a130bd69de
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 178 additions and 95 deletions

View File

@ -121,8 +121,8 @@ impl OffersMessageHandler for TestOffersMessageHandler {
struct TestAsyncPaymentsMessageHandler {}
impl AsyncPaymentsMessageHandler for TestAsyncPaymentsMessageHandler {
fn held_htlc_available(
&self, message: HeldHtlcAvailable, responder: Option<Responder>,
fn handle_held_htlc_available(
&self, _message: HeldHtlcAvailable, responder: Option<Responder>,
) -> Option<(ReleaseHeldHtlc, ResponseInstruction)> {
let responder = match responder {
Some(resp) => resp,
@ -130,7 +130,7 @@ impl AsyncPaymentsMessageHandler for TestAsyncPaymentsMessageHandler {
};
Some((ReleaseHeldHtlc {}, responder.respond()))
}
fn release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {}
fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {}
}
#[derive(Debug)]

View File

@ -560,11 +560,15 @@ pub enum PaymentFailureReason {
/// [`PaymentParameters::expiry_time`]: crate::routing::router::PaymentParameters::expiry_time
/// [`InvoiceRequestExpired`]: Self::InvoiceRequestExpired
PaymentExpired,
/// We failed to find a route while retrying the payment.
/// We failed to find a route while sending or retrying the payment.
///
/// Note that this generally indicates that we've exhausted the available set of possible
/// routes - we tried the payment over a few routes but were not able to find any further
/// candidate routes beyond those.
///
/// Also used for [`BlindedPathCreationFailed`] when downgrading to versions prior to 0.0.124.
///
/// [`BlindedPathCreationFailed`]: Self::BlindedPathCreationFailed
RouteNotFound,
/// This error should generally never happen. This likely means that there is a problem with
/// your router.
@ -577,6 +581,12 @@ pub enum PaymentFailureReason {
///
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
InvoiceRequestRejected,
/// Failed to create a blinded path back to ourselves.
/// We attempted to initiate payment to a static invoice but failed to create a reply path for our
/// [`HeldHtlcAvailable`] message.
///
/// [`HeldHtlcAvailable`]: crate::onion_message::async_payments::HeldHtlcAvailable
BlindedPathCreationFailed,
}
impl_writeable_tlv_based_enum_upgradable!(PaymentFailureReason,
@ -587,6 +597,7 @@ impl_writeable_tlv_based_enum_upgradable!(PaymentFailureReason,
(4, RetriesExhausted) => {},
(5, InvoiceRequestRejected) => {},
(6, PaymentExpired) => {},
(7, BlindedPathCreationFailed) => {},
(8, RouteNotFound) => {},
(10, UnexpectedError) => {},
);
@ -1651,6 +1662,8 @@ impl Writeable for Event {
&Some(PaymentFailureReason::RetriesExhausted),
Some(PaymentFailureReason::InvoiceRequestRejected) =>
&Some(PaymentFailureReason::RecipientRejected),
Some(PaymentFailureReason::BlindedPathCreationFailed) =>
&Some(PaymentFailureReason::RouteNotFound)
};
write_tlv_fields!(writer, {
(0, payment_id, required),

View File

@ -318,7 +318,7 @@ fn do_forward_checks_failure(check: ForwardCheckFail, intro_fails: bool) {
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
let cur_height = nodes[0].best_block_info().1;
let (mut onion_payloads, ..) = onion_utils::build_onion_payloads(
&route.paths[0], amt_msat, &recipient_onion_fields, cur_height, &None).unwrap();
&route.paths[0], amt_msat, &recipient_onion_fields, cur_height, &None, None).unwrap();
// Remove the receive payload so the blinded forward payload is encoded as a final payload
// (i.e. next_hop_hmac == [0; 32])
onion_payloads.pop();
@ -897,7 +897,7 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) {
let cur_height = nodes[0].best_block_info().1;
let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
let (mut onion_payloads, ..) = onion_utils::build_onion_payloads(
&route.paths[0], amt_msat, &recipient_onion_fields, cur_height, &None).unwrap();
&route.paths[0], amt_msat, &recipient_onion_fields, cur_height, &None, None).unwrap();
let update_add = &mut payment_event_1_2.msgs[0];
onion_payloads.last_mut().map(|p| {
@ -1447,7 +1447,7 @@ fn route_blinding_spec_test_vector() {
}),
};
let cur_height = 747_000;
let (bob_onion, _, _) = onion_utils::create_payment_onion(&secp_ctx, &path, &session_priv, amt_msat, &RecipientOnionFields::spontaneous_empty(), cur_height, &PaymentHash([0; 32]), &None, [0; 32]).unwrap();
let (bob_onion, _, _) = onion_utils::create_payment_onion(&secp_ctx, &path, &session_priv, amt_msat, &RecipientOnionFields::spontaneous_empty(), cur_height, &PaymentHash([0; 32]), &None, None, [0; 32]).unwrap();
struct TestEcdhSigner {
node_secret: SecretKey,

View File

@ -4306,14 +4306,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, session_priv_bytes
cur_height, payment_id, keysend_preimage, invoice_request: 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,
session_priv_bytes
invoice_request, 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());
@ -4322,7 +4322,7 @@ where
let (onion_packet, htlc_msat, htlc_cltv) = onion_utils::create_payment_onion(
&self.secp_ctx, &path, &session_priv, total_value, recipient_onion, cur_height,
payment_hash, keysend_preimage, prng_seed
payment_hash, keysend_preimage, invoice_request, prng_seed
).map_err(|e| {
let logger = WithContext::from(&self.logger, Some(path.hops.first().unwrap().pubkey), None, Some(*payment_hash));
log_error!(logger, "Failed to build an onion for path for payment hash {}", payment_hash);
@ -4590,7 +4590,7 @@ where
) {
Ok(paths) => paths,
Err(()) => {
self.abandon_payment_with_reason(payment_id, PaymentFailureReason::RouteNotFound);
self.abandon_payment_with_reason(payment_id, PaymentFailureReason::BlindedPathCreationFailed);
res = Err(Bolt12PaymentError::BlindedPathCreationFailed);
return NotifyOption::DoPersist
}
@ -11474,13 +11474,13 @@ where
MR::Target: MessageRouter,
L::Target: Logger,
{
fn held_htlc_available(
fn handle_held_htlc_available(
&self, _message: HeldHtlcAvailable, _responder: Option<Responder>
) -> Option<(ReleaseHeldHtlc, ResponseInstruction)> {
None
}
fn release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {
fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {
#[cfg(async_payments)] {
let AsyncPaymentsContext::OutboundPayment { payment_id, hmac, nonce } = _context;
if payment_id.verify_for_async_payment(hmac, nonce, &self.inbound_payment_key).is_err() { return }

View File

@ -1438,7 +1438,7 @@ fn test_fee_spike_violation_fails_htlc() {
let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap();
let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret);
let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0],
3460001, &recipient_onion_fields, cur_height, &None).unwrap();
3460001, &recipient_onion_fields, cur_height, &None, None).unwrap();
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash).unwrap();
let msg = msgs::UpdateAddHTLC {
channel_id: chan.2,
@ -1637,7 +1637,7 @@ fn test_chan_reserve_violation_inbound_htlc_outbound_channel() {
let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap();
let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret);
let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0],
700_000, &recipient_onion_fields, cur_height, &None).unwrap();
700_000, &recipient_onion_fields, cur_height, &None, None).unwrap();
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash).unwrap();
let msg = msgs::UpdateAddHTLC {
channel_id: chan.2,
@ -1817,7 +1817,7 @@ fn test_chan_reserve_violation_inbound_htlc_inbound_chan() {
let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route_2.paths[0], &session_priv).unwrap();
let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(
&route_2.paths[0], recv_value_2, &recipient_onion_fields, cur_height, &None).unwrap();
&route_2.paths[0], recv_value_2, &recipient_onion_fields, cur_height, &None, None).unwrap();
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash_1).unwrap();
let msg = msgs::UpdateAddHTLC {
channel_id: chan.2,
@ -3556,7 +3556,7 @@ fn fail_backward_pending_htlc_upon_channel_failure() {
let current_height = nodes[1].node.best_block.read().unwrap().height + 1;
let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret);
let (onion_payloads, _amount_msat, cltv_expiry) = onion_utils::build_onion_payloads(
&route.paths[0], 50_000, &recipient_onion_fields, current_height, &None).unwrap();
&route.paths[0], 50_000, &recipient_onion_fields, current_height, &None, None).unwrap();
let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap();
let onion_routing_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash).unwrap();
@ -6551,7 +6551,7 @@ fn test_update_add_htlc_bolt2_receiver_check_max_htlc_limit() {
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::signing_only(), &route.paths[0], &session_priv).unwrap();
let recipient_onion_fields = RecipientOnionFields::secret_only(our_payment_secret);
let (onion_payloads, _htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(
&route.paths[0], send_amt, &recipient_onion_fields, cur_height, &None).unwrap();
&route.paths[0], send_amt, &recipient_onion_fields, cur_height, &None, None).unwrap();
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash).unwrap();
let mut msg = msgs::UpdateAddHTLC {
@ -8294,7 +8294,7 @@ fn test_onion_value_mpp_set_calculation() {
let mut onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
let recipient_onion_fields = RecipientOnionFields::secret_only(our_payment_secret);
let (mut onion_payloads, _, _) = onion_utils::build_onion_payloads(&route.paths[0], 100_000,
&recipient_onion_fields, height + 1, &None).unwrap();
&recipient_onion_fields, height + 1, &None, None).unwrap();
// Edit amt_to_forward to simulate the sender having set
// the final amount and the routing node taking less fee
if let msgs::OutboundOnionPayload::Receive {

View File

@ -110,7 +110,7 @@ fn large_payment_metadata() {
let secp_ctx = Secp256k1::signing_only();
route_0_1.paths[0].hops[0].fee_msat = MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY;
route_0_1.paths[0].hops[0].cltv_expiry_delta = DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA;
let err = onion_utils::create_payment_onion(&secp_ctx, &route_0_1.paths[0], &test_utils::privkey(42), MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY, &recipient_onion_too_large_md, nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, &payment_hash, &None, [0; 32]).unwrap_err();
let err = onion_utils::create_payment_onion(&secp_ctx, &route_0_1.paths[0], &test_utils::privkey(42), MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY, &recipient_onion_too_large_md, nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, &payment_hash, &None, None, [0; 32]).unwrap_err();
match err {
APIError::InvalidRoute { err } => {
assert_eq!(err, "Route size too large considering onion data");
@ -184,6 +184,7 @@ fn one_hop_blinded_path_with_custom_tlv() {
encrypted_tlvs: &blinded_path.blinded_hops()[0].encrypted_payload,
intro_node_blinding_point: Some(blinded_path.blinding_point()),
keysend_preimage: None,
invoice_request: None,
custom_tlvs: &Vec::new()
}.serialized_length();
let max_custom_tlv_len = 1300
@ -275,7 +276,7 @@ fn blinded_path_with_custom_tlv() {
let reserved_packet_bytes_without_custom_tlv: usize = onion_utils::build_onion_payloads(
&route.paths[0], MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY,
&RecipientOnionFields::spontaneous_empty(),
nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, &None
nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, &None, None
)
.unwrap()
.0
@ -317,7 +318,7 @@ fn blinded_path_with_custom_tlv() {
let secp_ctx = Secp256k1::signing_only();
route.paths[0].hops[0].fee_msat = MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY;
route.paths[0].hops[0].cltv_expiry_delta = DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA;
let err = onion_utils::create_payment_onion(&secp_ctx, &route.paths[0], &test_utils::privkey(42), MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY, &recipient_onion_too_large_custom_tlv, nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, &payment_hash, &None, [0; 32]).unwrap_err();
let err = onion_utils::create_payment_onion(&secp_ctx, &route.paths[0], &test_utils::privkey(42), MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY, &recipient_onion_too_large_custom_tlv, nodes[0].best_block_info().1 + DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, &payment_hash, &None, None, [0; 32]).unwrap_err();
match err {
APIError::InvalidRoute { err } => {
assert_eq!(err, "Route size too large considering onion data");

View File

@ -1747,6 +1747,7 @@ pub struct FinalOnionHopData {
mod fuzzy_internal_msgs {
use bitcoin::secp256k1::PublicKey;
use crate::blinded_path::payment::{BlindedPaymentPath, PaymentConstraints, PaymentContext, PaymentRelay};
use crate::offers::invoice_request::InvoiceRequest;
use crate::types::payment::{PaymentPreimage, PaymentSecret};
use crate::types::features::{BlindedHopFeatures, Bolt12InvoiceFeatures};
use super::{FinalOnionHopData, TrampolineOnionPacket};
@ -1827,6 +1828,7 @@ mod fuzzy_internal_msgs {
intro_node_blinding_point: Option<PublicKey>, // Set if the introduction node of the blinded path is the final node
keysend_preimage: Option<PaymentPreimage>,
custom_tlvs: &'a Vec<(u64, Vec<u8>)>,
invoice_request: Option<&'a InvoiceRequest>,
}
}
@ -2760,13 +2762,17 @@ impl<'a> Writeable for OutboundOnionPayload<'a> {
},
Self::BlindedReceive {
sender_intended_htlc_amt_msat, total_msat, cltv_expiry_height, encrypted_tlvs,
intro_node_blinding_point, keysend_preimage, ref custom_tlvs,
intro_node_blinding_point, keysend_preimage, ref invoice_request, ref custom_tlvs,
} => {
// We need to update [`ln::outbound_payment::RecipientOnionFields::with_custom_tlvs`]
// to reject any reserved types in the experimental range if new ones are ever
// standardized.
let invoice_request_tlv = invoice_request.map(|invreq| (77_777, invreq.encode())); // TODO: update TLV type once the async payments spec is merged
let keysend_tlv = keysend_preimage.map(|preimage| (5482373484, preimage.encode()));
let mut custom_tlvs: Vec<&(u64, Vec<u8>)> = custom_tlvs.iter().chain(keysend_tlv.iter()).collect();
let mut custom_tlvs: Vec<&(u64, Vec<u8>)> = custom_tlvs.iter()
.chain(invoice_request_tlv.iter())
.chain(keysend_tlv.iter())
.collect();
custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ);
_encode_varint_length_prefixed_tlv!(w, {
(2, HighZeroBytesDroppedBigSize(*sender_intended_htlc_amt_msat), required),

View File

@ -541,7 +541,7 @@ mod tests {
let path = Path { hops, blinded_tail: None, };
let onion_keys = super::onion_utils::construct_onion_keys(&secp_ctx, &path, &session_priv).unwrap();
let (onion_payloads, ..) = super::onion_utils::build_onion_payloads(
&path, total_amt_msat, &recipient_onion, cur_height + 1, &Some(keysend_preimage)
&path, total_amt_msat, &recipient_onion, cur_height + 1, &Some(keysend_preimage), None
).unwrap();
assert!(super::onion_utils::construct_onion_packet(
@ -569,7 +569,7 @@ mod tests {
let (onion, amount_msat, cltv_expiry) = create_payment_onion(
&secp_ctx, &path, &session_priv, total_amt_msat, &recipient_onion,
cur_height, &payment_hash, &Some(preimage), prng_seed
cur_height, &payment_hash, &Some(preimage), None, prng_seed
).unwrap();
let msg = make_update_add_msg(amount_msat, cltv_expiry, payment_hash, onion);

View File

@ -356,7 +356,7 @@ fn test_onion_failure() {
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
let (mut onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads(
&route.paths[0], 40000, &recipient_onion_fields, cur_height, &None).unwrap();
&route.paths[0], 40000, &recipient_onion_fields, cur_height, &None, None).unwrap();
let mut new_payloads = Vec::new();
for payload in onion_payloads.drain(..) {
new_payloads.push(BogusOnionHopData::new(payload));
@ -375,7 +375,7 @@ fn test_onion_failure() {
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
let (mut onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads(
&route.paths[0], 40000, &recipient_onion_fields, cur_height, &None).unwrap();
&route.paths[0], 40000, &recipient_onion_fields, cur_height, &None, None).unwrap();
let mut new_payloads = Vec::new();
for payload in onion_payloads.drain(..) {
new_payloads.push(BogusOnionHopData::new(payload));
@ -627,7 +627,7 @@ fn test_onion_failure() {
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
let (onion_payloads, _, htlc_cltv) = onion_utils::build_onion_payloads(
&route.paths[0], 40000, &recipient_onion_fields, height, &None).unwrap();
&route.paths[0], 40000, &recipient_onion_fields, height, &None, None).unwrap();
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash).unwrap();
msg.cltv_expiry = htlc_cltv;
msg.onion_routing_packet = onion_packet;
@ -964,7 +964,7 @@ fn test_always_create_tlv_format_onion_payloads() {
let cur_height = nodes[0].best_block_info().1 + 1;
let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
let (onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads(
&route.paths[0], 40000, &recipient_onion_fields, cur_height, &None).unwrap();
&route.paths[0], 40000, &recipient_onion_fields, cur_height, &None, None).unwrap();
match onion_payloads[0] {
msgs::OutboundOnionPayload::Forward {..} => {},
@ -1221,7 +1221,7 @@ fn test_phantom_invalid_onion_payload() {
let recipient_onion_fields = RecipientOnionFields::secret_only(payment_secret);
let (mut onion_payloads, _, _) = onion_utils::build_onion_payloads(
&route.paths[0], msgs::MAX_VALUE_MSAT + 1,
&recipient_onion_fields, height + 1, &None).unwrap();
&recipient_onion_fields, height + 1, &None, None).unwrap();
// We only want to construct the onion packet for the last hop, not the entire route, so
// remove the first hop's payload and its keys.
onion_keys.remove(0);

View File

@ -13,6 +13,7 @@ use crate::crypto::streams::ChaChaReader;
use crate::ln::channel::TOTAL_BITCOIN_SUPPLY_SATOSHIS;
use crate::ln::channelmanager::{HTLCSource, RecipientOnionFields};
use crate::ln::msgs;
use crate::offers::invoice_request::InvoiceRequest;
use crate::routing::gossip::NetworkUpdate;
use crate::routing::router::{Path, RouteHop, RouteParameters};
use crate::sign::NodeSigner;
@ -179,6 +180,7 @@ pub(super) fn construct_onion_keys<T: secp256k1::Signing>(
pub(super) fn build_onion_payloads<'a>(
path: &'a Path, total_msat: u64, recipient_onion: &'a RecipientOnionFields,
starting_htlc_offset: u32, keysend_preimage: &Option<PaymentPreimage>,
invoice_request: Option<&'a InvoiceRequest>,
) -> Result<(Vec<msgs::OutboundOnionPayload<'a>>, u64, u32), APIError> {
let mut res: Vec<msgs::OutboundOnionPayload> = Vec::with_capacity(
path.hops.len() + path.blinded_tail.as_ref().map_or(0, |t| t.hops.len()),
@ -197,6 +199,7 @@ pub(super) fn build_onion_payloads<'a>(
recipient_onion,
starting_htlc_offset,
keysend_preimage,
invoice_request,
|action, payload| match action {
PayloadCallbackAction::PushBack => res.push(payload),
PayloadCallbackAction::PushFront => res.insert(0, payload),
@ -218,7 +221,8 @@ enum PayloadCallbackAction {
fn build_onion_payloads_callback<'a, H, B, F>(
hops: H, mut blinded_tail: Option<BlindedTailHopIter<'a, B>>, total_msat: u64,
recipient_onion: &'a RecipientOnionFields, starting_htlc_offset: u32,
keysend_preimage: &Option<PaymentPreimage>, mut callback: F,
keysend_preimage: &Option<PaymentPreimage>, invoice_request: Option<&'a InvoiceRequest>,
mut callback: F,
) -> Result<(u64, u32), APIError>
where
H: DoubleEndedIterator<Item = &'a RouteHop>,
@ -262,6 +266,7 @@ where
encrypted_tlvs: &blinded_hop.encrypted_payload,
intro_node_blinding_point: blinding_point.take(),
keysend_preimage: *keysend_preimage,
invoice_request,
custom_tlvs: &recipient_onion.custom_tlvs,
},
);
@ -315,7 +320,8 @@ pub(crate) const MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY: u64 = 100_000_000;
pub(crate) fn set_max_path_length(
route_params: &mut RouteParameters, recipient_onion: &RecipientOnionFields,
keysend_preimage: Option<PaymentPreimage>, best_block_height: u32,
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>,
best_block_height: u32,
) -> Result<(), ()> {
const PAYLOAD_HMAC_LEN: usize = 32;
let unblinded_intermed_payload_len = msgs::OutboundOnionPayload::Forward {
@ -362,6 +368,7 @@ pub(crate) fn set_max_path_length(
&recipient_onion,
best_block_height,
&keysend_preimage,
invoice_request,
|_, payload| {
num_reserved_bytes = num_reserved_bytes
.saturating_add(payload.serialized_length())
@ -1157,7 +1164,8 @@ where
pub fn create_payment_onion<T: secp256k1::Signing>(
secp_ctx: &Secp256k1<T>, path: &Path, session_priv: &SecretKey, total_msat: u64,
recipient_onion: &RecipientOnionFields, cur_block_height: u32, payment_hash: &PaymentHash,
keysend_preimage: &Option<PaymentPreimage>, prng_seed: [u8; 32],
keysend_preimage: &Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>,
prng_seed: [u8; 32],
) -> Result<(msgs::OnionPacket, u64, u32), APIError> {
let onion_keys = construct_onion_keys(&secp_ctx, &path, &session_priv).map_err(|_| {
APIError::InvalidRoute { err: "Pubkey along hop was maliciously selected".to_owned() }
@ -1168,6 +1176,7 @@ pub fn create_payment_onion<T: secp256k1::Signing>(
recipient_onion,
cur_block_height,
keysend_preimage,
invoice_request,
)?;
let onion_packet = construct_onion_packet(onion_payloads, onion_keys, prng_seed, payment_hash)
.map_err(|_| APIError::InvalidRoute {

View File

@ -83,6 +83,7 @@ pub(crate) enum PendingOutboundPayment {
keysend_preimage: PaymentPreimage,
retry_strategy: Retry,
route_params: RouteParameters,
invoice_request: InvoiceRequest,
},
Retryable {
retry_strategy: Option<Retry>,
@ -93,6 +94,7 @@ pub(crate) enum PendingOutboundPayment {
payment_secret: Option<PaymentSecret>,
payment_metadata: Option<Vec<u8>>,
keysend_preimage: Option<PaymentPreimage>,
invoice_request: Option<InvoiceRequest>,
custom_tlvs: Vec<(u64, Vec<u8>)>,
pending_amt_msat: u64,
/// Used to track the fee paid. Present iff the payment was serialized on 0.0.103+.
@ -641,6 +643,7 @@ impl RecipientOnionFields {
for (typ, _) in custom_tlvs.iter() {
if *typ < 1 << 16 { return Err(()); }
if *typ == 5482373484 { return Err(()); } // keysend
if *typ == 77_777 { return Err(()); } // invoice requests for async payments
match prev_type {
Some(prev) if prev >= *typ => return Err(()),
_ => {},
@ -715,6 +718,7 @@ pub(super) struct SendAlongPathArgs<'a> {
pub cur_height: u32,
pub payment_id: PaymentId,
pub keysend_preimage: &'a Option<PaymentPreimage>,
pub invoice_request: Option<&'a InvoiceRequest>,
pub session_priv_bytes: [u8; 32],
}
@ -768,7 +772,7 @@ impl OutboundPayments {
F: Fn(SendAlongPathArgs) -> Result<(), APIError>
{
let onion_session_privs = self.add_new_pending_payment(payment_hash, recipient_onion.clone(), payment_id, None, route, None, None, entropy_source, best_block_height)?;
self.pay_route_internal(route, payment_hash, &recipient_onion, None, payment_id, None,
self.pay_route_internal(route, payment_hash, &recipient_onion, None, None, payment_id, None,
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 })
}
@ -813,7 +817,7 @@ impl OutboundPayments {
let onion_session_privs = self.add_new_pending_payment(payment_hash, recipient_onion.clone(),
payment_id, Some(preimage), &route, None, None, entropy_source, best_block_height)?;
match self.pay_route_internal(route, payment_hash, &recipient_onion, Some(preimage),
match self.pay_route_internal(route, payment_hash, &recipient_onion, Some(preimage), None,
payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path
) {
Ok(()) => Ok(payment_hash),
@ -878,7 +882,7 @@ impl OutboundPayments {
route_params.max_total_routing_fee_msat = Some(max_fee_msat);
}
self.send_payment_for_bolt12_invoice_internal(
payment_id, payment_hash, None, route_params, retry_strategy, router, first_hops,
payment_id, payment_hash, None, None, route_params, retry_strategy, router, first_hops,
inflight_htlcs, entropy_source, node_signer, node_id_lookup, secp_ctx, best_block_height,
logger, pending_events, send_payment_along_path
)
@ -888,10 +892,10 @@ impl OutboundPayments {
R: Deref, ES: Deref, NS: Deref, NL: Deref, IH, SP, L: Deref
>(
&self, payment_id: PaymentId, payment_hash: PaymentHash,
keysend_preimage: Option<PaymentPreimage>, mut route_params: RouteParameters,
retry_strategy: Retry, router: &R, first_hops: Vec<ChannelDetails>, inflight_htlcs: IH,
entropy_source: &ES, node_signer: &NS, node_id_lookup: &NL,
secp_ctx: &Secp256k1<secp256k1::All>, best_block_height: u32, logger: &L,
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>,
mut route_params: RouteParameters, retry_strategy: Retry, router: &R,
first_hops: Vec<ChannelDetails>, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS,
node_id_lookup: &NL, secp_ctx: &Secp256k1<secp256k1::All>, best_block_height: u32, logger: &L,
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
send_payment_along_path: SP,
) -> Result<(), Bolt12PaymentError>
@ -928,8 +932,9 @@ impl OutboundPayments {
custom_tlvs: vec![],
};
let route = match self.find_initial_route(
payment_id, payment_hash, &recipient_onion, None, &mut route_params, router,
&first_hops, &inflight_htlcs, node_signer, best_block_height, logger,
payment_id, payment_hash, &recipient_onion, keysend_preimage, invoice_request,
&mut route_params, router, &first_hops, &inflight_htlcs, node_signer, best_block_height,
logger,
) {
Ok(route) => route,
Err(e) => {
@ -945,25 +950,38 @@ impl OutboundPayments {
};
let payment_params = Some(route_params.payment_params.clone());
let (retryable_payment, onion_session_privs) = self.create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, &route, Some(retry_strategy),
payment_params, entropy_source, best_block_height
);
match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
let onion_session_privs = match outbounds.entry(payment_id) {
hash_map::Entry::Occupied(entry) => match entry.get() {
PendingOutboundPayment::InvoiceReceived { .. }
| PendingOutboundPayment::StaticInvoiceReceived { .. } => {
PendingOutboundPayment::InvoiceReceived { .. } => {
let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, None, &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height
);
*entry.into_mut() = retryable_payment;
onion_session_privs
},
PendingOutboundPayment::StaticInvoiceReceived { .. } => {
let invreq = if let PendingOutboundPayment::StaticInvoiceReceived { invoice_request, .. } = entry.remove() {
invoice_request
} else { unreachable!() };
let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height
);
outbounds.insert(payment_id, retryable_payment);
onion_session_privs
},
_ => return Err(Bolt12PaymentError::DuplicateInvoice),
},
hash_map::Entry::Vacant(_) => return Err(Bolt12PaymentError::UnexpectedInvoice),
}
};
core::mem::drop(outbounds);
let result = self.pay_route_internal(
&route, payment_hash, &recipient_onion, keysend_preimage, payment_id,
Some(route_params.final_value_msat), onion_session_privs, node_signer,
best_block_height, &send_payment_along_path
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, payment_id,
Some(route_params.final_value_msat), onion_session_privs, node_signer, best_block_height,
&send_payment_along_path
);
log_info!(
logger, "Sending payment with id {} and hash {} returned {:?}", payment_id,
@ -1002,7 +1020,7 @@ impl OutboundPayments {
}
match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
hash_map::Entry::Occupied(mut entry) => match entry.get() {
hash_map::Entry::Occupied(mut entry) => match entry.get_mut() {
PendingOutboundPayment::AwaitingInvoice {
retry_strategy, retryable_invoice_request, max_total_routing_fee_msat, ..
} => {
@ -1035,7 +1053,7 @@ impl OutboundPayments {
if let Err(()) = onion_utils::set_max_path_length(
&mut route_params, &RecipientOnionFields::spontaneous_empty(), Some(keysend_preimage),
best_block_height
Some(invreq), best_block_height
) {
abandon_with_entry!(entry, PaymentFailureReason::RouteNotFound);
return Err(Bolt12PaymentError::SendingFailed(RetryableSendFailure::OnionPacketSizeExceeded))
@ -1046,6 +1064,11 @@ impl OutboundPayments {
keysend_preimage,
retry_strategy: *retry_strategy,
route_params,
invoice_request:
retryable_invoice_request
.take()
.ok_or(Bolt12PaymentError::UnexpectedInvoice)?
.invoice_request,
};
return Ok(())
},
@ -1074,13 +1097,14 @@ impl OutboundPayments {
IH: Fn() -> InFlightHtlcs,
SP: Fn(SendAlongPathArgs) -> Result<(), APIError>,
{
let (payment_hash, keysend_preimage, route_params, retry_strategy) =
let (payment_hash, keysend_preimage, route_params, retry_strategy, invoice_request) =
match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
hash_map::Entry::Occupied(entry) => match entry.get() {
PendingOutboundPayment::StaticInvoiceReceived {
payment_hash, route_params, retry_strategy, keysend_preimage, ..
payment_hash, route_params, retry_strategy, keysend_preimage, invoice_request, ..
} => {
(*payment_hash, *keysend_preimage, route_params.clone(), *retry_strategy)
(*payment_hash, *keysend_preimage, route_params.clone(), *retry_strategy,
invoice_request.clone())
},
_ => return Err(Bolt12PaymentError::DuplicateInvoice),
},
@ -1088,9 +1112,9 @@ impl OutboundPayments {
};
self.send_payment_for_bolt12_invoice_internal(
payment_id, payment_hash, Some(keysend_preimage), route_params, retry_strategy, router,
first_hops, inflight_htlcs, entropy_source, node_signer, node_id_lookup, secp_ctx,
best_block_height, logger, pending_events, send_payment_along_path
payment_id, payment_hash, Some(keysend_preimage), Some(&invoice_request), route_params,
retry_strategy, router, first_hops, inflight_htlcs, entropy_source, node_signer,
node_id_lookup, secp_ctx, best_block_height, logger, pending_events, send_payment_along_path
)
}
@ -1159,8 +1183,8 @@ impl OutboundPayments {
}
fn find_initial_route<R: Deref, NS: Deref, IH, L: Deref>(
&self, payment_id: PaymentId, payment_hash: PaymentHash,
recipient_onion: &RecipientOnionFields, keysend_preimage: Option<PaymentPreimage>,
&self, payment_id: PaymentId, payment_hash: PaymentHash, recipient_onion: &RecipientOnionFields,
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>,
route_params: &mut RouteParameters, router: &R, first_hops: &Vec<ChannelDetails>,
inflight_htlcs: &IH, node_signer: &NS, best_block_height: u32, logger: &L,
) -> Result<Route, RetryableSendFailure>
@ -1179,7 +1203,7 @@ impl OutboundPayments {
}
onion_utils::set_max_path_length(
route_params, recipient_onion, keysend_preimage, best_block_height
route_params, recipient_onion, keysend_preimage, invoice_request, best_block_height
)
.map_err(|()| {
log_error!(logger, "Can't construct an onion packet without exceeding 1300-byte onion \
@ -1227,7 +1251,7 @@ impl OutboundPayments {
SP: Fn(SendAlongPathArgs) -> Result<(), APIError>,
{
let route = self.find_initial_route(
payment_id, payment_hash, &recipient_onion, keysend_preimage, &mut route_params, router,
payment_id, payment_hash, &recipient_onion, keysend_preimage, None, &mut route_params, router,
&first_hops, &inflight_htlcs, node_signer, best_block_height, logger,
)?;
@ -1241,7 +1265,7 @@ impl OutboundPayments {
})?;
let res = self.pay_route_internal(&route, payment_hash, &recipient_onion,
keysend_preimage, payment_id, None, onion_session_privs, node_signer,
keysend_preimage, 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);
@ -1315,14 +1339,14 @@ impl OutboundPayments {
}
}
}
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs) = {
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request) = {
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
match outbounds.entry(payment_id) {
hash_map::Entry::Occupied(mut payment) => {
match payment.get() {
PendingOutboundPayment::Retryable {
total_msat, keysend_preimage, payment_secret, payment_metadata,
custom_tlvs, pending_amt_msat, ..
custom_tlvs, pending_amt_msat, invoice_request, ..
} => {
const RETRY_OVERFLOW_PERCENTAGE: u64 = 10;
let retry_amt_msat = route.get_total_amount();
@ -1345,6 +1369,7 @@ impl OutboundPayments {
custom_tlvs: custom_tlvs.clone(),
};
let keysend_preimage = *keysend_preimage;
let invoice_request = invoice_request.clone();
let mut onion_session_privs = Vec::with_capacity(route.paths.len());
for _ in 0..route.paths.len() {
@ -1357,7 +1382,7 @@ impl OutboundPayments {
payment.get_mut().increment_attempts();
(total_msat, recipient_onion, keysend_preimage, onion_session_privs)
(total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request)
},
PendingOutboundPayment::Legacy { .. } => {
log_error!(logger, "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102");
@ -1395,8 +1420,8 @@ impl OutboundPayments {
}
};
let res = self.pay_route_internal(&route, payment_hash, &recipient_onion, keysend_preimage,
payment_id, Some(total_msat), onion_session_privs, node_signer, best_block_height,
&send_payment_along_path);
invoice_request.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 {
self.handle_pay_route_err(e, payment_id, payment_hash, route, route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, logger, pending_events, send_payment_along_path);
@ -1507,7 +1532,8 @@ impl OutboundPayments {
let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
match self.pay_route_internal(&route, payment_hash, &recipient_onion_fields,
None, payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path
None, None, payment_id, None, onion_session_privs, node_signer, best_block_height,
&send_payment_along_path
) {
Ok(()) => Ok((payment_hash, payment_id)),
Err(e) => {
@ -1546,8 +1572,8 @@ impl OutboundPayments {
match pending_outbounds.entry(payment_id) {
hash_map::Entry::Occupied(_) => Err(PaymentSendFailure::DuplicatePayment),
hash_map::Entry::Vacant(entry) => {
let (payment, onion_session_privs) = self.create_pending_payment(
payment_hash, recipient_onion, keysend_preimage, route, retry_strategy,
let (payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion, keysend_preimage, None, route, retry_strategy,
payment_params, entropy_source, best_block_height
);
entry.insert(payment);
@ -1557,9 +1583,10 @@ impl OutboundPayments {
}
fn create_pending_payment<ES: Deref>(
&self, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
keysend_preimage: Option<PaymentPreimage>, route: &Route, retry_strategy: Option<Retry>,
payment_params: Option<PaymentParameters>, entropy_source: &ES, best_block_height: u32
payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<InvoiceRequest>,
route: &Route, retry_strategy: Option<Retry>, payment_params: Option<PaymentParameters>,
entropy_source: &ES, best_block_height: u32
) -> (PendingOutboundPayment, Vec<[u8; 32]>)
where
ES::Target: EntropySource,
@ -1580,6 +1607,7 @@ impl OutboundPayments {
payment_secret: recipient_onion.payment_secret,
payment_metadata: recipient_onion.payment_metadata,
keysend_preimage,
invoice_request,
custom_tlvs: recipient_onion.custom_tlvs,
starting_block_height: best_block_height,
total_msat: route.get_total_amount(),
@ -1619,9 +1647,9 @@ impl OutboundPayments {
fn pay_route_internal<NS: Deref, F>(
&self, route: &Route, payment_hash: PaymentHash, recipient_onion: &RecipientOnionFields,
keysend_preimage: Option<PaymentPreimage>, 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
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>,
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>
where
NS::Target: NodeSigner,
@ -1674,7 +1702,7 @@ impl OutboundPayments {
for (path, session_priv_bytes) in route.paths.iter().zip(onion_session_privs.into_iter()) {
let mut 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,
cur_height, payment_id, keysend_preimage: &keysend_preimage, invoice_request,
session_priv_bytes
});
match path_res {
@ -1759,7 +1787,7 @@ impl OutboundPayments {
F: Fn(SendAlongPathArgs) -> Result<(), APIError>,
{
self.pay_route_internal(route, payment_hash, &recipient_onion,
keysend_preimage, payment_id, recv_value_msat, onion_session_privs,
keysend_preimage, 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 })
}
@ -2141,6 +2169,7 @@ impl OutboundPayments {
payment_secret: None, // only used for retries, and we'll never retry on startup
payment_metadata: None, // only used for retries, and we'll never retry on startup
keysend_preimage: None, // only used for retries, and we'll never retry on startup
invoice_request: None, // only used for retries, and we'll never retry on startup
custom_tlvs: Vec::new(), // only used for retries, and we'll never retry on startup
pending_amt_msat: path_amt,
pending_fee_msat: Some(path_fee),
@ -2227,6 +2256,7 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
(9, custom_tlvs, optional_vec),
(10, starting_block_height, required),
(11, remaining_max_total_routing_fee_msat, option),
(13, invoice_request, option),
(not_written, retry_strategy, (static_value, None)),
(not_written, attempts, (static_value, PaymentAttempts::new())),
},
@ -2246,13 +2276,14 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
(2, retry_strategy, required),
(4, max_total_routing_fee_msat, option),
},
// Added in 0.0.125. Prior versions will drop these outbounds on downgrade, which is safe because
// no HTLCs are in-flight.
// Added in 0.1. Prior versions will drop these outbounds on downgrade, which is safe because no
// HTLCs are in-flight.
(9, StaticInvoiceReceived) => {
(0, payment_hash, required),
(2, keysend_preimage, required),
(4, retry_strategy, required),
(6, route_params, required),
(8, invoice_request, required),
},
);
@ -2267,15 +2298,19 @@ mod tests {
use crate::events::{Event, PathFailure, PaymentFailureReason};
use crate::types::payment::{PaymentHash, PaymentPreimage};
use crate::ln::channelmanager::{PaymentId, RecipientOnionFields};
use crate::ln::inbound_payment::ExpandedKey;
use crate::types::features::{Bolt12InvoiceFeatures, ChannelFeatures, NodeFeatures};
use crate::ln::msgs::{ErrorAction, LightningError};
use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, PendingOutboundPayment, Retry, RetryableSendFailure, StaleExpiration};
#[cfg(feature = "std")]
use crate::offers::invoice::DEFAULT_RELATIVE_EXPIRY;
use crate::offers::invoice_request::InvoiceRequest;
use crate::offers::nonce::Nonce;
use crate::offers::offer::OfferBuilder;
use crate::offers::test_utils::*;
use crate::routing::gossip::NetworkGraph;
use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteHop, RouteParameters};
use crate::sign::KeyMaterial;
use crate::sync::{Arc, Mutex, RwLock};
use crate::util::errors::APIError;
use crate::util::hash_tables::new_hash_map;
@ -2820,6 +2855,22 @@ mod tests {
assert!(pending_events.lock().unwrap().is_empty());
}
fn dummy_invoice_request() -> InvoiceRequest {
let expanded_key = ExpandedKey::new(&KeyMaterial([42; 32]));
let entropy = FixedEntropy {};
let nonce = Nonce::from_entropy_source(&entropy);
let secp_ctx = Secp256k1::new();
let payment_id = PaymentId([1; 32]);
OfferBuilder::new(recipient_pubkey())
.amount_msats(1000)
.build().unwrap()
.request_invoice_deriving_signing_pubkey(&expanded_key, nonce, &secp_ctx, payment_id)
.unwrap()
.build_and_sign()
.unwrap()
}
#[test]
fn time_out_unreleased_async_payments() {
let pending_events = Mutex::new(VecDeque::new());
@ -2841,6 +2892,7 @@ mod tests {
keysend_preimage: PaymentPreimage([0; 32]),
retry_strategy: Retry::Attempts(0),
route_params,
invoice_request: dummy_invoice_request(),
};
outbounds.insert(payment_id, outbound);
core::mem::drop(outbounds);
@ -2887,6 +2939,7 @@ mod tests {
keysend_preimage: PaymentPreimage([0; 32]),
retry_strategy: Retry::Attempts(0),
route_params,
invoice_request: dummy_invoice_request(),
};
outbounds.insert(payment_id, outbound);
core::mem::drop(outbounds);

View File

@ -4271,7 +4271,7 @@ fn peel_payment_onion_custom_tlvs() {
let (onion_routing_packet, first_hop_msat, cltv_expiry) = onion_utils::create_payment_onion(
&secp_ctx, &route.paths[0], &session_priv, amt_msat, &recipient_onion,
nodes[0].best_block_info().1, &payment_hash, &Some(keysend_preimage), prng_seed
nodes[0].best_block_info().1, &payment_hash, &Some(keysend_preimage), None, prng_seed
).unwrap();
let update_add = msgs::UpdateAddHTLC {

View File

@ -148,12 +148,12 @@ impl OffersMessageHandler for IgnoringMessageHandler {
}
}
impl AsyncPaymentsMessageHandler for IgnoringMessageHandler {
fn held_htlc_available(
fn handle_held_htlc_available(
&self, _message: HeldHtlcAvailable, _responder: Option<Responder>,
) -> Option<(ReleaseHeldHtlc, ResponseInstruction)> {
None
}
fn release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {}
fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {}
}
impl DNSResolverMessageHandler for IgnoringMessageHandler {
fn handle_dnssec_query(

View File

@ -27,13 +27,13 @@ const RELEASE_HELD_HTLC_TLV_TYPE: u64 = 74;
pub trait AsyncPaymentsMessageHandler {
/// Handle a [`HeldHtlcAvailable`] message. A [`ReleaseHeldHtlc`] should be returned to release
/// the held funds.
fn held_htlc_available(
fn handle_held_htlc_available(
&self, message: HeldHtlcAvailable, responder: Option<Responder>,
) -> Option<(ReleaseHeldHtlc, ResponseInstruction)>;
/// Handle a [`ReleaseHeldHtlc`] message. If authentication of the message succeeds, an HTLC
/// should be released to the corresponding payee.
fn release_held_htlc(&self, message: ReleaseHeldHtlc, context: AsyncPaymentsContext);
fn handle_release_held_htlc(&self, message: ReleaseHeldHtlc, context: AsyncPaymentsContext);
/// Release any [`AsyncPaymentsMessage`]s that need to be sent.
///

View File

@ -84,12 +84,12 @@ impl OffersMessageHandler for TestOffersMessageHandler {
struct TestAsyncPaymentsMessageHandler {}
impl AsyncPaymentsMessageHandler for TestAsyncPaymentsMessageHandler {
fn held_htlc_available(
fn handle_held_htlc_available(
&self, _message: HeldHtlcAvailable, _responder: Option<Responder>,
) -> Option<(ReleaseHeldHtlc, ResponseInstruction)> {
None
}
fn release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {}
fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {}
}
struct TestDNSResolverMessageHandler {}

View File

@ -1623,7 +1623,7 @@ where
},
#[cfg(async_payments)]
ParsedOnionMessageContents::AsyncPayments(AsyncPaymentsMessage::HeldHtlcAvailable(msg)) => {
let response_instructions = self.async_payments_handler.held_htlc_available(
let response_instructions = self.async_payments_handler.handle_held_htlc_available(
msg, responder
);
if let Some((msg, instructions)) = response_instructions {
@ -1640,7 +1640,7 @@ where
},
None => return,
};
self.async_payments_handler.release_held_htlc(msg, context);
self.async_payments_handler.handle_release_held_htlc(msg, context);
},
ParsedOnionMessageContents::DNSResolver(DNSResolverMessage::DNSSECQuery(msg)) => {
let response_instructions = self.dns_resolver_handler.handle_dnssec_query(msg, responder);

View File

@ -613,8 +613,9 @@ impl RouteParameters {
&mut self, recipient_onion: &RecipientOnionFields, is_keysend: bool, best_block_height: u32
) -> Result<(), ()> {
let keysend_preimage_opt = is_keysend.then(|| PaymentPreimage([42; 32]));
// TODO: no way to account for the invoice request here yet
onion_utils::set_max_path_length(
self, recipient_onion, keysend_preimage_opt, best_block_height
self, recipient_onion, keysend_preimage_opt, None, best_block_height
)
}
}