Skip to content

Commit 2d4632f

Browse files
committed
Add BOLT 12 Offers section to ChannelManager docs
1 parent da05a9e commit 2d4632f

File tree

2 files changed

+113
-0
lines changed

2 files changed

+113
-0
lines changed

lightning/src/ln/channelmanager.rs

+103
Original file line numberDiff line numberDiff line change
@@ -1526,6 +1526,105 @@ where
15261526
/// # }
15271527
/// ```
15281528
///
1529+
/// ## BOLT 12 Offers
1530+
///
1531+
/// The [`offers`] module is useful for creating BOLT 12 offers. An [`Offer`] is a precursor to a
1532+
/// [`Bolt12Invoice`], which must first be requested by the payer. The interchange of these messages
1533+
/// as defined in the specification is handled by [`ChannelManager`] and its implementation of
1534+
/// [`OffersMessageHandler`]. However, this only works with an [`Offer`] created using a builder
1535+
/// returned by [`create_offer_builder`]. With this approach, BOLT 12 offers and invoices are
1536+
/// stateless just as BOLT 11 invoices are.
1537+
///
1538+
/// ```
1539+
/// # use lightning::events::{Event, EventsProvider, PaymentPurpose};
1540+
/// # use lightning::ln::channelmanager::AChannelManager;
1541+
/// # use lightning::offers::parse::Bolt12SemanticError;
1542+
/// #
1543+
/// # fn example<T: AChannelManager>(channel_manager: T) -> Result<(), Bolt12SemanticError> {
1544+
/// # let channel_manager = channel_manager.get_cm();
1545+
/// let offer = channel_manager
1546+
/// .create_offer_builder("coffee".to_string())?
1547+
/// # ;
1548+
/// # // Needed for compiling for c_bindings
1549+
/// # let builder: lightning::offers::offer::OfferBuilder<_, _> = offer.into();
1550+
/// # let offer = builder
1551+
/// .amount_msats(10_000_000)
1552+
/// .build()?;
1553+
/// let bech32_offer = offer.to_string();
1554+
///
1555+
/// // On the event processing thread
1556+
/// channel_manager.process_pending_events(&|event| match event {
1557+
/// Event::PaymentClaimable { payment_hash, purpose, .. } => match purpose {
1558+
/// PaymentPurpose::InvoicePayment { payment_preimage: Some(payment_preimage), .. } => {
1559+
/// println!("Claiming payment {}", payment_hash);
1560+
/// channel_manager.claim_funds(payment_preimage);
1561+
/// },
1562+
/// PaymentPurpose::InvoicePayment { payment_preimage: None, .. } => {
1563+
/// println!("Unknown payment hash: {}", payment_hash);
1564+
/// },
1565+
/// // ...
1566+
/// # _ => {},
1567+
/// },
1568+
/// Event::PaymentClaimed { payment_hash, amount_msat, .. } => {
1569+
/// println!("Claimed {} msats", amount_msat);
1570+
/// },
1571+
/// // ...
1572+
/// # _ => {},
1573+
/// });
1574+
/// # Ok(())
1575+
/// # }
1576+
/// ```
1577+
///
1578+
/// Use [`pay_for_offer`] to initiated payment, which sends an [`InvoiceRequest`] for an [`Offer`]
1579+
/// and pays the [`Bolt12Invoice`] response. In addition to success and failure events,
1580+
/// [`ChannelManager`] may also generate an [`Event::InvoiceRequestFailed`].
1581+
///
1582+
/// ```
1583+
/// # use lightning::events::{Event, EventsProvider};
1584+
/// # use lightning::ln::channelmanager::{AChannelManager, PaymentId, RecentPaymentDetails, Retry};
1585+
/// # use lightning::offers::offer::Offer;
1586+
/// #
1587+
/// # fn example<T: AChannelManager>(
1588+
/// # channel_manager: T, offer: &Offer, quantity: Option<u64>, amount_msats: Option<u64>,
1589+
/// # payer_note: Option<String>, retry: Retry, max_total_routing_fee_msat: Option<u64>
1590+
/// # ) {
1591+
/// # let channel_manager = channel_manager.get_cm();
1592+
/// let payment_id = PaymentId([42; 32]);
1593+
/// match channel_manager.pay_for_offer(
1594+
/// offer, quantity, amount_msats, payer_note, payment_id, retry, max_total_routing_fee_msat
1595+
/// ) {
1596+
/// Ok(()) => println!("Requesting invoice for offer"),
1597+
/// Err(e) => println!("Unable to request invoice for offer: {:?}", e),
1598+
/// }
1599+
///
1600+
/// // First the payment will be waiting on an invoice
1601+
/// let expected_payment_id = payment_id;
1602+
/// assert!(
1603+
/// channel_manager.list_recent_payments().iter().find(|details| matches!(
1604+
/// details,
1605+
/// RecentPaymentDetails::AwaitingInvoice { payment_id: expected_payment_id }
1606+
/// )).is_some()
1607+
/// );
1608+
///
1609+
/// // Once the invoice is received, a payment will be sent
1610+
/// assert!(
1611+
/// channel_manager.list_recent_payments().iter().find(|details| matches!(
1612+
/// details,
1613+
/// RecentPaymentDetails::Pending { payment_id: expected_payment_id, .. }
1614+
/// )).is_some()
1615+
/// );
1616+
///
1617+
/// // On the event processing thread
1618+
/// channel_manager.process_pending_events(&|event| match event {
1619+
/// Event::PaymentSent { payment_id: Some(payment_id), .. } => println!("Paid {}", payment_id),
1620+
/// Event::PaymentFailed { payment_id, .. } => println!("Failed paying {}", payment_id),
1621+
/// Event::InvoiceRequestFailed { payment_id, .. } => println!("Failed paying {}", payment_id),
1622+
/// // ...
1623+
/// # _ => {},
1624+
/// });
1625+
/// # }
1626+
/// ```
1627+
///
15291628
/// # Persistence
15301629
///
15311630
/// Implements [`Writeable`] to write out all channel state to disk. Implies [`peer_disconnected`] for
@@ -1601,6 +1700,10 @@ where
16011700
/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
16021701
/// [`claim_funds`]: Self::claim_funds
16031702
/// [`send_payment`]: Self::send_payment
1703+
/// [`offers`]: crate::offers
1704+
/// [`create_offer_builder`]: Self::create_offer_builder
1705+
/// [`pay_for_offer`]: Self::pay_for_offer
1706+
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
16041707
/// [`peer_disconnected`]: msgs::ChannelMessageHandler::peer_disconnected
16051708
/// [`funding_created`]: msgs::FundingCreated
16061709
/// [`funding_transaction_generated`]: Self::funding_transaction_generated

lightning/src/offers/offer.rs

+10
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,16 @@ for OfferWithDerivedMetadataBuilder<'a> {
459459
}
460460
}
461461

462+
#[cfg(c_bindings)]
463+
impl<'a> From<OfferWithDerivedMetadataBuilder<'a>>
464+
for OfferBuilder<'a, DerivedMetadata, secp256k1::All> {
465+
fn from(builder: OfferWithDerivedMetadataBuilder<'a>) -> Self {
466+
let OfferWithDerivedMetadataBuilder { offer, metadata_strategy, secp_ctx } = builder;
467+
468+
Self { offer, metadata_strategy, secp_ctx }
469+
}
470+
}
471+
462472
/// An `Offer` is a potentially long-lived proposal for payment of a good or service.
463473
///
464474
/// An offer is a precursor to an [`InvoiceRequest`]. A merchant publishes an offer from which a

0 commit comments

Comments
 (0)