Repository navigation
Conversation
|
I've assigned @benthecarman as a reviewer! |
benthecarman
left a comment
There was a problem hiding this comment.
The wait logic here is mostly the same as pay_wait.rs just copied. Could we pull the common parts into a shared module before this lands? pay --wait and open-channel --wait are the first two, and close-channel or splice will likely want --wait too. I'd rather the next one doesn't become a third copy.
Thanks for the comment, that makes a lot of sense. I added new commit ce9d501 with the common parts in |
|
you do this as a refactor commit before the open-channel changes |
| pub(crate) async fn open_channel_and_wait( | ||
| client: &LdkServerClient, request: OpenChannelRequest, timeout: Option<Duration>, | ||
| ) { | ||
| let mut events = client.subscribe_events().await.map_err(handle_error).unwrap(); |
There was a problem hiding this comment.
I see this was introduced in pay_wait.rs but not sure we need the unwrap() here? Given that the handle_error() function never returns. Same as below and elsewhere
There was a problem hiding this comment.
Without the unwrap() it fails to compile, since map_err keeps the Result and only changes the error type to !:
expected `&mut GrpcStream<EventEnvelope>`, found `&mut Result<GrpcStream<_>, !>`Maybe changing it to something like below would read better:
let mut events = client.subscribe_events().await.unwrap_or_else(|e| handle_error(e));Happy to apply it here and in pay_wait.rs, if that sounds good.
There was a problem hiding this comment.
Maybe changing it to something like below would read better:
let mut events = client.subscribe_events().await.unwrap_or_else(|e| handle_error(e));
Yes, it does. Thank you!
There was a problem hiding this comment.
I added the changes to it's corresponding commits:
8e21bcf#diff-d29e74cfebaf01882098a3e9ea4a9229d8e7766881d0cfca168d13e68d93236aL49
d2b4635#diff-fb260124cb53a088a5ac045c42477fb33b7230d3bf4d0dfa8edc00d9f85bb84aR32
ce9d501 to
da0bfbb
Compare
Sure I changed so the refactor commit where |
Move the event-stream wait loop out of pay_wait.rs into a new event_wait module, so other CLI commands that need to block on a server event can use it. wait_for_event takes a matcher closure and returns WaitOutcome::Matched or WaitOutcome::TimedOut. It also uses timeout_at with a single deadline, so the remaining time no longer has to be recomputed on every loop. pay_wait now calls this helper with a small classify() function that maps PaymentSuccessful/PaymentFailed events into Ok/Err. The exit codes are renamed to EXIT_WAIT_FAILED/EXIT_WAIT_TIMEOUT and are shared from event_wait. Their values (2 and 3) and the CLI behavior are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Previously `open-channel` returned as soon as the open was initiated. Scripts had to poll list-channels to find out whether the counterparty accepted or rejected the channel. When the open was rejected, the channel did not show up in list-channels at all, and the reason only appeared in the server logs. With `--wait`, the CLI subscribes to the event stream before calling open_channel, because the stream is live-only. It then waits for a ChannelStateChanged event for the returned user_channel_id: - Pending (or Ready, if Pending was missed) counts as success. The CLI prints the channel_id and funding_txo. - OpenFailed or Closed counts as failure. The CLI prints the reason reported by the server. It does not wait for the funding transaction to confirm. `--wait-timeout <SECS>` limits how long it waits. On timeout, the CLI prints the current list_channels entry for the channel, if there is one. The exit codes match `pay --wait`: 0 for pending, 1 for a request or event stream error, 2 for a failed open and 3 for a timeout. Example: the counterparty rejects a channel below its minimum size: Channel open initiated: <user-channel-id> (waiting for the counterparty) Channel open <user-channel-id> failed: Channel closed because counterparty force-closed with message: chan size of 0.00100000 BTC is below min chan size of 0.00200000 BTC Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
da0bfbb to
d2b4635
Compare
benthecarman
left a comment
There was a problem hiding this comment.
Could we add e2e tests for --wait? Both cases should work with the current harness:
- Success: the same setup as
open_channel_via_cli, with--waitadded. Assert exit code 0 and that the output haschannel_idandfunding_txo. - Rejection: don't fund
server_b. ldk-node rejects inbound anchor channels when it lacks the on-chain reserve, so the open should fail with "Channel request rejected". Assert exit code 2 and that the reason is in the output.
run_cli_raw panics on any non-zero exit, so the rejection test needs a small helper that returns the raw Output. run_cli_raw could then call it.
| WaitOutcome::Matched(Outcome::Pending(event)) => print_response(&json!({ | ||
| "user_channel_id": event.user_channel_id, | ||
| "channel_id": event.channel_id, | ||
| "funding_txo": event.funding_txo, | ||
| })), | ||
| WaitOutcome::Matched(Outcome::Failed(event)) => { | ||
| let reason = event | ||
| .reason | ||
| .map(|reason| reason.message) | ||
| .filter(|message| !message.is_empty()) | ||
| .unwrap_or_else(|| "no reason given".to_string()); | ||
| eprintln!( | ||
| "{}", | ||
| sanitize_for_terminal(format!("Channel open {user_channel_id} failed: {reason}")) | ||
| ); | ||
| std::process::exit(EXIT_WAIT_FAILED); |
There was a problem hiding this comment.
Could success and failure both print the ChannelStateChanged event to stdout?
At the moment success prints a hand-built json! object, and failure prints nothing to stdout, only a message on stderr. A script that sees exit code 2 has to parse the stderr text to learn why the open failed. pay --wait prints a GetPaymentDetailsResponse for success, failure and timeout, so scripts can always parse stdout the same way.
We can't do quite the same here, because a channel that failed to open is already gone from list_channels. But the event works for both resolved outcomes:
WaitOutcome::Matched(Outcome::Pending(event)) => print_response(&event),
WaitOutcome::Matched(Outcome::Failed(event)) => {
// keep the existing stderr message
...
print_response(&event);
std::process::exit(EXIT_WAIT_FAILED);
},On success this still includes user_channel_id, channel_id and funding_txo, and adds state and counterparty_node_id. On failure, scripts can read reason.kind instead of matching on message text. The serde_json::json import can go after this change. Keeping the Channel from list_channels on timeout is fine, since pay also queries the current state there.
open-channelreturned as soon as the open was initiated, so scripts had to poll list-channels to learn whether the counterparty accepted or rejected the channel.With
--wait, the CLI subscribes to the event stream before calling open_channel, since the stream is live-only, and waits for a ChannelStateChanged event for the returned user_channel_id. Pending (or Ready, if Pending was missed) counts as success and prints the channel_id and funding_txo. OpenFailed or Closed counts as failure and prints the reason from the server. It does not wait for the funding transaction to confirm.--wait-timeout <SECS>bounds the wait. On timeout the CLI prints the current entry for the channel from list_channels, if there is one. Exit codes are 0 for pending, 1 for a request or event stream error, 2 for a failed open and 3 for a timeout, matchingpay --wait.For example when opening a channel for 100.000 sats when 200.000 is the minimum required by the counterpart node the command
open-channelwill finish and just replied:{ "user_channel_id": "<user-channel-id>" }But if the user looks for the channel using
list-channelsthe user cannot see anything about that channel. In order to know what happened the user has to actually look at the logs of the server. Or there might be other way I did not find.With the new
--waitwhen the same situation as before happens, it gives the user information about the failed channel open and the reason.And when the opening is successful it gives: