Skip to content

Commit b6474b6

Browse files
committed
Make payments not duplicatively fail/succeed on reload/reconnect
We currently generate duplicative PaymentFailed/PaymentSent events in two cases: a) If we receive a update_fulfill_htlc message, followed by a disconnect, then a resend of the same update_fulfill_htlc message, we will generate a PaymentSent event for each message. b) When a Channel is closed, any outbound HTLCs which were relayed through it are simply dropped when the Channel is. From there, the ChannelManager relies on the ChannelMonitor having a copy of the relevant fail-/claim-back data and processes the HTLC fail/claim when the ChannelMonitor tells it to. If, due to an on-chain event, an HTLC is failed/claimed, and then we serialize the ChannelManager, but do not re-serialize the relevant ChannelMonitor, we may end up getting a duplicative event. In order to provide the expected consistency, we add explicit tracking of pending outbound payments using their unique session_priv field which is generated when the payment is sent. Then, before generating PaymentFailed/PaymentSent events, we check that the session_priv for the payment is still pending. Thix fixes #209.
1 parent 7297e13 commit b6474b6

File tree

2 files changed

+64
-20
lines changed

2 files changed

+64
-20
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,17 @@ pub struct ChannelManager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref,
442442
/// Locked *after* channel_state.
443443
pending_inbound_payments: Mutex<HashMap<PaymentHash, PendingInboundPayment>>,
444444

445+
/// The session_priv bytes of outbound payments which are pending resolution.
446+
/// The authoritative state of these HTLCs resides either within Channels or ChannelMonitors
447+
/// (if the channel has been force-closed), however we track them here to prevent duplicative
448+
/// PaymentSent/PaymentFailed events. Specifically, in the case of a duplicative
449+
/// update_fulfill_htlc message after a reconnect, we may "claim" a payment twice.
450+
/// Additionally, for channels which have been force-closed, a ChannelMonitor may not be
451+
/// re-serialized after generating a claim event (even if this ChannelManager is), resulting in
452+
/// us "claiming" a payment twice.
453+
/// Locked *after* channel_state.
454+
outbound_pending_payments: Mutex<HashSet<[u8; 32]>>,
455+
445456
our_network_key: SecretKey,
446457
our_network_pubkey: PublicKey,
447458

@@ -895,6 +906,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
895906
pending_msg_events: Vec::new(),
896907
}),
897908
pending_inbound_payments: Mutex::new(HashMap::new()),
909+
outbound_pending_payments: Mutex::new(HashSet::new()),
898910

899911
our_network_key: keys_manager.get_node_secret(),
900912
our_network_pubkey: PublicKey::from_secret_key(&secp_ctx, &keys_manager.get_node_secret()),
@@ -1449,7 +1461,8 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
14491461
pub(crate) fn send_payment_along_path(&self, path: &Vec<RouteHop>, payment_hash: &PaymentHash, payment_secret: &Option<PaymentSecret>, total_value: u64, cur_height: u32) -> Result<(), APIError> {
14501462
log_trace!(self.logger, "Attempting to send payment for path with next hop {}", path.first().unwrap().short_channel_id);
14511463
let prng_seed = self.keys_manager.get_secure_random_bytes();
1452-
let session_priv = SecretKey::from_slice(&self.keys_manager.get_secure_random_bytes()[..]).expect("RNG is busted");
1464+
let session_priv_bytes = self.keys_manager.get_secure_random_bytes();
1465+
let session_priv = SecretKey::from_slice(&session_priv_bytes[..]).expect("RNG is busted");
14531466

14541467
let onion_keys = onion_utils::construct_onion_keys(&self.secp_ctx, &path, &session_priv)
14551468
.map_err(|_| APIError::RouteError{err: "Pubkey along hop was maliciously selected"})?;
@@ -1460,6 +1473,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
14601473
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, prng_seed, payment_hash);
14611474

14621475
let _persistence_guard = PersistenceNotifierGuard::new(&self.total_consistency_lock, &self.persistence_notifier);
1476+
assert!(self.outbound_pending_payments.lock().unwrap().insert(session_priv_bytes));
14631477

14641478
let err: Result<(), _> = loop {
14651479
let mut channel_lock = self.channel_state.lock().unwrap();
@@ -2188,17 +2202,23 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
21882202
self.fail_htlc_backwards_internal(channel_state,
21892203
htlc_src, &payment_hash, HTLCFailReason::Reason { failure_code, data: onion_failure_data});
21902204
},
2191-
HTLCSource::OutboundRoute { .. } => {
2192-
self.pending_events.lock().unwrap().push(
2193-
events::Event::PaymentFailed {
2194-
payment_hash,
2195-
rejected_by_dest: false,
2205+
HTLCSource::OutboundRoute { session_priv, .. } => {
2206+
if {
2207+
let mut session_priv_bytes = [0; 32];
2208+
session_priv_bytes.copy_from_slice(&session_priv[..]);
2209+
self.outbound_pending_payments.lock().unwrap().remove(&session_priv_bytes)
2210+
} {
2211+
self.pending_events.lock().unwrap().push(
2212+
events::Event::PaymentFailed {
2213+
payment_hash,
2214+
rejected_by_dest: false,
21962215
#[cfg(test)]
2197-
error_code: None,
2216+
error_code: None,
21982217
#[cfg(test)]
2199-
error_data: None,
2200-
}
2201-
)
2218+
error_data: None,
2219+
}
2220+
)
2221+
}
22022222
},
22032223
};
22042224
}
@@ -2220,7 +2240,14 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
22202240
// from block_connected which may run during initialization prior to the chain_monitor
22212241
// being fully configured. See the docs for `ChannelManagerReadArgs` for more.
22222242
match source {
2223-
HTLCSource::OutboundRoute { ref path, .. } => {
2243+
HTLCSource::OutboundRoute { ref path, session_priv, .. } => {
2244+
if {
2245+
let mut session_priv_bytes = [0; 32];
2246+
session_priv_bytes.copy_from_slice(&session_priv[..]);
2247+
!self.outbound_pending_payments.lock().unwrap().remove(&session_priv_bytes)
2248+
} {
2249+
return;
2250+
}
22242251
log_trace!(self.logger, "Failing outbound payment HTLC with payment_hash {}", log_bytes!(payment_hash.0));
22252252
mem::drop(channel_state_lock);
22262253
match &onion_error {
@@ -2449,12 +2476,18 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
24492476

24502477
fn claim_funds_internal(&self, mut channel_state_lock: MutexGuard<ChannelHolder<Signer>>, source: HTLCSource, payment_preimage: PaymentPreimage) {
24512478
match source {
2452-
HTLCSource::OutboundRoute { .. } => {
2479+
HTLCSource::OutboundRoute { session_priv, .. } => {
24532480
mem::drop(channel_state_lock);
2454-
let mut pending_events = self.pending_events.lock().unwrap();
2455-
pending_events.push(events::Event::PaymentSent {
2456-
payment_preimage
2457-
});
2481+
if {
2482+
let mut session_priv_bytes = [0; 32];
2483+
session_priv_bytes.copy_from_slice(&session_priv[..]);
2484+
self.outbound_pending_payments.lock().unwrap().remove(&session_priv_bytes)
2485+
} {
2486+
let mut pending_events = self.pending_events.lock().unwrap();
2487+
pending_events.push(events::Event::PaymentSent {
2488+
payment_preimage
2489+
});
2490+
}
24582491
},
24592492
HTLCSource::PreviousHopData(hop_data) => {
24602493
let prev_outpoint = hop_data.outpoint;
@@ -4423,6 +4456,12 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
44234456
pending_payment.write(writer)?;
44244457
}
44254458

4459+
let outbound_pending_payments = self.outbound_pending_payments.lock().unwrap();
4460+
(outbound_pending_payments.len() as u64).write(writer)?;
4461+
for session_priv in outbound_pending_payments.iter() {
4462+
session_priv.write(writer)?;
4463+
}
4464+
44264465
Ok(())
44274466
}
44284467
}
@@ -4662,6 +4701,14 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
46624701
}
46634702
}
46644703

4704+
let outbound_pending_payments_count: u64 = Readable::read(reader)?;
4705+
let mut outbound_pending_payments: HashSet<[u8; 32]> = HashSet::with_capacity(cmp::min(outbound_pending_payments_count as usize, MAX_ALLOC_SIZE/32));
4706+
for _ in 0..outbound_pending_payments_count {
4707+
if !outbound_pending_payments.insert(Readable::read(reader)?) {
4708+
return Err(DecodeError::InvalidValue);
4709+
}
4710+
}
4711+
46654712
let mut secp_ctx = Secp256k1::new();
46664713
secp_ctx.seeded_randomize(&args.keys_manager.get_secure_random_bytes());
46674714

@@ -4681,6 +4728,7 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
46814728
pending_msg_events: Vec::new(),
46824729
}),
46834730
pending_inbound_payments: Mutex::new(pending_inbound_payments),
4731+
outbound_pending_payments: Mutex::new(outbound_pending_payments),
46844732

46854733
our_network_key: args.keys_manager.get_node_secret(),
46864734
our_network_pubkey: PublicKey::from_secret_key(&secp_ctx, &args.keys_manager.get_node_secret()),

lightning/src/util/events.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,6 @@ pub enum Event {
9595
},
9696
/// Indicates an outbound payment we made succeeded (ie it made it all the way to its target
9797
/// and we got back the payment preimage for it).
98-
/// Note that duplicative PaymentSent Events may be generated - it is your responsibility to
99-
/// deduplicate them by payment_preimage (which MUST be unique)!
10098
PaymentSent {
10199
/// The preimage to the hash given to ChannelManager::send_payment.
102100
/// Note that this serves as a payment receipt, if you wish to have such a thing, you must
@@ -105,8 +103,6 @@ pub enum Event {
105103
},
106104
/// Indicates an outbound payment we made failed. Probably some intermediary node dropped
107105
/// something. You may wish to retry with a different route.
108-
/// Note that duplicative PaymentFailed Events may be generated - it is your responsibility to
109-
/// deduplicate them by payment_hash (which MUST be unique)!
110106
PaymentFailed {
111107
/// The hash which was given to ChannelManager::send_payment.
112108
payment_hash: PaymentHash,

0 commit comments

Comments
 (0)