Problem
src/event.rs, EventHandler::handle_event, handles outbound terminal LDK events by updating PaymentStore and then queueing a user-facing app event. However, the current match accepts DataStoreUpdateResult::NotFound as success because it matches Ok(_).
For LdkEvent::PaymentSent:
- The handler builds a
PaymentDetailsUpdate with the payment hash, preimage, fee, and PaymentStatus::Succeeded.
- It calls
self.payment_store.update(update).await.
- The match arm is
Ok(_) => {}, so NotFound is treated the same as Updated or Unchanged.
- It optionally calls
self.payment_store.get(&payment_id) only for logging.
- It queues
Event::PaymentSuccessful with self.event_queue.add_event(event).await.
For LdkEvent::PaymentFailed, the same pattern applies: it builds a failed update, calls self.payment_store.update(update).await, treats Ok(_) as success, and then queues Event::PaymentFailed.
The underlying DataStore::update in src/data_store.rs explicitly returns Ok(DataStoreUpdateResult::NotFound) when the object is missing. That result does not create or persist a new record.
This also matters for future duplicate protection. In src/payment/bolt11.rs, Bolt11Payment::send_internal checks self.payment_store.get(&payment_id) before calling LDK, and known_payment_blocks_send only blocks a send when the local record is Pending or Succeeded. If a successful outbound terminal event is emitted without reconstructing a missing Succeeded record, ldk-node's duplicate gate may later have no durable record proving that the invoice or payment hash was already paid.
Consequence
An application can receive PaymentSuccessful or PaymentFailed while Node::payment and Node::list_payments still have no corresponding payment record. For success, this can look like sats were spent with no durable ldk-node payment history. It can also weaken duplicate-payment protection: if LDK no longer tracks the payment in recent payment state and ldk-node has no Succeeded record, ldk-node may not block a later attempt to pay the same invoice or payment hash before handing it to LDK. For failure, retry behavior becomes harder to reason about because the terminal state was emitted as an event but not recorded in the payment store.
Problem
src/event.rs,EventHandler::handle_event, handles outbound terminal LDK events by updatingPaymentStoreand then queueing a user-facing app event. However, the current match acceptsDataStoreUpdateResult::NotFoundas success because it matchesOk(_).For
LdkEvent::PaymentSent:PaymentDetailsUpdatewith the payment hash, preimage, fee, andPaymentStatus::Succeeded.self.payment_store.update(update).await.Ok(_) => {}, soNotFoundis treated the same asUpdatedorUnchanged.self.payment_store.get(&payment_id)only for logging.Event::PaymentSuccessfulwithself.event_queue.add_event(event).await.For
LdkEvent::PaymentFailed, the same pattern applies: it builds a failed update, callsself.payment_store.update(update).await, treatsOk(_)as success, and then queuesEvent::PaymentFailed.The underlying
DataStore::updateinsrc/data_store.rsexplicitly returnsOk(DataStoreUpdateResult::NotFound)when the object is missing. That result does not create or persist a new record.This also matters for future duplicate protection. In
src/payment/bolt11.rs,Bolt11Payment::send_internalchecksself.payment_store.get(&payment_id)before calling LDK, andknown_payment_blocks_sendonly blocks a send when the local record isPendingorSucceeded. If a successful outbound terminal event is emitted without reconstructing a missingSucceededrecord, ldk-node's duplicate gate may later have no durable record proving that the invoice or payment hash was already paid.Consequence
An application can receive
PaymentSuccessfulorPaymentFailedwhileNode::paymentandNode::list_paymentsstill have no corresponding payment record. For success, this can look like sats were spent with no durable ldk-node payment history. It can also weaken duplicate-payment protection: if LDK no longer tracks the payment in recent payment state and ldk-node has noSucceededrecord, ldk-node may not block a later attempt to pay the same invoice or payment hash before handing it to LDK. For failure, retry behavior becomes harder to reason about because the terminal state was emitted as an event but not recorded in the payment store.