diff --git a/electrum/gui/qt/network_dialog.py b/electrum/gui/qt/network_dialog.py index 268e01b7f..7949eab5a 100644 --- a/electrum/gui/qt/network_dialog.py +++ b/electrum/gui/qt/network_dialog.py @@ -33,7 +33,7 @@ import PyQt5.QtCore as QtCore from electrum.i18n import _ from electrum import constants, blockchain from electrum.util import print_error -from electrum.network import serialize_server, deserialize_server +from electrum.interface import serialize_server, deserialize_server from .util import * @@ -337,7 +337,7 @@ class NetworkChoiceLayout(object): def update(self): host, port, protocol, proxy_config, auto_connect = self.network.get_parameters() self.server_host.setText(host) - self.server_port.setText(port) + self.server_port.setText(str(port)) self.autoconnect_cb.setChecked(auto_connect) interface = self.network.interface diff --git a/electrum/interface.py b/electrum/interface.py index 5cdcd4297..2b63c1978 100644 --- a/electrum/interface.py +++ b/electrum/interface.py @@ -30,6 +30,7 @@ import sys import traceback import asyncio import concurrent.futures +from typing import Tuple, Union import aiorpcx from aiorpcx import ClientSession, Notification, TaskGroup @@ -83,14 +84,28 @@ class CustomTaskGroup(TaskGroup): return super().spawn(*args, **kwargs) +def deserialize_server(server_str: str) -> Tuple[str, int, str]: + # host might be IPv6 address, hence do rsplit: + host, port, protocol = str(server_str).rsplit(':', 2) + if protocol not in ('s', 't'): + raise ValueError('invalid network protocol: {}'.format(protocol)) + port = int(port) # Throw if cannot be converted to int + if not (0 < port < 2**16): + raise ValueError('port {} is out of valid range'.format(port)) + return host, port, protocol + + +def serialize_server(host: str, port: Union[str, int], protocol: str) -> str: + return str(':'.join([host, str(port), protocol])) + + class Interface(PrintError): def __init__(self, network, server, config_path, proxy): self.exception = None self.ready = asyncio.Future() self.server = server - self.host, self.port, self.protocol = self.server.split(':') - self.port = int(self.port) + self.host, self.port, self.protocol = deserialize_server(self.server) self.config_path = config_path self.cert_path = os.path.join(self.config_path, 'certs', self.host) self.tip_header = None diff --git a/electrum/network.py b/electrum/network.py index 5858ed213..ebe40f058 100644 --- a/electrum/network.py +++ b/electrum/network.py @@ -20,7 +20,6 @@ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -import concurrent.futures import time import queue import os @@ -33,7 +32,6 @@ import json import sys import ipaddress import asyncio -import concurrent.futures import dns import dns.resolver @@ -44,7 +42,7 @@ from .util import PrintError, print_error, aiosafe, bfh from .bitcoin import COIN from . import constants from . import blockchain -from .interface import Interface +from .interface import Interface, serialize_server, deserialize_server from .version import PROTOCOL_VERSION NODES_RETRY_INTERVAL = 60 @@ -126,6 +124,7 @@ def deserialize_proxy(s): if s.lower() == 'none': return None proxy = { "mode":"socks5", "host":"localhost" } + # FIXME raw IPv6 address fails here args = s.split(':') n = 0 if proxy_modes.count(args[n]) == 1: @@ -147,19 +146,9 @@ def deserialize_proxy(s): return proxy -def deserialize_server(server_str): - host, port, protocol = str(server_str).rsplit(':', 2) - if protocol not in 'st': - raise ValueError('invalid network protocol: {}'.format(protocol)) - int(port) # Throw if cannot be converted to int - return host, port, protocol - - -def serialize_server(host, port, protocol): - return str(':'.join([host, port, protocol])) - INSTANCE = None + class Network(PrintError): """The Network class manages a set of connections to remote electrum servers, each connected socket is handled by an Interface() object. @@ -794,7 +783,7 @@ class Network(PrintError): if self.interface: server = self.interface.server host, port, protocol, proxy, auto_connect = self.get_parameters() - host, port, protocol = server.split(':') + host, port, protocol = deserialize_server(server) self.set_parameters(host, port, protocol, proxy, auto_connect) def get_local_height(self):