What changed, and why it matters
This commit adds a safety check when Electrum connects to a Bitcoin server. It asks the server for its genesis block hash and disconnects if it doesn't match the network Electrum is configured for (mainnet, testnet, signet, etc.). This prevents accidental cross-network connections that could fill the peer list with incompatible servers and make the wallet misbehave. It is a hardening fix, not a clear-cut exploit patch.
Treat as a defensive hardening improvement. Review whether the server.features call is available on all supported ElectrumX versions and whether older servers lacking it are handled gracefully. No urgent security response appears necessary.
Security signals we found
Network boundary enforcement added
Genesis hash validation on connection
Graceful disconnect on mismatch
Unit test coverage added for new behavior
Evidence from the diff
In electrum/interface.py, after the existing healthy-spread check, the client now calls server.features and compares the returned genesis_hash against constants.net.GENESIS. A mismatch raises GracefulDisconnect. A unit test was added to mock server.features. The change prevents a signet-configured client from connecting to mainnet servers and vice versa, which previously could pollute recent peers and cause confusion.
Changed components
electrum/interface.pytests/test_interface.pyInspect captured patch +21 / −0
diff --git a/electrum/interface.py b/electrum/interface.py
index 36cfb13..606c9b9 100644
--- a/electrum/interface.py
+++ b/electrum/interface.py
@@ -964,6 +964,14 @@ class Interface(Logger):
if not self.network.check_interface_against_healthy_spread_of_connected_servers(self):
raise GracefulDisconnect(f'too many connected servers already '
f'in bucket {self.bucket_based_on_ipaddress()}')
+
+ try:
+ features = await session.send_request('server.features')
+ server_genesis_hash = assert_dict_contains_field(features, field_name='genesis_hash')
+ except (aiorpcx.jsonrpc.RPCError, RequestCorrupted) as e:
+ raise GracefulDisconnect(e)
+ if server_genesis_hash != constants.net.GENESIS:
+ raise GracefulDisconnect(f'server on different chain: {server_genesis_hash=}. ours: {constants.net.GENESIS}')
self.logger.info(f"connection established. version: {ver}")
try:
diff --git a/tests/test_interface.py b/tests/test_interface.py
index da4f607..07b1333 100644
--- a/tests/test_interface.py
+++ b/tests/test_interface.py
@@ -11,6 +11,7 @@ from electrum.util import OldTaskGroup, bfh
from electrum.logging import Logger
from electrum.simple_config import SimpleConfig
from electrum.transaction import Transaction
+from electrum import constants
from . import ElectrumTestCase
@@ -130,6 +131,7 @@ class ServerSession(aiorpcx.RPCSession, Logger):
async def handle_request(self, request):
handlers = {
'server.version': self._handle_server_version,
+ 'server.features': self._handle_server_features,
'blockchain.estimatefee': self._handle_estimatefee,
'blockchain.headers.subscribe': self._handle_headers_subscribe,
'blockchain.block.header': self._handle_block_header,
@@ -146,6 +148,17 @@ class ServerSession(aiorpcx.RPCSession, Logger):
async def _handle_server_version(self, client_name='', protocol_version=None):
return ['best_server_impl/0.1', '1.4']
+ async def _handle_server_features(self) -> dict:
+ return {
+ 'genesis_hash': constants.net.GENESIS,
+ 'hosts': {"14.3.140.101": {"tcp_port": 51001, "ssl_port": 51002}},
+ 'protocol_max': '1.7.0',
+ 'protocol_min': '1.4.3',
+ 'pruning': None,
+ 'server_version': 'ElectrumX 1.19.0',
+ 'hash_function': 'sha256',
+ }
+
async def _handle_estimatefee(self, number, mode=None):
return 1000
Why this scored 35/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.