Description:
Summary
After a clean restart of an ldk-node LSPS2 service node, the node panics when the next inbound HTLC arrives for a JIT channel. The panic is caused by a stale payment queue entry from the previous session whose intercept SCID is still being used by the client node.
Panic
thread 'tokio-rt-worker' panicked at
lightning-liquidity/src/lsps2/payment_queue.rs:36:13:
assertion failed: entry.htlcs.iter().all(|htlc| htlc.intercept_id != new_htlc.intercept_id)
This poisons the internal mutex, cascading into:
thread 'tokio-rt-worker' panicked at lightning-liquidity/src/lsps2/service.rs:1510:53:
called Result::unwrap() on an Err value: PoisonError { .. }
thread 'tokio-rt-worker' panicked at lightning-net-tokio/src/lib.rs:359:5:
assertion failed: e.is_cancelled()
After this the LSP is completely non-functional until restarted again (and the cycle repeats).
Root Cause
Two problems combine to produce this:
-
The LSPS2 client (node-service) persists and reuses the intercept SCID after restart. receive_via_jit_channel stores its LSPS2 client state (including the assigned intercept SCID) in lightning_liquidity_state in SQLite. On restart, when a new invoice is requested, the client presents the same intercept SCID again via a new LSPS2 buy request.
-
The LSPS2 service (lsp-service) persists its payment queue but never cleans up settled entries. The lightning_liquidity_state|lsps2_service|<peer_id> row survives restart with the old queue entry for the completed payment still present. When the same intercept SCID arrives again, payment_queue.rs:36 asserts uniqueness and panics.
Description:
Summary
After a clean restart of an ldk-node LSPS2 service node, the node panics when the next inbound HTLC arrives for a JIT channel. The panic is caused by a stale payment queue entry from the previous session whose intercept SCID is still being used by the client node.
Panic
thread 'tokio-rt-worker' panicked at
lightning-liquidity/src/lsps2/payment_queue.rs:36:13:
assertion failed: entry.htlcs.iter().all(|htlc| htlc.intercept_id != new_htlc.intercept_id)
This poisons the internal mutex, cascading into:
thread 'tokio-rt-worker' panicked at lightning-liquidity/src/lsps2/service.rs:1510:53:
called
Result::unwrap()on anErrvalue: PoisonError { .. }thread 'tokio-rt-worker' panicked at lightning-net-tokio/src/lib.rs:359:5:
assertion failed: e.is_cancelled()
After this the LSP is completely non-functional until restarted again (and the cycle repeats).
Root Cause
Two problems combine to produce this:
The LSPS2 client (node-service) persists and reuses the intercept SCID after restart.
receive_via_jit_channel storesits LSPS2 client state (including the assigned intercept SCID) in lightning_liquidity_state in SQLite. On restart, when a new invoice is requested, the client presents the same intercept SCID again via a new LSPS2 buy request.The LSPS2 service (lsp-service) persists its payment queue but never cleans up settled entries. The lightning_liquidity_state|lsps2_service|<peer_id> row survives restart with the old queue entry for the completed payment still present. When the same intercept SCID arrives again, payment_queue.rs:36 asserts uniqueness and panics.