Skip to content
Open
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
2 changes: 2 additions & 0 deletions fuzz/src/invoice_request_deser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
.payer_note()
.map(|s| UntrustedString(s.to_string())),
human_readable_name: None,
contact_secret: None,
payer_offer: None,
}
};

Expand Down
54 changes: 53 additions & 1 deletion lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ use crate::ln::outbound_payment::{
};
use crate::ln::types::ChannelId;
use crate::offers::async_receive_offer_cache::AsyncReceiveOfferCache;
use crate::offers::contacts::ContactSecrets;
use crate::offers::flow::{HeldHtlcReplyPath, InvreqResponseInstructions, OffersMessageFlow};
use crate::offers::invoice::{Bolt12Invoice, UnsignedBolt12Invoice};
use crate::offers::invoice_error::InvoiceError;
Expand Down Expand Up @@ -698,6 +699,34 @@ pub struct OptionalOfferPaymentParams {
/// will ultimately fail once all pending paths have failed (generating an
/// [`Event::PaymentFailed`]).
pub retry_strategy: Retry,
/// Contact secrets to include in the invoice request for BLIP-42 contact management.
/// If provided, these secrets will be used to establish a contact relationship with the recipient.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be substantially more filled-out, including information about intended UX and UI integration logic, how/when to derive secrets, etc.

pub contact_secrets: Option<ContactSecrets>,
/// A custom payer offer to include in the invoice request for BLIP-42 contact management.
///
/// If provided, this offer will be included in the invoice request, allowing the recipient to
/// contact you back. If `None`, **no payer offer will be included** in the invoice request.
///
/// You can create custom offers using [`OffersMessageFlow::create_compact_offer_builder`]:
/// - Pass `None` for no blinded path (smallest size, ~70 bytes)
/// - Pass `Some(intro_node_id)` for a single blinded path (~200 bytes)
///
/// # Example
/// ```rust,ignore
/// // Include a compact offer with a single blinded path
/// let payer_offer = flow.create_compact_offer_builder(
/// &entropy_source,
/// Some(trusted_peer_pubkey)
/// )?.build()?;
///
/// let params = OptionalOfferPaymentParams {
/// payer_offer: Some(payer_offer),
/// ..Default::default()
/// };
/// ```
///
/// [`OffersMessageFlow::create_compact_offer_builder`]: crate::offers::flow::OffersMessageFlow::create_compact_offer_builder
pub payer_offer: Option<Offer>,
}

impl Default for OptionalOfferPaymentParams {
Expand All @@ -709,6 +738,8 @@ impl Default for OptionalOfferPaymentParams {
retry_strategy: Retry::Timeout(core::time::Duration::from_secs(2)),
#[cfg(not(feature = "std"))]
retry_strategy: Retry::Attempts(3),
contact_secrets: None,
payer_offer: None,
}
}
}
Expand Down Expand Up @@ -13324,6 +13355,8 @@ where
payment_id,
None,
create_pending_payment_fn,
optional_params.contact_secrets,
optional_params.payer_offer,
)
}

Expand Down Expand Up @@ -13353,6 +13386,8 @@ where
payment_id,
Some(offer.hrn),
create_pending_payment_fn,
optional_params.contact_secrets,
optional_params.payer_offer,
)
}

Expand Down Expand Up @@ -13395,6 +13430,8 @@ where
payment_id,
None,
create_pending_payment_fn,
optional_params.contact_secrets,
optional_params.payer_offer,
)
}

Expand All @@ -13403,6 +13440,7 @@ where
&self, offer: &Offer, quantity: Option<u64>, amount_msats: Option<u64>,
payer_note: Option<String>, payment_id: PaymentId,
human_readable_name: Option<HumanReadableName>, create_pending_payment: CPP,
contacts: Option<ContactSecrets>, payer_offer: Option<Offer>,
) -> Result<(), Bolt12SemanticError> {
let entropy = &*self.entropy_source;
let nonce = Nonce::from_entropy_source(entropy);
Expand All @@ -13428,6 +13466,20 @@ where
Some(hrn) => builder.sourced_from_human_readable_name(hrn),
};

let builder = if let Some(secrets) = contacts.as_ref() {
builder.contact_secrets(secrets.clone())
} else {
builder
};

// Add payer offer only if provided by the user.
// If the user explicitly wants to include an offer, they should provide it via payer_offer parameter.
let builder = if let Some(offer) = payer_offer {
builder.payer_offer(&offer)
} else {
builder
};

let invoice_request = builder.build_and_sign()?;
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);

Expand Down Expand Up @@ -16076,7 +16128,7 @@ where
self.pending_outbound_payments
.received_offer(payment_id, Some(retryable_invoice_request))
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)
});
}, None, None);
if offer_pay_res.is_err() {
// The offer we tried to pay is the canonical current offer for the name we
// wanted to pay. If we can't pay it, there's no way to recover so fail the
Expand Down
12 changes: 12 additions & 0 deletions lightning/src/ln/offers_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,8 @@ fn creates_and_pays_for_offer_using_two_hop_blinded_path() {
quantity: None,
payer_note_truncated: None,
human_readable_name: None,
contact_secret: None,
payer_offer: None,
},
});
assert_eq!(invoice_request.amount_msats(), Some(10_000_000));
Expand Down Expand Up @@ -841,6 +843,8 @@ fn creates_and_pays_for_offer_using_one_hop_blinded_path() {
quantity: None,
payer_note_truncated: None,
human_readable_name: None,
contact_secret: None,
payer_offer: None,
},
});
assert_eq!(invoice_request.amount_msats(), Some(10_000_000));
Expand Down Expand Up @@ -962,6 +966,8 @@ fn pays_for_offer_without_blinded_paths() {
quantity: None,
payer_note_truncated: None,
human_readable_name: None,
contact_secret: None,
payer_offer: None,
},
});

Expand Down Expand Up @@ -1229,6 +1235,8 @@ fn creates_and_pays_for_offer_with_retry() {
quantity: None,
payer_note_truncated: None,
human_readable_name: None,
contact_secret: None,
payer_offer: None,
},
});
assert_eq!(invoice_request.amount_msats(), Some(10_000_000));
Expand Down Expand Up @@ -1294,6 +1302,8 @@ fn pays_bolt12_invoice_asynchronously() {
quantity: None,
payer_note_truncated: None,
human_readable_name: None,
contact_secret: None,
payer_offer: None,
},
});

Expand Down Expand Up @@ -1391,6 +1401,8 @@ fn creates_offer_with_blinded_path_using_unannounced_introduction_node() {
quantity: None,
payer_note_truncated: None,
human_readable_name: None,
contact_secret: None,
payer_offer: None,
},
});
assert_ne!(invoice_request.payer_signing_pubkey(), bob_id);
Expand Down
Loading
Loading