Skip to content

Commit 8679d8d

Browse files
authored
Merge pull request #4263 from shaavan/payment-custom
Add custom TLV in Bolt11 Payer API
2 parents 2962de8 + 53e668b commit 8679d8d

9 files changed

Lines changed: 171 additions & 103 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/blinded_payment_tests.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crate::ln::msgs::{
2222
};
2323
use crate::ln::onion_payment;
2424
use crate::ln::onion_utils::{self, LocalHTLCFailureReason};
25-
use crate::ln::outbound_payment::{Retry, IDEMPOTENCY_TIMEOUT_TICKS};
25+
use crate::ln::outbound_payment::{RecipientCustomTlvs, Retry, IDEMPOTENCY_TIMEOUT_TICKS};
2626
use crate::ln::types::ChannelId;
2727
use crate::offers::invoice::UnsignedBolt12Invoice;
2828
use crate::prelude::*;
@@ -1431,8 +1431,7 @@ fn custom_tlvs_to_blinded_path() {
14311431
);
14321432

14331433
let recipient_onion_fields = RecipientOnionFields::spontaneous_empty()
1434-
.with_custom_tlvs(vec![((1 << 16) + 1, vec![42, 42])])
1435-
.unwrap();
1434+
.with_custom_tlvs(RecipientCustomTlvs::new(vec![((1 << 16) + 1, vec![42, 42])]).unwrap());
14361435
nodes[0].node.send_payment(payment_hash, recipient_onion_fields.clone(),
14371436
PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
14381437
check_added_monitors(&nodes[0], 1);

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",
@@ -2232,19 +2262,19 @@ impl<
22322262
/// # use bitcoin::hashes::Hash;
22332263
/// # use lightning::events::{Event, EventsProvider};
22342264
/// # use lightning::types::payment::PaymentHash;
2235-
/// # use lightning::ln::channelmanager::{AChannelManager, PaymentId, RecentPaymentDetails, Retry};
2236-
/// # use lightning::routing::router::RouteParametersConfig;
2265+
/// # use lightning::ln::channelmanager::{AChannelManager, OptionalBolt11PaymentParams, PaymentId, RecentPaymentDetails, Retry};
22372266
/// # use lightning_invoice::Bolt11Invoice;
22382267
/// #
22392268
/// # fn example<T: AChannelManager>(
2240-
/// # channel_manager: T, invoice: &Bolt11Invoice, route_params_config: RouteParametersConfig,
2269+
/// # channel_manager: T, invoice: &Bolt11Invoice, optional_params: OptionalBolt11PaymentParams,
22412270
/// # retry: Retry
22422271
/// # ) {
22432272
/// # let channel_manager = channel_manager.get_cm();
22442273
/// # let payment_id = PaymentId([42; 32]);
22452274
/// # let payment_hash = invoice.payment_hash();
2275+
///
22462276
/// match channel_manager.pay_for_bolt11_invoice(
2247-
/// invoice, payment_id, None, route_params_config, retry
2277+
/// invoice, payment_id, None, optional_params
22482278
/// ) {
22492279
/// Ok(()) => println!("Sending payment with hash {}", payment_hash),
22502280
/// Err(e) => println!("Failed sending payment with hash {}: {:?}", payment_hash, e),
@@ -5427,7 +5457,7 @@ impl<
54275457
/// To use default settings, call the function with [`RouteParametersConfig::default`].
54285458
pub fn pay_for_bolt11_invoice(
54295459
&self, invoice: &Bolt11Invoice, payment_id: PaymentId, amount_msats: Option<u64>,
5430-
route_params_config: RouteParametersConfig, retry_strategy: Retry,
5460+
optional_params: OptionalBolt11PaymentParams,
54315461
) -> Result<(), Bolt11PaymentError> {
54325462
let best_block_height = self.best_block.read().unwrap().height;
54335463
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
@@ -5436,8 +5466,7 @@ impl<
54365466
invoice,
54375467
payment_id,
54385468
amount_msats,
5439-
route_params_config,
5440-
retry_strategy,
5469+
optional_params,
54415470
&self.router,
54425471
self.list_usable_channels(),
54435472
|| self.compute_inflight_htlcs(),

lightning/src/ln/invoice_utils.rs

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -588,12 +588,13 @@ mod test {
588588
use super::*;
589589
use crate::chain::channelmonitor::HTLC_FAIL_BACK_BUFFER;
590590
use crate::ln::channelmanager::{
591-
Bolt11InvoiceParameters, PaymentId, PhantomRouteHints, RecipientOnionFields, Retry,
592-
MIN_FINAL_CLTV_EXPIRY_DELTA,
591+
Bolt11InvoiceParameters, OptionalBolt11PaymentParams, PaymentId, PhantomRouteHints,
592+
RecipientOnionFields, Retry, MIN_FINAL_CLTV_EXPIRY_DELTA,
593593
};
594594
use crate::ln::functional_test_utils::*;
595595
use crate::ln::msgs::{BaseMessageHandler, ChannelMessageHandler, MessageSendEvent};
596-
use crate::routing::router::{PaymentParameters, RouteParameters};
596+
use crate::ln::outbound_payment::RecipientCustomTlvs;
597+
use crate::routing::router::{PaymentParameters, RouteParameters, RouteParametersConfig};
597598
use crate::sign::PhantomKeysManager;
598599
use crate::types::payment::{PaymentHash, PaymentPreimage};
599600
use crate::util::config::UserConfig;
@@ -636,26 +637,26 @@ mod test {
636637
}
637638

638639
#[test]
639-
fn create_and_pay_for_bolt11_invoice() {
640+
fn create_and_pay_for_bolt11_invoice_with_custom_tlvs() {
640641
let chanmon_cfgs = create_chanmon_cfgs(2);
641642
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
642643
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
643644
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
644645
create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 10001);
645646

646-
let node_a_id = nodes[0].node.get_our_node_id();
647-
647+
let amt_msat = 10_000;
648648
let description =
649649
Bolt11InvoiceDescription::Direct(Description::new("test".to_string()).unwrap());
650650
let non_default_invoice_expiry_secs = 4200;
651+
651652
let invoice_params = Bolt11InvoiceParameters {
652-
amount_msats: Some(10_000),
653+
amount_msats: Some(amt_msat),
653654
description,
654655
invoice_expiry_delta_secs: Some(non_default_invoice_expiry_secs),
655656
..Default::default()
656657
};
657658
let invoice = nodes[1].node.create_bolt11_invoice(invoice_params).unwrap();
658-
assert_eq!(invoice.amount_milli_satoshis(), Some(10_000));
659+
assert_eq!(invoice.amount_milli_satoshis(), Some(amt_msat));
659660
// If no `min_final_cltv_expiry_delta` is specified, then it should be `MIN_FINAL_CLTV_EXPIRY_DELTA`.
660661
assert_eq!(invoice.min_final_cltv_expiry_delta(), MIN_FINAL_CLTV_EXPIRY_DELTA as u64);
661662
assert_eq!(
@@ -667,6 +668,10 @@ mod test {
667668
Duration::from_secs(non_default_invoice_expiry_secs.into())
668669
);
669670

671+
let (payment_hash, payment_secret) = (invoice.payment_hash(), *invoice.payment_secret());
672+
673+
let preimage = nodes[1].node.get_payment_preimage(payment_hash, payment_secret).unwrap();
674+
670675
// Invoice SCIDs should always use inbound SCID aliases over the real channel ID, if one is
671676
// available.
672677
let chan = &nodes[1].node.list_usable_channels()[0];
@@ -680,21 +685,34 @@ mod test {
680685
assert_eq!(invoice.route_hints()[0].0[0].htlc_minimum_msat, chan.inbound_htlc_minimum_msat);
681686
assert_eq!(invoice.route_hints()[0].0[0].htlc_maximum_msat, chan.inbound_htlc_maximum_msat);
682687

683-
let retry = Retry::Attempts(0);
688+
let custom_tlvs = RecipientCustomTlvs::new(vec![(65537, vec![42; 42])]).unwrap();
689+
let optional_params = OptionalBolt11PaymentParams {
690+
custom_tlvs: custom_tlvs.clone(),
691+
route_params_config: RouteParametersConfig::default(),
692+
retry_strategy: Retry::Attempts(0),
693+
};
694+
684695
nodes[0]
685696
.node
686-
.pay_for_bolt11_invoice(&invoice, PaymentId([42; 32]), None, Default::default(), retry)
697+
.pay_for_bolt11_invoice(&invoice, PaymentId([42; 32]), None, optional_params)
687698
.unwrap();
688699
check_added_monitors(&nodes[0], 1);
689700

690701
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
691702
assert_eq!(events.len(), 1);
692-
let payment_event = SendEvent::from_event(events.remove(0));
693-
nodes[1].node.handle_update_add_htlc(node_a_id, &payment_event.msgs[0]);
694-
nodes[1].node.handle_commitment_signed_batch_test(node_a_id, &payment_event.commitment_msg);
695-
check_added_monitors(&nodes[1], 1);
696-
let events = nodes[1].node.get_and_clear_pending_msg_events();
697-
assert_eq!(events.len(), 2);
703+
let ev = remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut events);
704+
705+
let path = &[&nodes[1]];
706+
let args = PassAlongPathArgs::new(&nodes[0], path, amt_msat, payment_hash, ev)
707+
.with_payment_preimage(preimage)
708+
.with_payment_secret(payment_secret)
709+
.with_custom_tlvs(custom_tlvs.clone().into_inner());
710+
711+
do_pass_along_path(args);
712+
claim_payment_along_route(
713+
ClaimAlongRouteArgs::new(&nodes[0], &[&[&nodes[1]]], preimage)
714+
.with_custom_tlvs(custom_tlvs.into_inner()),
715+
);
698716
}
699717

700718
fn do_create_invoice_min_final_cltv_delta(with_custom_delta: bool) {

lightning/src/ln/max_payment_path_len_tests.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ use crate::ln::msgs;
2323
use crate::ln::msgs::{BaseMessageHandler, OnionMessageHandler};
2424
use crate::ln::onion_utils;
2525
use crate::ln::onion_utils::MIN_FINAL_VALUE_ESTIMATE_WITH_OVERPAY;
26-
use crate::ln::outbound_payment::{RecipientOnionFields, Retry, RetryableSendFailure};
26+
use crate::ln::outbound_payment::{
27+
RecipientCustomTlvs, RecipientOnionFields, Retry, RetryableSendFailure,
28+
};
2729
use crate::prelude::*;
2830
use crate::routing::router::{
2931
PaymentParameters, RouteParameters, DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA,
@@ -259,9 +261,9 @@ fn one_hop_blinded_path_with_custom_tlv() {
259261
- final_payload_len_without_custom_tlv;
260262

261263
// Check that we can send the maximum custom TLV with 1 blinded hop.
262-
let max_sized_onion = RecipientOnionFields::spontaneous_empty()
263-
.with_custom_tlvs(vec![(CUSTOM_TLV_TYPE, vec![42; max_custom_tlv_len])])
264-
.unwrap();
264+
let max_sized_onion = RecipientOnionFields::spontaneous_empty().with_custom_tlvs(
265+
RecipientCustomTlvs::new(vec![(CUSTOM_TLV_TYPE, vec![42; max_custom_tlv_len])]).unwrap(),
266+
);
265267
let id = PaymentId(payment_hash.0);
266268
let no_retry = Retry::Attempts(0);
267269
nodes[1]
@@ -385,9 +387,9 @@ fn blinded_path_with_custom_tlv() {
385387
- reserved_packet_bytes_without_custom_tlv;
386388

387389
// Check that we can send the maximum custom TLV size with 0 intermediate unblinded hops.
388-
let max_sized_onion = RecipientOnionFields::spontaneous_empty()
389-
.with_custom_tlvs(vec![(CUSTOM_TLV_TYPE, vec![42; max_custom_tlv_len])])
390-
.unwrap();
390+
let max_sized_onion = RecipientOnionFields::spontaneous_empty().with_custom_tlvs(
391+
RecipientCustomTlvs::new(vec![(CUSTOM_TLV_TYPE, vec![42; max_custom_tlv_len])]).unwrap(),
392+
);
391393
let no_retry = Retry::Attempts(0);
392394
let id = PaymentId(payment_hash.0);
393395
nodes[1]

lightning/src/ln/msgs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3753,7 +3753,7 @@ impl<'a> Writeable for OutboundOnionPayload<'a> {
37533753
ref invoice_request,
37543754
ref custom_tlvs,
37553755
} => {
3756-
// We need to update [`ln::outbound_payment::RecipientOnionFields::with_custom_tlvs`]
3756+
// We need to update [`ln::outbound_payments::RecipientCustomTlvs::new`]
37573757
// to reject any reserved types in the experimental range if new ones are ever
37583758
// standardized.
37593759
let invoice_request_tlv = invoice_request.map(|invreq| (77_777, invreq.encode())); // TODO: update TLV type once the async payments spec is merged

0 commit comments

Comments
 (0)