Skip to content

Commit 097e285

Browse files
blip42: Add contact secret and payer offer support to invoice requests
Implements BLIP-42 contact management for the sender side: - Add contact_secret and payer_offer fields to InvoiceRequestContents - Add builder methods: contact_secrets(), payer_offer() - Add accessor methods: contact_secret(), payer_offer() - Add OptionalOfferPaymentParams fields for contact_secrects and payer_offer - Update ChannelManager::pay_for_offer to pass contact information - Add create_compact_offer_builder to OffersMessageFlow for small payer offers - Add test for pay_offer_and_add_contacts_info_blip42 Signed-off-by: Vincenzo Palazzo <[email protected]>
1 parent ae6810b commit 097e285

File tree

4 files changed

+360
-21
lines changed

4 files changed

+360
-21
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ use crate::ln::outbound_payment::{
9292
};
9393
use crate::ln::types::ChannelId;
9494
use crate::offers::async_receive_offer_cache::AsyncReceiveOfferCache;
95+
use crate::offers::contacts::ContactSecrets;
9596
use crate::offers::flow::{HeldHtlcReplyPath, InvreqResponseInstructions, OffersMessageFlow};
9697
use crate::offers::invoice::{Bolt12Invoice, UnsignedBolt12Invoice};
9798
use crate::offers::invoice_error::InvoiceError;
@@ -728,6 +729,34 @@ pub struct OptionalOfferPaymentParams {
728729
/// will ultimately fail once all pending paths have failed (generating an
729730
/// [`Event::PaymentFailed`]).
730731
pub retry_strategy: Retry,
732+
/// Contact secrets to include in the invoice request for BLIP-42 contact management.
733+
/// If provided, these secrets will be used to establish a contact relationship with the recipient.
734+
pub contact_secrects: Option<ContactSecrets>,
735+
/// A custom payer offer to include in the invoice request for BLIP-42 contact management.
736+
///
737+
/// If provided, this offer will be included in the invoice request, allowing the recipient to
738+
/// contact you back. If `None`, **no payer offer will be included** in the invoice request.
739+
///
740+
/// You can create custom offers using [`OffersMessageFlow::create_compact_offer_builder`]:
741+
/// - Pass `None` for no blinded path (smallest size, ~70 bytes)
742+
/// - Pass `Some(intro_node_id)` for a single blinded path (~200 bytes)
743+
///
744+
/// # Example
745+
/// ```rust,ignore
746+
/// // Include a compact offer with a single blinded path
747+
/// let payer_offer = flow.create_compact_offer_builder(
748+
/// &entropy_source,
749+
/// Some(trusted_peer_pubkey)
750+
/// )?.build()?;
751+
///
752+
/// let params = OptionalOfferPaymentParams {
753+
/// payer_offer: Some(payer_offer),
754+
/// ..Default::default()
755+
/// };
756+
/// ```
757+
///
758+
/// [`OffersMessageFlow::create_compact_offer_builder`]: crate::offers::flow::OffersMessageFlow::create_compact_offer_builder
759+
pub payer_offer: Option<Offer>,
731760
}
732761

733762
impl Default for OptionalOfferPaymentParams {
@@ -739,6 +768,8 @@ impl Default for OptionalOfferPaymentParams {
739768
retry_strategy: Retry::Timeout(core::time::Duration::from_secs(2)),
740769
#[cfg(not(feature = "std"))]
741770
retry_strategy: Retry::Attempts(3),
771+
contact_secrects: None,
772+
payer_offer: None,
742773
}
743774
}
744775
}
@@ -12944,6 +12975,8 @@ where
1294412975
payment_id,
1294512976
None,
1294612977
create_pending_payment_fn,
12978+
optional_params.contact_secrects,
12979+
optional_params.payer_offer,
1294712980
)
1294812981
}
1294912982

@@ -12973,6 +13006,8 @@ where
1297313006
payment_id,
1297413007
Some(offer.hrn),
1297513008
create_pending_payment_fn,
13009+
optional_params.contact_secrects,
13010+
optional_params.payer_offer,
1297613011
)
1297713012
}
1297813013

@@ -13015,6 +13050,8 @@ where
1301513050
payment_id,
1301613051
None,
1301713052
create_pending_payment_fn,
13053+
optional_params.contact_secrects,
13054+
optional_params.payer_offer,
1301813055
)
1301913056
}
1302013057

@@ -13023,6 +13060,7 @@ where
1302313060
&self, offer: &Offer, quantity: Option<u64>, amount_msats: Option<u64>,
1302413061
payer_note: Option<String>, payment_id: PaymentId,
1302513062
human_readable_name: Option<HumanReadableName>, create_pending_payment: CPP,
13063+
contacts: Option<ContactSecrets>, payer_offer: Option<Offer>,
1302613064
) -> Result<(), Bolt12SemanticError> {
1302713065
let entropy = &*self.entropy_source;
1302813066
let nonce = Nonce::from_entropy_source(entropy);
@@ -13048,6 +13086,20 @@ where
1304813086
Some(hrn) => builder.sourced_from_human_readable_name(hrn),
1304913087
};
1305013088

13089+
let builder = if let Some(secrets) = contacts.as_ref() {
13090+
builder.contact_secrets(secrets.clone())
13091+
} else {
13092+
builder
13093+
};
13094+
13095+
// Add payer offer only if provided by the user.
13096+
// If the user explicitly wants to include an offer, they should provide it via payer_offer parameter.
13097+
let builder = if let Some(offer) = payer_offer {
13098+
builder.payer_offer(&offer)
13099+
} else {
13100+
builder
13101+
};
13102+
1305113103
let invoice_request = builder.build_and_sign()?;
1305213104
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
1305313105

@@ -15649,7 +15701,7 @@ where
1564915701
self.pending_outbound_payments
1565015702
.received_offer(payment_id, Some(retryable_invoice_request))
1565115703
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)
15652-
});
15704+
}, None, None);
1565315705
if offer_pay_res.is_err() {
1565415706
// The offer we tried to pay is the canonical current offer for the name we
1565515707
// wanted to pay. If we can't pay it, there's no way to recover so fail the

0 commit comments

Comments
 (0)