Skip to content

Commit 6b20fee

Browse files
committed
Introduce custom TLVs in pay_for_bolt11_invoice
Custom TLVs let the payer attach arbitrary data to the onion packet, enabling everything from richer metadata to custom authentication on the payee's side. Until now, this flexibility existed only through `send_payment`. The simpler `pay_for_bolt11_invoice` API offered no way to pass custom TLVs, limiting its usefulness in flows that rely on additional context. This commit adds custom TLV support to `pay_for_bolt11_invoice`, bringing it to feature parity.
1 parent e9c6bbc commit 6b20fee

6 files changed

Lines changed: 66 additions & 43 deletions

File tree

lightning-liquidity/tests/lsps2_integration_tests.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ use common::{
99

1010
use lightning::events::{ClosureReason, Event};
1111
use lightning::get_event_msg;
12-
use lightning::ln::channelmanager::PaymentId;
13-
use lightning::ln::channelmanager::Retry;
12+
use lightning::ln::channelmanager::{OptionalBolt11PaymentParams, PaymentId};
1413
use lightning::ln::functional_test_utils::*;
1514
use lightning::ln::msgs::BaseMessageHandler;
1615
use lightning::ln::msgs::ChannelMessageHandler;
@@ -1214,8 +1213,7 @@ fn client_trusts_lsp_end_to_end_test() {
12141213
&invoice,
12151214
PaymentId(invoice.payment_hash().0),
12161215
None,
1217-
Default::default(),
1218-
Retry::Attempts(3),
1216+
OptionalBolt11PaymentParams::default(),
12191217
)
12201218
.unwrap();
12211219

@@ -1687,8 +1685,7 @@ fn late_payment_forwarded_and_safe_after_force_close_does_not_broadcast() {
16871685
&invoice,
16881686
PaymentId(invoice.payment_hash().0),
16891687
None,
1690-
Default::default(),
1691-
Retry::Attempts(3),
1688+
OptionalBolt11PaymentParams::default(),
16921689
)
16931690
.unwrap();
16941691

@@ -1878,8 +1875,7 @@ fn htlc_timeout_before_client_claim_results_in_handling_failed() {
18781875
&invoice,
18791876
PaymentId(invoice.payment_hash().0),
18801877
None,
1881-
Default::default(),
1882-
Retry::Attempts(3),
1878+
OptionalBolt11PaymentParams::default(),
18831879
)
18841880
.unwrap();
18851881

@@ -2215,8 +2211,7 @@ fn client_trusts_lsp_partial_fee_does_not_trigger_broadcast() {
22152211
&invoice,
22162212
PaymentId(invoice.payment_hash().0),
22172213
None,
2218-
Default::default(),
2219-
Retry::Attempts(3),
2214+
OptionalBolt11PaymentParams::default(),
22202215
)
22212216
.unwrap();
22222217

lightning/src/ln/bolt11_payment_tests.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@
1010
//! Tests for verifying the correct end-to-end handling of BOLT11 payments, including metadata propagation.
1111
1212
use crate::events::Event;
13-
use crate::ln::channelmanager::{PaymentId, Retry};
13+
use crate::ln::channelmanager::{OptionalBolt11PaymentParams, PaymentId};
1414
use crate::ln::functional_test_utils::*;
1515
use crate::ln::msgs::ChannelMessageHandler;
1616
use crate::ln::outbound_payment::Bolt11PaymentError;
17-
use crate::routing::router::RouteParametersConfig;
1817
use crate::sign::{NodeSigner, Recipient};
1918
use bitcoin::hashes::sha256::Hash as Sha256;
2019
use bitcoin::hashes::Hash;
@@ -55,8 +54,7 @@ fn payment_metadata_end_to_end_for_invoice_with_amount() {
5554
&invoice,
5655
PaymentId(payment_hash.0),
5756
Some(100),
58-
RouteParametersConfig::default(),
59-
Retry::Attempts(0),
57+
OptionalBolt11PaymentParams::default(),
6058
) {
6159
Err(Bolt11PaymentError::InvalidAmount) => (),
6260
_ => panic!("Unexpected result"),
@@ -68,8 +66,7 @@ fn payment_metadata_end_to_end_for_invoice_with_amount() {
6866
&invoice,
6967
PaymentId(payment_hash.0),
7068
None,
71-
RouteParametersConfig::default(),
72-
Retry::Attempts(0),
69+
OptionalBolt11PaymentParams::default(),
7370
)
7471
.unwrap();
7572

@@ -123,8 +120,7 @@ fn payment_metadata_end_to_end_for_invoice_with_no_amount() {
123120
&invoice,
124121
PaymentId(payment_hash.0),
125122
None,
126-
RouteParametersConfig::default(),
127-
Retry::Attempts(0),
123+
OptionalBolt11PaymentParams::default(),
128124
) {
129125
Err(Bolt11PaymentError::InvalidAmount) => (),
130126
_ => panic!("Unexpected result"),
@@ -136,8 +132,7 @@ fn payment_metadata_end_to_end_for_invoice_with_no_amount() {
136132
&invoice,
137133
PaymentId(payment_hash.0),
138134
Some(50_000),
139-
RouteParametersConfig::default(),
140-
Retry::Attempts(0),
135+
OptionalBolt11PaymentParams::default(),
141136
)
142137
.unwrap();
143138

lightning/src/ln/channelmanager.rs

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ use crate::ln::our_peer_storage::{EncryptedOurPeerStorage, PeerStorageMonitorHol
8585
#[cfg(test)]
8686
use crate::ln::outbound_payment;
8787
use crate::ln::outbound_payment::{
88-
OutboundPayments, PendingOutboundPayment, RetryableInvoiceRequest, SendAlongPathArgs,
89-
StaleExpiration,
88+
OutboundPayments, PendingOutboundPayment, RecipientCustomTlvs, RetryableInvoiceRequest,
89+
SendAlongPathArgs, StaleExpiration,
9090
};
9191
use crate::ln::types::ChannelId;
9292
use crate::offers::async_receive_offer_cache::AsyncReceiveOfferCache;
@@ -674,6 +674,36 @@ impl Readable for InterceptId {
674674
}
675675
}
676676

677+
/// Optional arguments to [`ChannelManager::pay_for_bolt11_invoice`]
678+
///
679+
/// These fields will often not need to be set, and the provided [`Self::default`] can be used.
680+
pub struct OptionalBolt11PaymentParams {
681+
/// A set of custom tlvs, user can send along the payment.
682+
pub custom_tlvs: RecipientCustomTlvs,
683+
/// Pathfinding options which tweak how the path is constructed to the recipient.
684+
pub route_params_config: RouteParametersConfig,
685+
/// The number of tries or time during which we'll retry this payment if some paths to the
686+
/// recipient fail.
687+
///
688+
/// Once the retry limit is reached, further path failures will not be retried and the payment
689+
/// will ultimately fail once all pending paths have failed (generating an
690+
/// [`Event::PaymentFailed`]).
691+
pub retry_strategy: Retry,
692+
}
693+
694+
impl Default for OptionalBolt11PaymentParams {
695+
fn default() -> Self {
696+
Self {
697+
custom_tlvs: RecipientCustomTlvs::new(vec![]).unwrap(),
698+
route_params_config: Default::default(),
699+
#[cfg(feature = "std")]
700+
retry_strategy: Retry::Timeout(core::time::Duration::from_secs(2)),
701+
#[cfg(not(feature = "std"))]
702+
retry_strategy: Retry::Attempts(3),
703+
}
704+
}
705+
}
706+
677707
/// Optional arguments to [`ChannelManager::pay_for_offer`]
678708
#[cfg_attr(
679709
feature = "dnssec",
@@ -2277,19 +2307,19 @@ where
22772307
/// # use bitcoin::hashes::Hash;
22782308
/// # use lightning::events::{Event, EventsProvider};
22792309
/// # use lightning::types::payment::PaymentHash;
2280-
/// # use lightning::ln::channelmanager::{AChannelManager, PaymentId, RecentPaymentDetails, Retry};
2281-
/// # use lightning::routing::router::RouteParametersConfig;
2310+
/// # use lightning::ln::channelmanager::{AChannelManager, OptionalBolt11PaymentParams, PaymentId, RecentPaymentDetails, Retry};
22822311
/// # use lightning_invoice::Bolt11Invoice;
22832312
/// #
22842313
/// # fn example<T: AChannelManager>(
2285-
/// # channel_manager: T, invoice: &Bolt11Invoice, route_params_config: RouteParametersConfig,
2314+
/// # channel_manager: T, invoice: &Bolt11Invoice, optional_params: OptionalBolt11PaymentParams,
22862315
/// # retry: Retry
22872316
/// # ) {
22882317
/// # let channel_manager = channel_manager.get_cm();
22892318
/// # let payment_id = PaymentId([42; 32]);
22902319
/// # let payment_hash = invoice.payment_hash();
2320+
///
22912321
/// match channel_manager.pay_for_bolt11_invoice(
2292-
/// invoice, payment_id, None, route_params_config, retry
2322+
/// invoice, payment_id, None, optional_params
22932323
/// ) {
22942324
/// Ok(()) => println!("Sending payment with hash {}", payment_hash),
22952325
/// Err(e) => println!("Failed sending payment with hash {}: {:?}", payment_hash, e),
@@ -5498,16 +5528,15 @@ where
54985528
/// To use default settings, call the function with [`RouteParametersConfig::default`].
54995529
pub fn pay_for_bolt11_invoice(
55005530
&self, invoice: &Bolt11Invoice, payment_id: PaymentId, amount_msats: Option<u64>,
5501-
route_params_config: RouteParametersConfig, retry_strategy: Retry,
5531+
optional_params: OptionalBolt11PaymentParams,
55025532
) -> Result<(), Bolt11PaymentError> {
55035533
let best_block_height = self.best_block.read().unwrap().height;
55045534
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
55055535
self.pending_outbound_payments.pay_for_bolt11_invoice(
55065536
invoice,
55075537
payment_id,
55085538
amount_msats,
5509-
route_params_config,
5510-
retry_strategy,
5539+
optional_params,
55115540
&self.router,
55125541
self.list_usable_channels(),
55135542
|| self.compute_inflight_htlcs(),

lightning/src/ln/invoice_utils.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -615,8 +615,8 @@ mod test {
615615
use super::*;
616616
use crate::chain::channelmonitor::HTLC_FAIL_BACK_BUFFER;
617617
use crate::ln::channelmanager::{
618-
Bolt11InvoiceParameters, PaymentId, PhantomRouteHints, RecipientOnionFields, Retry,
619-
MIN_FINAL_CLTV_EXPIRY_DELTA,
618+
Bolt11InvoiceParameters, OptionalBolt11PaymentParams, PaymentId, PhantomRouteHints,
619+
RecipientOnionFields, Retry, MIN_FINAL_CLTV_EXPIRY_DELTA,
620620
};
621621
use crate::ln::functional_test_utils::*;
622622
use crate::ln::msgs::{BaseMessageHandler, ChannelMessageHandler, MessageSendEvent};
@@ -707,10 +707,14 @@ mod test {
707707
assert_eq!(invoice.route_hints()[0].0[0].htlc_minimum_msat, chan.inbound_htlc_minimum_msat);
708708
assert_eq!(invoice.route_hints()[0].0[0].htlc_maximum_msat, chan.inbound_htlc_maximum_msat);
709709

710-
let retry = Retry::Attempts(0);
711710
nodes[0]
712711
.node
713-
.pay_for_bolt11_invoice(&invoice, PaymentId([42; 32]), None, Default::default(), retry)
712+
.pay_for_bolt11_invoice(
713+
&invoice,
714+
PaymentId([42; 32]),
715+
None,
716+
OptionalBolt11PaymentParams::default(),
717+
)
714718
.unwrap();
715719
check_added_monitors(&nodes[0], 1);
716720

lightning/src/ln/outbound_payment.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ use crate::blinded_path::{IntroductionNode, NodeIdLookUp};
1818
use crate::events::{self, PaidBolt12Invoice, PaymentFailureReason};
1919
use crate::ln::channel_state::ChannelDetails;
2020
use crate::ln::channelmanager::{
21-
EventCompletionAction, HTLCSource, PaymentCompleteUpdate, PaymentId,
21+
EventCompletionAction, HTLCSource, OptionalBolt11PaymentParams, PaymentCompleteUpdate,
22+
PaymentId,
2223
};
2324
use crate::ln::onion_utils;
2425
use crate::ln::onion_utils::{DecodedOnionFailure, HTLCFailReason};
@@ -949,8 +950,7 @@ where
949950
pub(super) fn pay_for_bolt11_invoice<R: Deref, ES: Deref, NS: Deref, IH, SP>(
950951
&self, invoice: &Bolt11Invoice, payment_id: PaymentId,
951952
amount_msats: Option<u64>,
952-
route_params_config: RouteParametersConfig,
953-
retry_strategy: Retry,
953+
optional_params: OptionalBolt11PaymentParams,
954954
router: &R,
955955
first_hops: Vec<ChannelDetails>, compute_inflight_htlcs: IH, entropy_source: &ES,
956956
node_signer: &NS, best_block_height: u32,
@@ -972,19 +972,20 @@ where
972972
(None, None) => return Err(Bolt11PaymentError::InvalidAmount),
973973
};
974974

975-
let mut recipient_onion = RecipientOnionFields::secret_only(*invoice.payment_secret());
975+
let mut recipient_onion = RecipientOnionFields::secret_only(*invoice.payment_secret())
976+
.with_custom_tlvs(optional_params.custom_tlvs);
976977
recipient_onion.payment_metadata = invoice.payment_metadata().map(|v| v.clone());
977978

978979
let payment_params = PaymentParameters::from_bolt11_invoice(invoice)
979-
.with_user_config_ignoring_fee_limit(route_params_config);
980+
.with_user_config_ignoring_fee_limit(optional_params.route_params_config);
980981

981982
let mut route_params = RouteParameters::from_payment_params_and_value(payment_params, amount);
982983

983-
if let Some(max_fee_msat) = route_params_config.max_total_routing_fee_msat {
984+
if let Some(max_fee_msat) = optional_params.route_params_config.max_total_routing_fee_msat {
984985
route_params.max_total_routing_fee_msat = Some(max_fee_msat);
985986
}
986987

987-
self.send_payment_for_non_bolt12_invoice(payment_id, payment_hash, recipient_onion, None, retry_strategy, route_params,
988+
self.send_payment_for_non_bolt12_invoice(payment_id, payment_hash, recipient_onion, None, optional_params.retry_strategy, route_params,
988989
router, first_hops, compute_inflight_htlcs,
989990
entropy_source, node_signer, best_block_height,
990991
pending_events, send_payment_along_path

lightning/src/ln/payment_tests.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5403,11 +5403,10 @@ fn max_out_mpp_path() {
54035403
..Default::default()
54045404
};
54055405
let invoice = nodes[2].node.create_bolt11_invoice(invoice_params).unwrap();
5406-
let route_params_cfg = crate::routing::router::RouteParametersConfig::default();
5406+
let optional_params = crate::ln::channelmanager::OptionalBolt11PaymentParams::default();
54075407

54085408
let id = PaymentId([42; 32]);
5409-
let retry = Retry::Attempts(0);
5410-
nodes[0].node.pay_for_bolt11_invoice(&invoice, id, None, route_params_cfg, retry).unwrap();
5409+
nodes[0].node.pay_for_bolt11_invoice(&invoice, id, None, optional_params).unwrap();
54115410

54125411
assert!(nodes[0].node.list_recent_payments().len() == 1);
54135412
check_added_monitors(&nodes[0], 2); // one monitor update per MPP part

0 commit comments

Comments
 (0)