Repository navigation
Consistently return NotRunning from live-node APIs - #1160
Conversation
|
👋 Thanks for assigning @tnull as a reviewer! |
|
Hi @tnull, this is ready for a look. I did a self-review pass after opening it and pushed a follow-up commit ( What the PR does
What the follow-up adds
Where I'd appreciate your steer
Happy to split the broadcast-queue flush into its own PR if you'd rather review it separately. |
tnull
left a comment
There was a problem hiding this comment.
Thanks for taking a look!
No, I think for now we should really just leave it at what's described in #1141, i.e., return NotRunning for the API methods that are missing it.
We want eventually to do proper start/stop handling that also covers node restarts on persistence failures, etc. But this is out-of-scope for now.
Btw, note that the chosen approach in this PR currently introduces a lot of complexity and risks, e.g., it makes the Node prone to deadlocks on shutdown.
e72650b to
696d2f5
Compare
Thanks for the feedback, and fair point on the complexity and the shutdown deadlock risk. I've cut the PR back to what #1141 describes: it now only adds the missing What's left:
I've left the TOCTOU race and the start/stop lifecycle handling for the broader start/stop rework you mentioned. |
696d2f5 to
3dcfe25
Compare
Pushed a small update (still a single commit): the previous revision failed Note that Ready for another look whenever you have time. |
3dcfe25 to
efe10ea
Compare
efe10ea to
18c7be2
Compare
18c7be2 to
56db51c
Compare
tnull
left a comment
There was a problem hiding this comment.
It seems there are still gaps. Codex:
• Bolt12Payment::receive_async() has no guard and changes persistent state. bolt12.rs:646 (/home/tnull/workspace/ldk-node-review-pr-1160-F3A8wr/src/payment/bolt12.rs:646) calls LDK’s get_async_receive_offer(), which marks a fresh offer as used and signals persistence. After stop(), the
background processor has stopped, so that change will not be persisted. Dropping and rebuilding the node can lose the marker, allowing an offer already handed to the application to be rotated out later. This deserves a running check or explicit synchronous persistence for offline use.• Bolt12Payment::set_paths_to_static_invoice_server() has no guard in either Rust or UniFFI. bolt12.rs:663 (/home/tnull/workspace/ldk-node-review-pr-1160-F3A8wr/src/payment/bolt12.rs:663), bolt12.rs:695 (/home/tnull/workspace/ldk-node-review-pr-1160-F3A8wr/src/payment/bolt12.rs:695). LDK
updates its cached server paths and signals persistence; it also attempts to initiate offer setup when peers are connected. After shutdown, valid paths can return Ok(()) while leaving the change unpersisted. Offline configuration needs an explicit persistence path if it is intended.• Both announced-channel opening methods check too late. lib.rs:1587 (/home/tnull/workspace/ldk-node-review-pr-1160-F3A8wr/src/lib.rs:1587), lib.rs:1664 (/home/tnull/workspace/ldk-node-review-pr-1160-F3A8wr/src/lib.rs:1664). They validate announcement configuration before reaching the guard
in open_channel_inner(). An unstarted node without the required configuration therefore returns ChannelCreationFailed, bypassing NotRunning. This performs validation and logging, but no node-state mutation.
56db51c to
350c801
Compare
@tnull Thanks, good catches. Added the running check as the first step in |
Several public APIs that need a live node didn't check the running state, e.g., `OnchainPayment::bump_fee_rbf` could reach wallet mutation and signing after `Node::stop`, while the on-chain send methods returned `Error::NotRunning`. Add the missing checks before any side effects, using the same pattern as the existing ones: RBF fee bumps, splicing, channel closure and config updates, BOLT11 claim/fail and JIT receive, BOLT12 refunds, LSPS1 requests and `add_liquidity_source`. The unified payment flow no longer falls back to the next payment method if the node isn't running. Methods that intentionally work offline (e.g., address generation, invoice and offer creation, listings) are left as is. Adds a unit test for the not-running errors and an integration test covering calls after shutdown and normal operation after a restart. AI-assisted: this change was prepared with Claude Code.
350c801 to
d59456d
Compare
Related to #1141.
Several public APIs that need a live node didn't check the running state. For example,
OnchainPayment::bump_fee_rbfcould reach wallet mutation and signing afterNode::stop(), while the on-chain send methods returnedError::NotRunning.NotRunningis now returned as the first step of the following API methods, before any validation or side effects:OnchainPayment::bump_fee_rbfopen_channel,open_announced_channel,open_0reserve_channeland their*_with_allvariants (so the announcement-configuration validation no longer runs first),splice_in,splice_in_with_all,splice_out,bump_channel_funding_fee,close_channel,force_close_channel,update_channel_configclaim_for_id,fail_for_id, and thereceive_*via_jit_channel*methodsinitiate_refund,receive_async, andset_paths_to_static_invoice_server(both the Rust and the UniFFI variants). The latter two update state that LDK persists via the background processor, which is no longer running afterstop().Liquidity::add_liquidity_source, and LSPS1request_channel/check_order_status(the running state is now plumbed into both handlers)UnifiedPayment::sendreturnsNotRunningbefore any URI parsing or HRN resolution, instead of falling back to the next payment methodThe checks use the same inline pattern as the existing ones and are placed in the public methods rather than in shared helpers.
Methods that intentionally work offline are left as is: address generation, BOLT11/BOLT12 invoice and offer creation (these only derive data from keys, including the
*_for_hashvariants), listings, balances, signing and verification,remove_payment, forwarding analytics, andlnurl_auth.The TOCTOU race and the broader start/stop lifecycle handling are out of scope here, as discussed.
Testing
unstarted_node_rejects_live_node_apis(unit test): the newly checked methods returnNotRunningon a node that isn't running, including the announced-channel,receive_async,set_paths_to_static_invoice_serverandUnifiedPayment::sendcases. It fails on the previous implementation (e.g.,bump_fee_rbfreturnedInvalidPaymentId,open_announced_channelreturnedChannelCreationFailed) and passes with this change, with and without theuniffifeature.live_node_apis_return_not_running_after_stop(integration test): calls the live-node APIs afterstop(), restarts the node, and checks that it is fully functional again by syncing and sending on-chain.cargo +nightly fmt --checkandcargo clippy --lib -- -D clippy::unwrap_used.AI disclosure
This change was prepared with the assistance of Claude Code.