Skip to content

Commit 4267436

Browse files
committed
Reuse the PaymentHash as PaymentId for new outbound payments
In c986e52, an `MppId` was added to `HTLCSource` objects as a way of correlating HTLCs which belong to the same payment when the `ChannelManager` sees an HTLC succeed/fail. This allows it to have awareness of the state of all HTLCs in a payment when it generates the ultimate user-facing payment success/failure events. This was used in the same PR to avoid generating duplicative success/failure events for a single payment. Because the field was only used as an internal token to correlate HTLCs, and retries were not supported, it was generated randomly by calling the `KeysInterface`'s 32-byte random-fetching function. This also provided a backwards-compatibility story as the existing HTLC randomization key was re-used for older clients. In 28eea12 `MppId` was renamed to the current `PaymentId` which was then used expose the `retry_payment` interface, allowing users to send new HTLCs which are considered a part of an existing payment. At no point has the payment-sending API seriously considered idempotency, a major drawback which leaves the API unsafe in most deployments. Luckily, there is a simple solution - because the `PaymentId` must be unique, and because payment information for a given payment is held for several blocks after a payment completes/fails, it represents an obvious idempotency token. Here we simply reuse the `PaymentHash` as the `PaymentId` in `send_payment`, ensuring idempotency of payments with a given hash.
1 parent 559ed20 commit 4267436

File tree

5 files changed

+116
-58
lines changed

5 files changed

+116
-58
lines changed

lightning-invoice/src/utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ where
526526
&self, route: &Route, payment_preimage: PaymentPreimage,
527527
) -> Result<PaymentId, PaymentSendFailure> {
528528
self.send_spontaneous_payment(route, Some(payment_preimage))
529-
.map(|(_, payment_id)| payment_id)
529+
.map(|payment_hash| PaymentId(payment_hash.0))
530530
}
531531

532532
fn retry_payment(

lightning/src/ln/channelmanager.rs

Lines changed: 93 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2454,35 +2454,23 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
24542454
let mut channel_lock = self.channel_state.lock().unwrap();
24552455

24562456
let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
2457-
let payment_entry = pending_outbounds.entry(payment_id);
2458-
if let hash_map::Entry::Occupied(payment) = &payment_entry {
2459-
if !payment.get().is_retryable() {
2457+
let payment = if let Some(payment) = pending_outbounds.get_mut(&payment_id) {
2458+
if !payment.is_retryable() {
24602459
return Err(APIError::RouteError {
24612460
err: "Payment already completed"
24622461
});
24632462
}
2464-
}
2463+
payment
2464+
} else {
2465+
debug_assert!(false, "This shouldn't happen generally as callsites check this, but technically its race-y and could");
2466+
return Err(APIError::RouteError { err: "Payment was completed or failed while we were processing it" });
2467+
};
24652468

24662469
let id = match channel_lock.short_to_chan_info.get(&path.first().unwrap().short_channel_id) {
24672470
None => return Err(APIError::ChannelUnavailable{err: "No channel available with first hop!".to_owned()}),
24682471
Some((_cp_id, chan_id)) => chan_id.clone(),
24692472
};
24702473

2471-
macro_rules! insert_outbound_payment {
2472-
() => {
2473-
let payment = payment_entry.or_insert_with(|| PendingOutboundPayment::Retryable {
2474-
session_privs: HashSet::new(),
2475-
pending_amt_msat: 0,
2476-
pending_fee_msat: Some(0),
2477-
payment_hash: *payment_hash,
2478-
payment_secret: *payment_secret,
2479-
starting_block_height: self.best_block.read().unwrap().height(),
2480-
total_msat: total_value,
2481-
});
2482-
assert!(payment.insert(session_priv_bytes, path));
2483-
}
2484-
}
2485-
24862474
let channel_state = &mut *channel_lock;
24872475
if let hash_map::Entry::Occupied(mut chan) = channel_state.by_id.entry(id) {
24882476
match {
@@ -2512,7 +2500,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
25122500
{
25132501
(ChannelMonitorUpdateStatus::PermanentFailure, Err(e)) => break Err(e),
25142502
(ChannelMonitorUpdateStatus::Completed, Ok(())) => {
2515-
insert_outbound_payment!();
2503+
assert!(payment.insert(session_priv_bytes, path));
25162504
},
25172505
(ChannelMonitorUpdateStatus::InProgress, Err(_)) => {
25182506
// Note that MonitorUpdateInProgress here indicates (per function
@@ -2521,7 +2509,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
25212509
// indicating that it is unsafe to retry the payment wholesale,
25222510
// which we do in the send_payment check for
25232511
// MonitorUpdateInProgress, below.
2524-
insert_outbound_payment!(); // Only do this after possibly break'ing on Perm failure above.
2512+
assert!(payment.insert(session_priv_bytes, path)); // Only do this after possibly break'ing on Perm failure above.
25252513
return Err(APIError::MonitorUpdateInProgress);
25262514
},
25272515
_ => unreachable!(),
@@ -2540,7 +2528,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
25402528
},
25412529
});
25422530
},
2543-
None => { insert_outbound_payment!(); },
2531+
None => { assert!(payment.insert(session_priv_bytes, path)); },
25442532
}
25452533
} else { unreachable!(); }
25462534
return Ok(());
@@ -2559,12 +2547,18 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
25592547
/// Value parameters are provided via the last hop in route, see documentation for RouteHop
25602548
/// fields for more info.
25612549
///
2562-
/// Note that if the payment_hash already exists elsewhere (eg you're sending a duplicative
2563-
/// payment), we don't do anything to stop you! We always try to ensure that if the provided
2564-
/// next hop knows the preimage to payment_hash they can claim an additional amount as
2565-
/// specified in the last hop in the route! Thus, you should probably do your own
2566-
/// payment_preimage tracking (which you should already be doing as they represent "proof of
2567-
/// payment") and prevent double-sends yourself.
2550+
/// If a pending payment is currently in-flight with the same [`PaymentHash`] provided, this
2551+
/// method will error with an [`APIError::RouteError`]. Note, however, that once a payment
2552+
/// is no longer pending (either via [`ChannelManager::abandon_payment`], or handling of an
2553+
/// [`Event::PaymentSent`]) a second payment with the same [`PaymentHash`] can be sent.
2554+
///
2555+
/// Thus, in order to ensure that duplicate payments are not sent, you should likely implement
2556+
/// your own [`PaymentHash`]-based tracking of payments, including storing received
2557+
/// [`PaymentPreimage`]s as proof of payment.
2558+
///
2559+
/// For backwards-compatibility, this method returns a [`PaymentId`] which uniquely represents
2560+
/// this payment and is used in [`ChannelManager::abandon_payment`] and
2561+
/// [`ChannelManager::retry_payment`], however the vaule is always the [`PaymentHash`].
25682562
///
25692563
/// May generate SendHTLCs message(s) event on success, which should be relayed.
25702564
///
@@ -2590,14 +2584,50 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
25902584
/// newer nodes, it will be provided to you in the invoice. If you do not have one, the Route
25912585
/// must not contain multiple paths as multi-path payments require a recipient-provided
25922586
/// payment_secret.
2587+
///
25932588
/// If a payment_secret *is* provided, we assume that the invoice had the payment_secret feature
25942589
/// bit set (either as required or as available). If multiple paths are present in the Route,
25952590
/// we assume the invoice had the basic_mpp feature set.
2591+
///
2592+
/// [`Event::PaymentSent`]: events::Event::PaymentSent
25962593
pub fn send_payment(&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>) -> Result<PaymentId, PaymentSendFailure> {
2597-
self.send_payment_internal(route, payment_hash, payment_secret, None, None, None)
2594+
let total_value_msat = route.paths.iter().map(|path| path.last().map(|h| h.fee_msat).unwrap_or(0)).sum();
2595+
let payment_id = PaymentId(payment_hash.0);
2596+
self.add_new_pending_payment(payment_hash, *payment_secret, payment_id, total_value_msat)?;
2597+
self.send_payment_internal(route, payment_hash, payment_secret, None, payment_id, None)?;
2598+
Ok(payment_id)
2599+
}
2600+
2601+
#[cfg(any(test, feature = "_test_utils"))]
2602+
pub fn send_payment_with_id(&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>, payment_id: PaymentId) -> Result<(), PaymentSendFailure> {
2603+
let total_value_msat = route.paths.iter().map(|path| path.last().map(|h| h.fee_msat).unwrap_or(0)).sum();
2604+
self.add_new_pending_payment(payment_hash, *payment_secret, payment_id, total_value_msat)?;
2605+
self.send_payment_internal(route, payment_hash, payment_secret, None, payment_id, None)
25982606
}
25992607

2600-
fn send_payment_internal(&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>, keysend_preimage: Option<PaymentPreimage>, payment_id: Option<PaymentId>, recv_value_msat: Option<u64>) -> Result<PaymentId, PaymentSendFailure> {
2608+
// Only public for testing, this should otherwise never be called direcly
2609+
pub(crate) fn add_new_pending_payment(&self, payment_hash: PaymentHash, payment_secret: Option<PaymentSecret>, payment_id: PaymentId, total_msat: u64) -> Result<(), PaymentSendFailure> {
2610+
let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
2611+
match pending_outbounds.entry(payment_id) {
2612+
hash_map::Entry::Occupied(_) => Err(PaymentSendFailure::ParameterError(APIError::RouteError {
2613+
err: "Payment already in progress"
2614+
})),
2615+
hash_map::Entry::Vacant(entry) => {
2616+
entry.insert(PendingOutboundPayment::Retryable {
2617+
session_privs: HashSet::new(),
2618+
pending_amt_msat: 0,
2619+
pending_fee_msat: Some(0),
2620+
payment_hash,
2621+
payment_secret,
2622+
starting_block_height: self.best_block.read().unwrap().height(),
2623+
total_msat,
2624+
});
2625+
Ok(())
2626+
},
2627+
}
2628+
}
2629+
2630+
fn send_payment_internal(&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>, keysend_preimage: Option<PaymentPreimage>, payment_id: PaymentId, recv_value_msat: Option<u64>) -> Result<(), PaymentSendFailure> {
26012631
if route.paths.len() < 1 {
26022632
return Err(PaymentSendFailure::ParameterError(APIError::RouteError{err: "There must be at least one path to send over"}));
26032633
}
@@ -2607,7 +2637,6 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
26072637
let mut total_value = 0;
26082638
let our_node_id = self.get_our_node_id();
26092639
let mut path_errs = Vec::with_capacity(route.paths.len());
2610-
let payment_id = if let Some(id) = payment_id { id } else { PaymentId(self.keys_manager.get_secure_random_bytes()) };
26112640
'path_check: for path in route.paths.iter() {
26122641
if path.len() < 1 || path.len() > 20 {
26132642
path_errs.push(Err(APIError::RouteError{err: "Path didn't go anywhere/had bogus size"}));
@@ -2667,12 +2696,13 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
26672696
} else { None },
26682697
})
26692698
} else if has_err {
2670-
// If we failed to send any paths, we shouldn't have inserted the new PaymentId into
2671-
// our `pending_outbound_payments` map at all.
2672-
debug_assert!(self.pending_outbound_payments.lock().unwrap().get(&payment_id).is_none());
2699+
// If we failed to send any paths, we should remove the new PaymentId from the
2700+
// `pending_outbound_payments` map, as the user isn't expected to `abandon_payment`.
2701+
let removed = self.pending_outbound_payments.lock().unwrap().remove(&payment_id).is_some();
2702+
debug_assert!(removed, "We should always have a pending payment to remove here");
26732703
Err(PaymentSendFailure::AllFailedRetrySafe(results.drain(..).map(|r| r.unwrap_err()).collect()))
26742704
} else {
2675-
Ok(payment_id)
2705+
Ok(())
26762706
}
26772707
}
26782708

@@ -2733,7 +2763,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
27332763
}))
27342764
}
27352765
};
2736-
return self.send_payment_internal(route, payment_hash, &payment_secret, None, Some(payment_id), Some(total_msat)).map(|_| ())
2766+
self.send_payment_internal(route, payment_hash, &payment_secret, None, payment_id, Some(total_msat))
27372767
}
27382768

27392769
/// Signals that no further retries for the given payment will occur.
@@ -2773,22 +2803,30 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
27732803
/// would be able to guess -- otherwise, an intermediate node may claim the payment and it will
27742804
/// never reach the recipient.
27752805
///
2776-
/// See [`send_payment`] documentation for more details on the return value of this function.
2806+
/// See [`send_payment`] documentation for more details on the return value of this function
2807+
/// and idempotency guarantees provided.
2808+
///
2809+
/// Note that the [`PaymentId`] for the generated payment will be
2810+
/// `PaymentId(PaymentHashReturned.0)`.
27772811
///
27782812
/// Similar to regular payments, you MUST NOT reuse a `payment_preimage` value. See
27792813
/// [`send_payment`] for more information about the risks of duplicate preimage usage.
27802814
///
27812815
/// Note that `route` must have exactly one path.
27822816
///
27832817
/// [`send_payment`]: Self::send_payment
2784-
pub fn send_spontaneous_payment(&self, route: &Route, payment_preimage: Option<PaymentPreimage>) -> Result<(PaymentHash, PaymentId), PaymentSendFailure> {
2818+
pub fn send_spontaneous_payment(&self, route: &Route, payment_preimage: Option<PaymentPreimage>) -> Result<PaymentHash, PaymentSendFailure> {
27852819
let preimage = match payment_preimage {
27862820
Some(p) => p,
27872821
None => PaymentPreimage(self.keys_manager.get_secure_random_bytes()),
27882822
};
27892823
let payment_hash = PaymentHash(Sha256::hash(&preimage.0).into_inner());
2790-
match self.send_payment_internal(route, payment_hash, &None, Some(preimage), None, None) {
2791-
Ok(payment_id) => Ok((payment_hash, payment_id)),
2824+
let total_value_msat = route.paths.iter().map(|path| path.last().map(|h| h.fee_msat).unwrap_or(0)).sum();
2825+
let payment_id = PaymentId(payment_hash.0);
2826+
self.add_new_pending_payment(payment_hash, None, payment_id, total_value_msat)?;
2827+
2828+
match self.send_payment_internal(route, payment_hash, &None, Some(preimage), payment_id, None) {
2829+
Ok(()) => Ok(payment_hash),
27922830
Err(e) => Err(e)
27932831
}
27942832
}
@@ -2798,7 +2836,6 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
27982836
/// us to easily discern them from real payments.
27992837
pub fn send_probe(&self, hops: Vec<RouteHop>) -> Result<(PaymentHash, PaymentId), PaymentSendFailure> {
28002838
let payment_id = PaymentId(self.keys_manager.get_secure_random_bytes());
2801-
28022839
let payment_hash = self.probing_cookie_from_id(&payment_id);
28032840

28042841
if hops.len() < 2 {
@@ -2809,8 +2846,11 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
28092846

28102847
let route = Route { paths: vec![hops], payment_params: None };
28112848

2812-
match self.send_payment_internal(&route, payment_hash, &None, None, Some(payment_id), None) {
2813-
Ok(payment_id) => Ok((payment_hash, payment_id)),
2849+
let total_value_msat = route.paths.iter().map(|path| path.last().map(|h| h.fee_msat).unwrap_or(0)).sum();
2850+
self.add_new_pending_payment(payment_hash, None, payment_id, total_value_msat)?;
2851+
2852+
match self.send_payment_internal(&route, payment_hash, &None, None, payment_id, None) {
2853+
Ok(()) => Ok((payment_hash, payment_id)),
28142854
Err(e) => Err(e)
28152855
}
28162856
}
@@ -7400,6 +7440,7 @@ mod tests {
74007440
// Use the utility function send_payment_along_path to send the payment with MPP data which
74017441
// indicates there are more HTLCs coming.
74027442
let cur_height = CHAN_CONFIRM_DEPTH + 1; // route_payment calls send_payment, which adds 1 to the current height. So we do the same here to match.
7443+
nodes[0].node.add_new_pending_payment(our_payment_hash, Some(payment_secret), payment_id, 200_000).unwrap();
74037444
nodes[0].node.send_payment_along_path(&route.paths[0], &route.payment_params, &our_payment_hash, &Some(payment_secret), 200_000, cur_height, payment_id, &None).unwrap();
74047445
check_added_monitors!(nodes[0], 1);
74057446
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
@@ -7561,7 +7602,7 @@ mod tests {
75617602
&nodes[0].node.get_our_node_id(), &route_params, &nodes[0].network_graph,
75627603
None, nodes[0].logger, &scorer, &random_seed_bytes
75637604
).unwrap();
7564-
let (payment_hash, _) = nodes[0].node.send_spontaneous_payment(&route, Some(payment_preimage)).unwrap();
7605+
let payment_hash = nodes[0].node.send_spontaneous_payment(&route, Some(payment_preimage)).unwrap();
75657606
check_added_monitors!(nodes[0], 1);
75667607
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
75677608
assert_eq!(events.len(), 1);
@@ -7571,7 +7612,7 @@ mod tests {
75717612

75727613
// Next, attempt a regular payment and make sure it fails.
75737614
let payment_secret = PaymentSecret([43; 32]);
7574-
nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
7615+
nodes[0].node.send_payment_with_id(&route, payment_hash, &Some(payment_secret), PaymentId(payment_secret.0)).unwrap();
75757616
check_added_monitors!(nodes[0], 1);
75767617
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
75777618
assert_eq!(events.len(), 1);
@@ -7614,7 +7655,7 @@ mod tests {
76147655
let _chan = create_chan_between_nodes(&nodes[0], &nodes[1], channelmanager::provided_init_features(), channelmanager::provided_init_features());
76157656
let route_params = RouteParameters {
76167657
payment_params: PaymentParameters::for_keysend(payee_pubkey),
7617-
final_value_msat: 10000,
7658+
final_value_msat: 10_000,
76187659
final_cltv_expiry_delta: 40,
76197660
};
76207661
let network_graph = nodes[0].network_graph;
@@ -7628,7 +7669,8 @@ mod tests {
76287669

76297670
let test_preimage = PaymentPreimage([42; 32]);
76307671
let mismatch_payment_hash = PaymentHash([43; 32]);
7631-
let _ = nodes[0].node.send_payment_internal(&route, mismatch_payment_hash, &None, Some(test_preimage), None, None).unwrap();
7672+
nodes[0].node.add_new_pending_payment(mismatch_payment_hash, None, PaymentId(mismatch_payment_hash.0), 10_000).unwrap();
7673+
nodes[0].node.send_payment_internal(&route, mismatch_payment_hash, &None, Some(test_preimage), PaymentId(mismatch_payment_hash.0), None).unwrap();
76327674
check_added_monitors!(nodes[0], 1);
76337675

76347676
let updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
@@ -7658,7 +7700,7 @@ mod tests {
76587700
let _chan = create_chan_between_nodes(&nodes[0], &nodes[1], channelmanager::provided_init_features(), channelmanager::provided_init_features());
76597701
let route_params = RouteParameters {
76607702
payment_params: PaymentParameters::for_keysend(payee_pubkey),
7661-
final_value_msat: 10000,
7703+
final_value_msat: 10_000,
76627704
final_cltv_expiry_delta: 40,
76637705
};
76647706
let network_graph = nodes[0].network_graph;
@@ -7673,7 +7715,8 @@ mod tests {
76737715
let test_preimage = PaymentPreimage([42; 32]);
76747716
let test_secret = PaymentSecret([43; 32]);
76757717
let payment_hash = PaymentHash(Sha256::hash(&test_preimage.0).into_inner());
7676-
let _ = nodes[0].node.send_payment_internal(&route, payment_hash, &Some(test_secret), Some(test_preimage), None, None).unwrap();
7718+
nodes[0].node.add_new_pending_payment(payment_hash, Some(test_secret), PaymentId(payment_hash.0), 10_000).unwrap();
7719+
nodes[0].node.send_payment_internal(&route, payment_hash, &Some(test_secret), Some(test_preimage), PaymentId(payment_hash.0), None).unwrap();
76777720
check_added_monitors!(nodes[0], 1);
76787721

76797722
let updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
@@ -7865,7 +7908,7 @@ pub mod bench {
78657908
use chain::Listen;
78667909
use chain::chainmonitor::{ChainMonitor, Persist};
78677910
use chain::keysinterface::{KeysManager, KeysInterface, InMemorySigner};
7868-
use ln::channelmanager::{self, BestBlock, ChainParameters, ChannelManager, PaymentHash, PaymentPreimage};
7911+
use ln::channelmanager::{self, BestBlock, ChainParameters, ChannelManager, PaymentHash, PaymentPreimage, PaymentId};
78697912
use ln::features::{InitFeatures, InvoiceFeatures};
78707913
use ln::functional_test_utils::*;
78717914
use ln::msgs::{ChannelMessageHandler, Init};

lightning/src/ln/functional_test_utils.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1636,7 +1636,8 @@ pub fn expect_payment_failed_conditions<'a, 'b, 'c, 'd, 'e>(
16361636
}
16371637

16381638
pub fn send_along_route_with_secret<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, route: Route, expected_paths: &[&[&Node<'a, 'b, 'c>]], recv_value: u64, our_payment_hash: PaymentHash, our_payment_secret: PaymentSecret) -> PaymentId {
1639-
let payment_id = origin_node.node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
1639+
let payment_id = PaymentId(origin_node.keys_manager.backing.get_secure_random_bytes());
1640+
origin_node.node.send_payment_with_id(&route, our_payment_hash, &Some(our_payment_secret), payment_id).unwrap();
16401641
check_added_monitors!(origin_node, expected_paths.len());
16411642
pass_along_route(origin_node, expected_paths, recv_value, our_payment_hash, our_payment_secret);
16421643
payment_id

0 commit comments

Comments
 (0)