fix(electrum): return JSON-RPC error for unknown methods instead of dropping the connection#220
Open
EddieHouston wants to merge 1 commit into
Conversation
…ropping the connection The catch-all arm in handle_command used bail!, which returns Err from the whole function and bypasses the error-to-JSON conversion below it. The error then propagated out of handle_replies and run() shut the socket down, so any unknown method (including methods compiled out by feature flags, like scripthash.get_balance on Liquid builds) closed the connection with no reply, and a single unknown method inside a batch killed the entire batch. Produce the error as a value inside 'result' instead, so it is returned as a normal JSON-RPC error reply and the connection stays open. The method's params are no longer echoed back in the error message.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resolves #219
Problem
Any method without a dispatch arm in
handle_command— a typo, an unimplementedElectrum method (
blockchain.scripthash.get_mempool,blockchain.transaction.get_tsc_merkle), orblockchain.scripthash.get_balanceonLiquid builds (where the arm is compiled out via
#[cfg(not(feature = "liquid"))]) —causes the server to close the TCP connection without writing any JSON-RPC response.
Clients cannot distinguish an intentionally unsupported method from an outage, and
retrying clients turn one cheap error reply into repeated TCP+TLS reconnects.
Inside a batch request, a single unknown method kills the whole batch and the
connection; the remaining requests are never answered.
Cause
The catch-all arm in
handle_command:bail!returnsErrfrom the whole function, bypassing the error-to-JSON conversiona few lines below — that conversion only sees errors that dispatched handlers assign
into
result. The unknown-method error instead propagates through the?inhandle_valueandhandle_replies, andrun()logs "connection handling failed"and shuts the socket down.
Fix
Produce the error as a value inside
result, so it flows through the existingconversion and is returned as a normal JSON-RPC error reply:
Electrum server implementations (ElectrumX, Fulcrum).
paramsare deliberately omitted from the message so client-controlled input isnot echoed back; only the method name is included.
Prior art
Both related projects have already fixed this. mempool/electrs (a fork of this
codebase) replaced the catch-all in mempool/electrs#103 (merged 2024-11-14) to return
a JSON-RPC error object with code
-32601and keep the connection open;romanz/electrs (the v0.9 rewrite) returns
{"code": -32601, "message": "method not found"}via its typedRpcErrorenum. This PR keeps the existing bare-string errorshape for minimal diff; moving to JSON-RPC 2.0 error objects across the board is left
to a follow-up.
Out of scope
Malformed input still drops the connection, unchanged: invalid JSON, requests
without a string
method/ arrayparams/id, oversized batches, invalid UTF-8,and TLS bytes on the plaintext port. The first two arguably deserve error replies as
well (with
"id": nullper JSON-RPC), but that is a larger change than this fix.Testing
Against a local instance:
Before: connection closes with no payload.
After:
{"error":"unknown method blockchain.scripthash.get_mempool","id":1,"jsonrpc":"2.0"}and the connection remains open. A batch mixing
server.pingwith an unknown methodnow returns results for both entries instead of dropping the connection.
Full
cargo test(lib + integration: 4 electrum, 22 REST) passes.