Skip to content

Commit 998fffb

Browse files
committed
Add BOLT 12 Refunds section to ChannelManager docs
1 parent 761f09b commit 998fffb

File tree

2 files changed

+109
-0
lines changed

2 files changed

+109
-0
lines changed

lightning/src/ln/channelmanager.rs

+99
Original file line numberDiff line numberDiff line change
@@ -1625,6 +1625,103 @@ where
16251625
/// # }
16261626
/// ```
16271627
///
1628+
/// ## BOLT 12 Refunds
1629+
///
1630+
/// A [`Refund`] is a request for an invoice to be paid. Like *paying* for an [`Offer`], *creating*
1631+
/// a [`Refund`] involves maintaining state since it represents a future outbound payment.
1632+
/// Therefore, use [`create_refund_builder`] when creating one, otherwise [`ChannelManager`] will
1633+
/// refuse to pay any corresponding [`Bolt12Invoice`] that it receives.
1634+
///
1635+
/// ```
1636+
/// # use core::time::Duration;
1637+
/// # use lightning::events::{Event, EventsProvider};
1638+
/// # use lightning::ln::channelmanager::{AChannelManager, PaymentId, RecentPaymentDetails, Retry};
1639+
/// # use lightning::offers::parse::Bolt12SemanticError;
1640+
/// #
1641+
/// # fn example<T: AChannelManager>(
1642+
/// # channel_manager: T, amount_msats: u64, absolute_expiry: Duration, retry: Retry,
1643+
/// # max_total_routing_fee_msat: Option<u64>
1644+
/// # ) -> Result<(), Bolt12SemanticError> {
1645+
/// # let channel_manager = channel_manager.get_cm();
1646+
/// let payment_id = PaymentId([42; 32]);
1647+
/// let refund = channel_manager
1648+
/// .create_refund_builder(
1649+
/// "coffee".to_string(), amount_msats, absolute_expiry, payment_id, retry,
1650+
/// max_total_routing_fee_msat
1651+
/// )?
1652+
/// # ;
1653+
/// # // Needed for compiling for c_bindings
1654+
/// # let builder: lightning::offers::refund::RefundBuilder<_> = refund.into();
1655+
/// # let refund = builder
1656+
/// .payer_note("refund for order 1234".to_string())
1657+
/// .build()?;
1658+
/// let bech32_refund = refund.to_string();
1659+
///
1660+
/// // First the payment will be waiting on an invoice
1661+
/// let expected_payment_id = payment_id;
1662+
/// assert!(
1663+
/// channel_manager.list_recent_payments().iter().find(|details| matches!(
1664+
/// details,
1665+
/// RecentPaymentDetails::AwaitingInvoice { payment_id: expected_payment_id }
1666+
/// )).is_some()
1667+
/// );
1668+
///
1669+
/// // Once the invoice is received, a payment will be sent
1670+
/// assert!(
1671+
/// channel_manager.list_recent_payments().iter().find(|details| matches!(
1672+
/// details,
1673+
/// RecentPaymentDetails::Pending { payment_id: expected_payment_id, .. }
1674+
/// )).is_some()
1675+
/// );
1676+
///
1677+
/// // On the event processing thread
1678+
/// channel_manager.process_pending_events(&|event| match event {
1679+
/// Event::PaymentSent { payment_id: Some(payment_id), .. } => println!("Paid {}", payment_id),
1680+
/// Event::PaymentFailed { payment_id, .. } => println!("Failed paying {}", payment_id),
1681+
/// // ...
1682+
/// # _ => {},
1683+
/// });
1684+
/// # Ok(())
1685+
/// # }
1686+
/// ```
1687+
///
1688+
/// Use [`request_refund_payment`] to send a [`Bolt12Invoice`] for receiving the refund. Similar to
1689+
/// *creating* an [`Offer`], this is stateless as it represents an inbound payment.
1690+
///
1691+
/// ```
1692+
/// # use lightning::events::{Event, EventsProvider, PaymentPurpose};
1693+
/// # use lightning::ln::channelmanager::AChannelManager;
1694+
/// # use lightning::offers::refund::Refund;
1695+
/// #
1696+
/// # fn example<T: AChannelManager>(channel_manager: T, refund: &Refund) {
1697+
/// # let channel_manager = channel_manager.get_cm();
1698+
/// match channel_manager.request_refund_payment(refund) {
1699+
/// Ok(()) => println!("Requesting payment for refund"),
1700+
/// Err(e) => println!("Unable to request payment for refund: {:?}", e),
1701+
/// }
1702+
///
1703+
/// // On the event processing thread
1704+
/// channel_manager.process_pending_events(&|event| match event {
1705+
/// Event::PaymentClaimable { payment_hash, purpose, .. } => match purpose {
1706+
/// PaymentPurpose::InvoicePayment { payment_preimage: Some(payment_preimage), .. } => {
1707+
/// println!("Claiming payment {}", payment_hash);
1708+
/// channel_manager.claim_funds(payment_preimage);
1709+
/// },
1710+
/// PaymentPurpose::InvoicePayment { payment_preimage: None, .. } => {
1711+
/// println!("Unknown payment hash: {}", payment_hash);
1712+
/// },
1713+
/// // ...
1714+
/// # _ => {},
1715+
/// },
1716+
/// Event::PaymentClaimed { payment_hash, amount_msat, .. } => {
1717+
/// println!("Claimed {} msats", amount_msat);
1718+
/// },
1719+
/// // ...
1720+
/// # _ => {},
1721+
/// });
1722+
/// # }
1723+
/// ```
1724+
///
16281725
/// # Persistence
16291726
///
16301727
/// Implements [`Writeable`] to write out all channel state to disk. Implies [`peer_disconnected`] for
@@ -1704,6 +1801,8 @@ where
17041801
/// [`create_offer_builder`]: Self::create_offer_builder
17051802
/// [`pay_for_offer`]: Self::pay_for_offer
17061803
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
1804+
/// [`create_refund_builder`]: Self::create_refund_builder
1805+
/// [`request_refund_payment`]: Self::request_refund_payment
17071806
/// [`peer_disconnected`]: msgs::ChannelMessageHandler::peer_disconnected
17081807
/// [`funding_created`]: msgs::FundingCreated
17091808
/// [`funding_transaction_generated`]: Self::funding_transaction_generated

lightning/src/offers/refund.rs

+10
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,16 @@ for RefundMaybeWithDerivedMetadataBuilder<'a> {
375375
}
376376
}
377377

378+
#[cfg(c_bindings)]
379+
impl<'a> From<RefundMaybeWithDerivedMetadataBuilder<'a>>
380+
for RefundBuilder<'a, secp256k1::All> {
381+
fn from(builder: RefundMaybeWithDerivedMetadataBuilder<'a>) -> Self {
382+
let RefundMaybeWithDerivedMetadataBuilder { refund, secp_ctx } = builder;
383+
384+
Self { refund, secp_ctx }
385+
}
386+
}
387+
378388
/// A `Refund` is a request to send an [`Bolt12Invoice`] without a preceding [`Offer`].
379389
///
380390
/// Typically, after an invoice is paid, the recipient may publish a refund allowing the sender to

0 commit comments

Comments
 (0)