Skip to content

Commit 99b7dba

Browse files
Set UpdateAddHTLC::hold_htlc for offline payees
As part of supporting sending payments as an often-offline sender, the sender needs to be able to set a flag in their update_add_htlc message indicating that the HTLC should be held until receipt of a release_held_htlc onion message from the often-offline payment recipient. The prior commits laid groundwork to finally set the flag here in this commit. See-also BOLTs PR 989
1 parent feeab19 commit 99b7dba

File tree

3 files changed

+54
-25
lines changed

3 files changed

+54
-25
lines changed

lightning/src/ln/channel.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9265,7 +9265,7 @@ where
92659265
onion_routing_packet: (**onion_packet).clone(),
92669266
skimmed_fee_msat: htlc.skimmed_fee_msat,
92679267
blinding_point: htlc.blinding_point,
9268-
hold_htlc: None, // Will be set by the async sender when support is added
9268+
hold_htlc: htlc.hold_htlc,
92699269
});
92709270
}
92719271
}

lightning/src/ln/channelmanager.rs

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4994,6 +4994,7 @@ where
49944994
invoice_request: None,
49954995
bolt12_invoice: None,
49964996
session_priv_bytes,
4997+
hold_htlc_at_next_hop: false,
49974998
})
49984999
}
49995000

@@ -5009,6 +5010,7 @@ where
50095010
invoice_request,
50105011
bolt12_invoice,
50115012
session_priv_bytes,
5013+
hold_htlc_at_next_hop,
50125014
} = args;
50135015
// The top-level caller should hold the total_consistency_lock read lock.
50145016
debug_assert!(self.total_consistency_lock.try_write().is_err());
@@ -5098,7 +5100,7 @@ where
50985100
htlc_source,
50995101
onion_packet,
51005102
None,
5101-
false,
5103+
hold_htlc_at_next_hop,
51025104
&self.fee_estimator,
51035105
&&logger,
51045106
);
@@ -5483,19 +5485,35 @@ where
54835485
},
54845486
};
54855487

5486-
let enqueue_held_htlc_available_res = self.flow.enqueue_held_htlc_available(
5487-
invoice,
5488-
payment_id,
5489-
self.get_peers_for_blinded_path(),
5490-
);
5491-
if enqueue_held_htlc_available_res.is_err() {
5492-
self.abandon_payment_with_reason(
5488+
// If the call to `Self::hold_htlc_channels` succeeded, then we are a private node and can
5489+
// hold the HTLCs for this payment at our next-hop channel counterparty until the recipient
5490+
// comes online. This allows us to go offline after locking in the HTLCs.
5491+
if let Ok(channels) = self.hold_htlc_channels() {
5492+
if let Err(e) =
5493+
self.send_payment_for_static_invoice_no_persist(payment_id, channels, true)
5494+
{
5495+
log_trace!(
5496+
self.logger,
5497+
"Failed to send held HTLC with payment id {}: {:?}",
5498+
payment_id,
5499+
e
5500+
);
5501+
}
5502+
} else {
5503+
let enqueue_held_htlc_available_res = self.flow.enqueue_held_htlc_available(
5504+
invoice,
54935505
payment_id,
5494-
PaymentFailureReason::BlindedPathCreationFailed,
5506+
self.get_peers_for_blinded_path(),
54955507
);
5496-
res = Err(Bolt12PaymentError::BlindedPathCreationFailed);
5497-
return NotifyOption::DoPersist;
5498-
};
5508+
if enqueue_held_htlc_available_res.is_err() {
5509+
self.abandon_payment_with_reason(
5510+
payment_id,
5511+
PaymentFailureReason::BlindedPathCreationFailed,
5512+
);
5513+
res = Err(Bolt12PaymentError::BlindedPathCreationFailed);
5514+
return NotifyOption::DoPersist;
5515+
};
5516+
}
54995517

55005518
NotifyOption::DoPersist
55015519
});
@@ -5532,7 +5550,7 @@ where
55325550
let first_hops = self.list_usable_channels();
55335551
PersistenceNotifierGuard::optionally_notify(self, || {
55345552
let outbound_pmts_res =
5535-
self.send_payment_for_static_invoice_no_persist(payment_id, first_hops);
5553+
self.send_payment_for_static_invoice_no_persist(payment_id, first_hops, false);
55365554
match outbound_pmts_res {
55375555
Err(Bolt12PaymentError::UnexpectedInvoice)
55385556
| Err(Bolt12PaymentError::DuplicateInvoice) => {
@@ -5550,11 +5568,12 @@ where
55505568

55515569
/// Useful if the caller is already triggering a persist of the `ChannelManager`.
55525570
fn send_payment_for_static_invoice_no_persist(
5553-
&self, payment_id: PaymentId, first_hops: Vec<ChannelDetails>,
5571+
&self, payment_id: PaymentId, first_hops: Vec<ChannelDetails>, hold_htlcs_at_next_hop: bool,
55545572
) -> Result<(), Bolt12PaymentError> {
55555573
let best_block_height = self.best_block.read().unwrap().height;
55565574
self.pending_outbound_payments.send_payment_for_static_invoice(
55575575
payment_id,
5576+
hold_htlcs_at_next_hop,
55585577
&self.router,
55595578
first_hops,
55605579
|| self.compute_inflight_htlcs(),

lightning/src/ln/outbound_payment.rs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,7 @@ pub(super) struct SendAlongPathArgs<'a> {
834834
pub invoice_request: Option<&'a InvoiceRequest>,
835835
pub bolt12_invoice: Option<&'a PaidBolt12Invoice>,
836836
pub session_priv_bytes: [u8; 32],
837+
pub hold_htlc_at_next_hop: bool,
837838
}
838839

839840
pub(super) struct OutboundPayments<L: Deref>
@@ -999,9 +1000,9 @@ where
9991000
}
10001001
let invoice = PaidBolt12Invoice::Bolt12Invoice(invoice.clone());
10011002
self.send_payment_for_bolt12_invoice_internal(
1002-
payment_id, payment_hash, None, None, invoice, route_params, retry_strategy, router, first_hops,
1003-
inflight_htlcs, entropy_source, node_signer, node_id_lookup, secp_ctx, best_block_height,
1004-
pending_events, send_payment_along_path
1003+
payment_id, payment_hash, None, None, invoice, route_params, retry_strategy, false, router,
1004+
first_hops, inflight_htlcs, entropy_source, node_signer, node_id_lookup, secp_ctx,
1005+
best_block_height, pending_events, send_payment_along_path
10051006
)
10061007
}
10071008

@@ -1012,7 +1013,7 @@ where
10121013
&self, payment_id: PaymentId, payment_hash: PaymentHash,
10131014
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<&InvoiceRequest>,
10141015
bolt12_invoice: PaidBolt12Invoice,
1015-
mut route_params: RouteParameters, retry_strategy: Retry, router: &R,
1016+
mut route_params: RouteParameters, retry_strategy: Retry, hold_htlcs_at_next_hop: bool, router: &R,
10161017
first_hops: Vec<ChannelDetails>, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS,
10171018
node_id_lookup: &NL, secp_ctx: &Secp256k1<secp256k1::All>, best_block_height: u32,
10181019
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
@@ -1097,7 +1098,7 @@ where
10971098

10981099
let result = self.pay_route_internal(
10991100
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, Some(&bolt12_invoice), payment_id,
1100-
Some(route_params.final_value_msat), &onion_session_privs, false, node_signer,
1101+
Some(route_params.final_value_msat), &onion_session_privs, hold_htlcs_at_next_hop, node_signer,
11011102
best_block_height, &send_payment_along_path
11021103
);
11031104
log_info!(
@@ -1231,9 +1232,9 @@ where
12311232
IH,
12321233
SP,
12331234
>(
1234-
&self, payment_id: PaymentId, router: &R, first_hops: Vec<ChannelDetails>,
1235-
inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS, node_id_lookup: &NL,
1236-
secp_ctx: &Secp256k1<secp256k1::All>, best_block_height: u32,
1235+
&self, payment_id: PaymentId, hold_htlcs_at_next_hop: bool, router: &R,
1236+
first_hops: Vec<ChannelDetails>, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS,
1237+
node_id_lookup: &NL, secp_ctx: &Secp256k1<secp256k1::All>, best_block_height: u32,
12371238
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
12381239
send_payment_along_path: SP,
12391240
) -> Result<(), Bolt12PaymentError>
@@ -1249,7 +1250,7 @@ where
12491250
payment_hash,
12501251
keysend_preimage,
12511252
route_params,
1252-
retry_strategy,
1253+
mut retry_strategy,
12531254
invoice_request,
12541255
invoice,
12551256
) = match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
@@ -1274,6 +1275,14 @@ where
12741275
},
12751276
hash_map::Entry::Vacant(_) => return Err(Bolt12PaymentError::UnexpectedInvoice),
12761277
};
1278+
1279+
// If we expect the HTLCs for this payment to be held at our next-hop counterparty, don't
1280+
// retry the payment. In future iterations of this feature, we will send this payment via
1281+
// trampoline and the counterparty will retry on our behalf.
1282+
if hold_htlcs_at_next_hop {
1283+
retry_strategy = Retry::Attempts(0);
1284+
}
1285+
12771286
let invoice = PaidBolt12Invoice::StaticInvoice(invoice);
12781287
self.send_payment_for_bolt12_invoice_internal(
12791288
payment_id,
@@ -1283,6 +1292,7 @@ where
12831292
invoice,
12841293
route_params,
12851294
retry_strategy,
1295+
hold_htlcs_at_next_hop,
12861296
router,
12871297
first_hops,
12881298
inflight_htlcs,
@@ -2116,7 +2126,7 @@ where
21162126
let path_res = send_payment_along_path(SendAlongPathArgs {
21172127
path: &path, payment_hash: &payment_hash, recipient_onion, total_value,
21182128
cur_height, payment_id, keysend_preimage: &keysend_preimage, invoice_request,
2119-
bolt12_invoice,
2129+
bolt12_invoice, hold_htlc_at_next_hop: hold_htlcs_at_next_hop,
21202130
session_priv_bytes: *session_priv_bytes
21212131
});
21222132
results.push(path_res);

0 commit comments

Comments
 (0)