Add Electrum protocol version negotiation#62
Conversation
|
The direction looks right: One concern: the advertised protocol range is Can we either advertise |
|
Yes, absolutely. There are so many versions of the protocol by now that we should generally agree on one version and then manage it properly. I’m happy to take another pass at it and update the PR. |
Moves server.version handling out of _ping_loop into a dedicated `handshaking` state in the monitor loop state machine. The handshake runs synchronously after recv/write/notify threads are started but before the ping thread is launched, ensuring the protocol ordering constraint (server.version before server.ping, required since protocol 1.2) is enforced by the architecture, not just by call order within a thread. Changes: - New `handshaking` state between `creating_threads` and `execute_recreation_callback`; ping thread only starts after a successful handshake - Protocol range raised from ["1.2", "1.4"] to ["1.3", "1.4"]; blockchain.block.header, used throughout spectrum.py, was added in 1.3 - Negotiated server software version and protocol version stored as `server_software_version` and `negotiated_protocol` on the socket instance and logged at INFO level - All failure paths (RPCError, timeout, unexpected response format, sub-minimum negotiated version) trigger reconnect via `broken_killing_threads` - Fix logger.exception() missing argument in _create_threads Tested against libbitcoin, ElectrumX, Fulcrum and electrs-esplora.
What
This introduces proper protocol version negotiation to the Electrum Interface.
Changes
Min/Max Versions: Mininum Supported Version is 1.3 since we make use of
blockchain.block.header. Maximum is 1.4 since we don't implement any 1.5 features yet.Result stored: The server's response
[server_software_version, negotiated_protocol]is stored on the socket instance and logged at INFO level on every connection and reconnect.Fail/reconnect on all error paths: RPCError (e.g. libbitcoin code 19, ElectrumX code 1), timeout, unexpected response format, and a negotiated version below our minimum all trigger an immediate reconnect via the existing
broken_killing_threadspath. Previously, failures were logged as warnings and the connection continued without a negotiated version.Architecture: The handshake is moved out of
_ping_loopinto a newhandshakingstate in the monitor loop state machine, betweencreating_threadsandexecute_recreation_callback. The recv, write and notify threads are started first (soself.call()works), the handshake runs synchronously in the monitor loop, and the ping thread is only started after a successful handshake. This guarantees thatstatus == okis never set before version negotiation has completed, and thatserver.versionalways precedesserver.pingby construction.Tested against
Limitations
["1.3", "1.4"]). There is no mechanism to negotiate a different range at runtime or per-instance.