Skip to content

Commit f7a21d3

Browse files
committed
Move create_refund_builder to OffersMessageFlow
1 parent ac7c4ea commit f7a21d3

File tree

5 files changed

+206
-175
lines changed

5 files changed

+206
-175
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 16 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ use crate::offers::invoice_request::{InvoiceRequest, InvoiceRequestBuilder};
7070
use crate::offers::nonce::Nonce;
7171
use crate::offers::offer::Offer;
7272
use crate::offers::parse::Bolt12SemanticError;
73-
use crate::offers::refund::{Refund, RefundBuilder};
73+
use crate::offers::refund::Refund;
7474
use crate::offers::signer;
7575
use crate::onion_message::async_payments::{AsyncPaymentsMessage, HeldHtlcAvailable, ReleaseHeldHtlc, AsyncPaymentsMessageHandler};
7676
use crate::onion_message::dns_resolution::HumanReadableName;
@@ -105,8 +105,6 @@ use {
105105
crate::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters},
106106
crate::sign::KeysManager,
107107
};
108-
#[cfg(c_bindings)]
109-
use crate::offers::refund::RefundMaybeWithDerivedMetadataBuilder;
110108

111109
use lightning_invoice::{Bolt11Invoice, Bolt11InvoiceDescription, CreationError, Currency, Description, InvoiceBuilder as Bolt11InvoiceBuilder, SignOrCreationError, DEFAULT_EXPIRY_TIME};
112110

@@ -2009,6 +2007,7 @@ where
20092007
/// # use lightning::events::{Event, EventsProvider};
20102008
/// # use lightning::types::payment::PaymentHash;
20112009
/// # use lightning::ln::channelmanager::{AChannelManager, PaymentId, RecentPaymentDetails, RecipientOnionFields, Retry};
2010+
/// # use lightning::offers::flow::OffersMessageCommons;
20122011
/// # use lightning::routing::router::RouteParameters;
20132012
/// #
20142013
/// # fn example<T: AChannelManager>(
@@ -2063,6 +2062,7 @@ where
20632062
/// ```
20642063
/// # use lightning::events::{Event, EventsProvider};
20652064
/// # use lightning::ln::channelmanager::{AChannelManager, PaymentId, RecentPaymentDetails, Retry};
2065+
/// # use lightning::offers::flow::OffersMessageCommons;
20662066
/// # use lightning::offers::offer::Offer;
20672067
/// #
20682068
/// # fn example<T: AChannelManager>(
@@ -2109,67 +2109,8 @@ where
21092109
/// ```
21102110
///
21112111
/// ## BOLT 12 Refunds
2112-
///
2113-
/// A [`Refund`] is a request for an invoice to be paid. Like *paying* for an [`Offer`], *creating*
2114-
/// a [`Refund`] involves maintaining state since it represents a future outbound payment.
2115-
/// Therefore, use [`create_refund_builder`] when creating one, otherwise [`ChannelManager`] will
2116-
/// refuse to pay any corresponding [`Bolt12Invoice`] that it receives.
2117-
///
2118-
/// ```
2119-
/// # use core::time::Duration;
2120-
/// # use lightning::events::{Event, EventsProvider};
2121-
/// # use lightning::ln::channelmanager::{AChannelManager, PaymentId, RecentPaymentDetails, Retry};
2122-
/// # use lightning::offers::parse::Bolt12SemanticError;
2123-
/// #
2124-
/// # fn example<T: AChannelManager>(
2125-
/// # channel_manager: T, amount_msats: u64, absolute_expiry: Duration, retry: Retry,
2126-
/// # max_total_routing_fee_msat: Option<u64>
2127-
/// # ) -> Result<(), Bolt12SemanticError> {
2128-
/// # let channel_manager = channel_manager.get_cm();
2129-
/// let payment_id = PaymentId([42; 32]);
2130-
/// let refund = channel_manager
2131-
/// .create_refund_builder(
2132-
/// amount_msats, absolute_expiry, payment_id, retry, max_total_routing_fee_msat
2133-
/// )?
2134-
/// # ;
2135-
/// # // Needed for compiling for c_bindings
2136-
/// # let builder: lightning::offers::refund::RefundBuilder<_> = refund.into();
2137-
/// # let refund = builder
2138-
/// .description("coffee".to_string())
2139-
/// .payer_note("refund for order 1234".to_string())
2140-
/// .build()?;
2141-
/// let bech32_refund = refund.to_string();
2142-
///
2143-
/// // First the payment will be waiting on an invoice
2144-
/// let expected_payment_id = payment_id;
2145-
/// assert!(
2146-
/// channel_manager.list_recent_payments().iter().find(|details| matches!(
2147-
/// details,
2148-
/// RecentPaymentDetails::AwaitingInvoice { payment_id: expected_payment_id }
2149-
/// )).is_some()
2150-
/// );
2151-
///
2152-
/// // Once the invoice is received, a payment will be sent
2153-
/// assert!(
2154-
/// channel_manager.list_recent_payments().iter().find(|details| matches!(
2155-
/// details,
2156-
/// RecentPaymentDetails::Pending { payment_id: expected_payment_id, .. }
2157-
/// )).is_some()
2158-
/// );
2159-
///
2160-
/// // On the event processing thread
2161-
/// channel_manager.process_pending_events(&|event| {
2162-
/// match event {
2163-
/// Event::PaymentSent { payment_id: Some(payment_id), .. } => println!("Paid {}", payment_id),
2164-
/// Event::PaymentFailed { payment_id, .. } => println!("Failed paying {}", payment_id),
2165-
/// // ...
2166-
/// # _ => {},
2167-
/// }
2168-
/// Ok(())
2169-
/// });
2170-
/// # Ok(())
2171-
/// # }
2172-
/// ```
2112+
///
2113+
/// For more information on creating refunds, see [`create_refund_builder`].
21732114
///
21742115
/// Use [`request_refund_payment`] to send a [`Bolt12Invoice`] for receiving the refund. Similar to
21752116
/// *creating* an [`Offer`], this is stateless as it represents an inbound payment.
@@ -2298,7 +2239,6 @@ where
22982239
/// [`offers`]: crate::offers
22992240
/// [`pay_for_offer`]: Self::pay_for_offer
23002241
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
2301-
/// [`create_refund_builder`]: Self::create_refund_builder
23022242
/// [`request_refund_payment`]: Self::request_refund_payment
23032243
/// [`peer_disconnected`]: msgs::ChannelMessageHandler::peer_disconnected
23042244
/// [`funding_created`]: msgs::FundingCreated
@@ -2308,6 +2248,7 @@ where
23082248
/// [`ChannelUpdate`]: msgs::ChannelUpdate
23092249
/// [`read`]: ReadableArgs::read
23102250
/// [`create_offer_builder`]: crate::offers::flow::OffersMessageFlow::create_offer_builder
2251+
/// [`create_refund_builder`]: crate::offers::flow::OffersMessageFlow::create_refund_builder
23112252
//
23122253
// Lock order:
23132254
// The tree structure below illustrates the lock order requirements for the different locks of the
@@ -2803,12 +2744,14 @@ const MAX_NO_CHANNEL_PEERS: usize = 250;
28032744
/// The maximum expiration from the current time where an [`Offer`] or [`Refund`] is considered
28042745
/// short-lived, while anything with a greater expiration is considered long-lived.
28052746
///
2806-
/// Using [`OffersMessageFlow::create_offer_builder`] or [`ChannelManager::create_refund_builder`],
2747+
/// Using [`OffersMessageFlow::create_offer_builder`] or [`OffersMessageFlow::create_refund_builder`],
28072748
/// will included a [`BlindedMessagePath`] created using:
28082749
/// - [`MessageRouter::create_compact_blinded_paths`] when short-lived, and
28092750
/// - [`MessageRouter::create_blinded_paths`] when long-lived.
28102751
///
28112752
/// [`OffersMessageFlow::create_offer_builder`]: crate::offers::flow::OffersMessageFlow::create_offer_builder
2753+
/// [`OffersMessageFlow::create_refund_builder`]: crate::offers::flow::OffersMessageFlow::create_refund_builder
2754+
///
28122755
///
28132756
/// Using compact [`BlindedMessagePath`]s may provide better privacy as the [`MessageRouter`] could select
28142757
/// more hops. However, since they use short channel ids instead of pubkeys, they are more likely to
@@ -9634,87 +9577,6 @@ impl Default for Bolt11InvoiceParameters {
96349577
}
96359578
}
96369579

9637-
macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
9638-
/// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
9639-
/// [`ChannelManager`] when handling [`Bolt12Invoice`] messages for the refund.
9640-
///
9641-
/// # Payment
9642-
///
9643-
/// The provided `payment_id` is used to ensure that only one invoice is paid for the refund.
9644-
/// See [Avoiding Duplicate Payments] for other requirements once the payment has been sent.
9645-
///
9646-
/// The builder will have the provided expiration set. Any changes to the expiration on the
9647-
/// returned builder will not be honored by [`ChannelManager`]. For non-`std`, the highest seen
9648-
/// block time minus two hours is used for the current time when determining if the refund has
9649-
/// expired.
9650-
///
9651-
/// To revoke the refund, use [`ChannelManager::abandon_payment`] prior to receiving the
9652-
/// invoice. If abandoned, or an invoice isn't received before expiration, the payment will fail
9653-
/// with an [`Event::PaymentFailed`].
9654-
///
9655-
/// If `max_total_routing_fee_msat` is not specified, The default from
9656-
/// [`RouteParameters::from_payment_params_and_value`] is applied.
9657-
///
9658-
/// # Privacy
9659-
///
9660-
/// Uses [`MessageRouter`] to construct a [`BlindedMessagePath`] for the refund based on the given
9661-
/// `absolute_expiry` according to [`MAX_SHORT_LIVED_RELATIVE_EXPIRY`]. See those docs for
9662-
/// privacy implications as well as those of the parameterized [`Router`], which implements
9663-
/// [`MessageRouter`].
9664-
///
9665-
/// Also, uses a derived payer id in the refund for payer privacy.
9666-
///
9667-
/// # Limitations
9668-
///
9669-
/// Requires a direct connection to an introduction node in the responding
9670-
/// [`Bolt12Invoice::payment_paths`].
9671-
///
9672-
/// # Errors
9673-
///
9674-
/// Errors if:
9675-
/// - a duplicate `payment_id` is provided given the caveats in the aforementioned link,
9676-
/// - `amount_msats` is invalid, or
9677-
/// - the parameterized [`Router`] is unable to create a blinded path for the refund.
9678-
///
9679-
/// [`Refund`]: crate::offers::refund::Refund
9680-
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
9681-
/// [`Bolt12Invoice::payment_paths`]: crate::offers::invoice::Bolt12Invoice::payment_paths
9682-
/// [Avoiding Duplicate Payments]: #avoiding-duplicate-payments
9683-
pub fn create_refund_builder(
9684-
&$self, amount_msats: u64, absolute_expiry: Duration, payment_id: PaymentId,
9685-
retry_strategy: Retry, max_total_routing_fee_msat: Option<u64>
9686-
) -> Result<$builder, Bolt12SemanticError> {
9687-
let node_id = $self.get_our_node_id();
9688-
let expanded_key = &$self.inbound_payment_key;
9689-
let entropy = &*$self.entropy_source;
9690-
let secp_ctx = &$self.secp_ctx;
9691-
9692-
let nonce = Nonce::from_entropy_source(entropy);
9693-
let context = OffersContext::OutboundPayment { payment_id, nonce, hmac: None };
9694-
let path = $self.create_blinded_paths_using_absolute_expiry(context, Some(absolute_expiry))
9695-
.and_then(|paths| paths.into_iter().next().ok_or(()))
9696-
.map_err(|_| Bolt12SemanticError::MissingPaths)?;
9697-
9698-
let builder = RefundBuilder::deriving_signing_pubkey(
9699-
node_id, expanded_key, nonce, secp_ctx, amount_msats, payment_id
9700-
)?
9701-
.chain_hash($self.chain_hash)
9702-
.absolute_expiry(absolute_expiry)
9703-
.path(path);
9704-
9705-
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop($self);
9706-
9707-
let expiration = StaleExpiration::AbsoluteTimeout(absolute_expiry);
9708-
$self.pending_outbound_payments
9709-
.add_new_awaiting_invoice(
9710-
payment_id, expiration, retry_strategy, max_total_routing_fee_msat, None,
9711-
)
9712-
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
9713-
9714-
Ok(builder.into())
9715-
}
9716-
} }
9717-
97189580
impl<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, MR: Deref, L: Deref> OffersMessageCommons for ChannelManager<M, T, ES, NS, SP, F, R, MR, L>
97199581
where
97209582
M::Target: chain::Watch<<SP::Target as SignerProvider>::EcdsaSigner>,
@@ -9788,6 +9650,13 @@ where
97889650
fn get_chain_hash(&self) -> ChainHash {
97899651
self.chain_hash
97909652
}
9653+
9654+
fn add_new_awaiting_invoice(&self, payment_id: PaymentId, expiration: StaleExpiration, retry_strategy: Retry, max_total_routing_fee_msat: Option<u64>, retryable_invoice_request: Option<RetryableInvoiceRequest>) -> Result<(), ()> {
9655+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
9656+
self.pending_outbound_payments.add_new_awaiting_invoice (
9657+
payment_id, expiration, retry_strategy, max_total_routing_fee_msat, retryable_invoice_request,
9658+
)
9659+
}
97919660
}
97929661

97939662
/// Defines the maximum number of [`OffersMessage`] including different reply paths to be sent
@@ -9809,11 +9678,6 @@ where
98099678
MR::Target: MessageRouter,
98109679
L::Target: Logger,
98119680
{
9812-
#[cfg(not(c_bindings))]
9813-
create_refund_builder!(self, RefundBuilder<secp256k1::All>);
9814-
9815-
#[cfg(c_bindings)]
9816-
create_refund_builder!(self, RefundMaybeWithDerivedMetadataBuilder);
98179681

98189682
/// Create an offer for receiving async payments as an often-offline recipient.
98199683
///

lightning/src/ln/offers_tests.rs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ fn creates_short_lived_refund() {
453453

454454
let absolute_expiry = bob.node.duration_since_epoch() + MAX_SHORT_LIVED_RELATIVE_EXPIRY;
455455
let payment_id = PaymentId([1; 32]);
456-
let refund = bob.node
456+
let refund = bob.offers_handler
457457
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
458458
.unwrap()
459459
.build().unwrap();
@@ -482,7 +482,7 @@ fn creates_long_lived_refund() {
482482
let absolute_expiry = bob.node.duration_since_epoch() + MAX_SHORT_LIVED_RELATIVE_EXPIRY
483483
+ Duration::from_secs(1);
484484
let payment_id = PaymentId([1; 32]);
485-
let refund = bob.node
485+
let refund = bob.offers_handler
486486
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
487487
.unwrap()
488488
.build().unwrap();
@@ -641,7 +641,7 @@ fn creates_and_pays_for_refund_using_two_hop_blinded_path() {
641641

642642
let absolute_expiry = Duration::from_secs(u64::MAX);
643643
let payment_id = PaymentId([1; 32]);
644-
let refund = david.node
644+
let refund = david.offers_handler
645645
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
646646
.unwrap()
647647
.build().unwrap();
@@ -770,7 +770,7 @@ fn creates_and_pays_for_refund_using_one_hop_blinded_path() {
770770

771771
let absolute_expiry = Duration::from_secs(u64::MAX);
772772
let payment_id = PaymentId([1; 32]);
773-
let refund = bob.node
773+
let refund = bob.offers_handler
774774
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
775775
.unwrap()
776776
.build().unwrap();
@@ -879,7 +879,7 @@ fn pays_for_refund_without_blinded_paths() {
879879

880880
let absolute_expiry = Duration::from_secs(u64::MAX);
881881
let payment_id = PaymentId([1; 32]);
882-
let refund = bob.node
882+
let refund = bob.offers_handler
883883
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
884884
.unwrap()
885885
.clear_paths()
@@ -1034,7 +1034,7 @@ fn send_invoice_for_refund_with_distinct_reply_path() {
10341034

10351035
let absolute_expiry = Duration::from_secs(u64::MAX);
10361036
let payment_id = PaymentId([1; 32]);
1037-
let refund = alice.node
1037+
let refund = alice.offers_handler
10381038
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
10391039
.unwrap()
10401040
.build().unwrap();
@@ -1237,7 +1237,7 @@ fn creates_refund_with_blinded_path_using_unannounced_introduction_node() {
12371237

12381238
let absolute_expiry = Duration::from_secs(u64::MAX);
12391239
let payment_id = PaymentId([1; 32]);
1240-
let refund = bob.node
1240+
let refund = bob.offers_handler
12411241
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
12421242
.unwrap()
12431243
.build().unwrap();
@@ -1520,7 +1520,7 @@ fn fails_authentication_when_handling_invoice_for_refund() {
15201520

15211521
let absolute_expiry = Duration::from_secs(u64::MAX);
15221522
let payment_id = PaymentId([1; 32]);
1523-
let refund = david.node
1523+
let refund = david.offers_handler
15241524
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
15251525
.unwrap()
15261526
.build().unwrap();
@@ -1554,7 +1554,7 @@ fn fails_authentication_when_handling_invoice_for_refund() {
15541554
// Send the invoice to David using an invalid blinded path.
15551555
let invalid_path = refund.paths().first().unwrap().clone();
15561556
let payment_id = PaymentId([2; 32]);
1557-
let refund = david.node
1557+
let refund = david.offers_handler
15581558
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
15591559
.unwrap()
15601560
.build().unwrap();
@@ -1681,7 +1681,7 @@ fn fails_creating_refund_or_sending_invoice_without_connected_peers() {
16811681

16821682
let absolute_expiry = david.node.duration_since_epoch() + MAX_SHORT_LIVED_RELATIVE_EXPIRY;
16831683
let payment_id = PaymentId([1; 32]);
1684-
match david.node.create_refund_builder(
1684+
match david.offers_handler.create_refund_builder(
16851685
10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None
16861686
) {
16871687
Ok(_) => panic!("Expected error"),
@@ -1692,7 +1692,7 @@ fn fails_creating_refund_or_sending_invoice_without_connected_peers() {
16921692
args.send_channel_ready = (true, true);
16931693
reconnect_nodes(args);
16941694

1695-
let refund = david.node
1695+
let refund = david.offers_handler
16961696
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
16971697
.unwrap()
16981698
.build().unwrap();
@@ -1750,7 +1750,7 @@ fn fails_sending_invoice_with_unsupported_chain_for_refund() {
17501750

17511751
let absolute_expiry = Duration::from_secs(u64::MAX);
17521752
let payment_id = PaymentId([1; 32]);
1753-
let refund = bob.node
1753+
let refund = bob.offers_handler
17541754
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
17551755
.unwrap()
17561756
.chain(Network::Signet)
@@ -1848,13 +1848,13 @@ fn fails_creating_refund_with_duplicate_payment_id() {
18481848
let absolute_expiry = Duration::from_secs(u64::MAX);
18491849
let payment_id = PaymentId([1; 32]);
18501850
assert!(
1851-
nodes[0].node.create_refund_builder(
1851+
nodes[0].offers_handler.create_refund_builder(
18521852
10_000, absolute_expiry, payment_id, Retry::Attempts(0), None
18531853
).is_ok()
18541854
);
18551855
expect_recent_payment!(nodes[0], RecentPaymentDetails::AwaitingInvoice, payment_id);
18561856

1857-
match nodes[0].node.create_refund_builder(
1857+
match nodes[0].offers_handler.create_refund_builder(
18581858
10_000, absolute_expiry, payment_id, Retry::Attempts(0), None
18591859
) {
18601860
Ok(_) => panic!("Expected error"),
@@ -1974,7 +1974,7 @@ fn fails_sending_invoice_without_blinded_payment_paths_for_refund() {
19741974

19751975
let absolute_expiry = Duration::from_secs(u64::MAX);
19761976
let payment_id = PaymentId([1; 32]);
1977-
let refund = david.node
1977+
let refund = david.offers_handler
19781978
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
19791979
.unwrap()
19801980
.build().unwrap();
@@ -2023,7 +2023,7 @@ fn fails_paying_invoice_more_than_once() {
20232023

20242024
let absolute_expiry = Duration::from_secs(u64::MAX);
20252025
let payment_id = PaymentId([1; 32]);
2026-
let refund = david.node
2026+
let refund = david.offers_handler
20272027
.create_refund_builder(10_000_000, absolute_expiry, payment_id, Retry::Attempts(0), None)
20282028
.unwrap()
20292029
.build().unwrap();

0 commit comments

Comments
 (0)