-
Notifications
You must be signed in to change notification settings - Fork 407
LSPS2: Add error handling events for failed client requests #3804
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Add GetInfoFailed and BuyRequestFailed event variants to LSPS2ClientEvent to handle LSP error responses, similar to the OrderRequestFailed event added to LSPS1. Fixes lightningdevkit#3459
Update handle_get_info_error and handle_buy_error to: - Emit GetInfoFailed and BuyRequestFailed events to notify users - Return LightningError instead of Ok() to properly signal failures - Use the actual error parameter instead of ignoring it This ensures consistent error handling behavior across LSPS implementations.
Replace hardcoded BuyError responses with appropriate error types based on the actual request (GetInfo vs Buy) when rate limiting is triggered. This fixes incorrect error response types and provides an easy way to test the error event handling added in this PR.
Add tests for the new error events: - Test per-peer request limit rejection (GetInfoFailed) - Test global request limit rejection (BuyRequestFailed) - Test invalid token handling (GetInfoFailed) These tests verify that error events are properly emitted when LSP requests fail, ensuring the error handling behavior works end-to-end.
I've assigned @tnull as a reviewer! |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #3804 +/- ##
==========================================
+ Coverage 89.76% 89.82% +0.06%
==========================================
Files 159 159
Lines 128828 128850 +22
Branches 128828 128850 +22
==========================================
+ Hits 115638 115737 +99
+ Misses 10508 10432 -76
+ Partials 2682 2681 -1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Mostly looks good, just some comments.
Will review the test changes closer once #3712 landed and we rebased, as the same refactoring is happening there.
/// A request previously issued via [`LSPS2ClientHandler::select_opening_params`] | ||
/// failed as the LSP returned an error response. | ||
/// | ||
/// [`LSPS2ClientHandler::select_opening_params`]: crate::lsps2::client::LSPS2ClientHandler::select_opening_params |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, preexisting, but it seems we don't document on that method what the returned LSPSRequestId
is for. Can we add that in a separate commit while we're here?
event_queue_notifier.enqueue(LSPS2ClientEvent::GetInfoFailed { | ||
request_id: request_id.clone(), | ||
counterparty_node_id: *counterparty_node_id, | ||
error: error.clone(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can avoid the clones
here and below if we first construct the error result before enqueuing the event, which can then move request_id
/error
.
code: LSPS0_CLIENT_REJECTED_ERROR_CODE, | ||
message: "Reached maximum number of pending requests. Please try again later." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it was very intentional that we hardcoded a standardized response here, because while we want to log the full details, the counterparty shouldn't learn anything about our rate limits.
Could you expand what else you mean with "incorrect error response types"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you expand what else you mean with "incorrect error response types"?
insert_pending_request
is called by handle_get_info_request
and handle_buy_request
but it always returns BuyError
, even if the request type is GetInfo
. Now it returns GetInfoError
for GetInfo
requests and BuyError
for Buy
requests.
👋 The first review has been submitted! Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer. |
✅ Added second reviewer: @joostjager |
service_node.liquidity_manager.handle_custom_message(get_info_request, client_node_id).unwrap(); | ||
|
||
let get_info_event = service_node.liquidity_manager.next_event().unwrap(); | ||
match get_info_event { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With something like this, you can keep the happy flow unindented for improved readability.
let LiquidityEvent::LSPS2Service(LSPS2ServiceEvent::GetInfo {
request_id,
counterparty_node_id,
token,
}) = service_node.liquidity_manager.next_event().unwrap() else {
panic!("Unexpected event");
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let-else bindings are only stabilized since rustc 1.65, so we can't use them currently due to our 1.63 MSRV.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm too bad. Still if let
can I think remove one level of indent?
/// The node id of the LSP. | ||
counterparty_node_id: PublicKey, | ||
/// The error that was returned. | ||
error: LSPSResponseError, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would there be a point in putting these three fields in a struct and reusing that across lsps1 and 2?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, please let's not break the current API pattern in a single place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not in a single place, doing it everywhere. Just a thought, can understand you don't want to change existing API. Although in other cases, we aren't so conservative with API changes?
"Peer {} reached maximum number of total pending requests: {}", | ||
if self.total_pending_requests.load(Ordering::Relaxed) >= MAX_TOTAL_PENDING_REQUESTS { | ||
let error_message = format!( | ||
"reached maximum number of total pending requests {}: {}", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not capitalized anymore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it useful to have counterparty_node_id in this message when the total is exceeded?
@@ -1226,45 +1226,43 @@ where | |||
&self, peer_state_lock: &mut MutexGuard<'a, PeerState>, request_id: LSPSRequestId, | |||
counterparty_node_id: PublicKey, request: LSPS2Request, | |||
) -> (Result<(), LightningError>, Option<LSPSMessage>) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pre-existing but shouldn't the type be Result<(), (LightningError, LSPSMessage>)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, the method returns either Ok(())
or an error, and optionally a message that needs to be sent to the counterparty.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought that in this fn, it's indeed either Ok(()), or an error always accompanied by a msg (not optional). But maybe this is a useful type further up the call stack?
Fixes #3459
Add GetInfoFailed and BuyRequestFailed event variants to LSPS2ClientEvent
to handle LSP error responses, similar to the OrderRequestFailed event
added to LSPS1.