introduce NetworkParameters namedtuple

This commit is contained in:
SomberNight 2018-09-10 00:59:53 +02:00
parent ecf4ea9ba7
commit 3d424077fd
No known key found for this signature in database
GPG key ID: B33B5F232C6271E9
9 changed files with 80 additions and 56 deletions

View file

@ -94,8 +94,9 @@ class ElectrumWindow(App):
auto_connect = BooleanProperty(False) auto_connect = BooleanProperty(False)
def on_auto_connect(self, instance, x): def on_auto_connect(self, instance, x):
host, port, protocol, proxy, auto_connect = self.network.get_parameters() net_params = self.network.get_parameters()
self.network.set_parameters(host, port, protocol, proxy, self.auto_connect) net_params = net_params._replace(auto_connect=self.auto_connect)
self.network.set_parameters(net_params)
def toggle_auto_connect(self, x): def toggle_auto_connect(self, x):
self.auto_connect = not self.auto_connect self.auto_connect = not self.auto_connect
@ -267,11 +268,11 @@ class ElectrumWindow(App):
if self.network: if self.network:
self.num_blocks = self.network.get_local_height() self.num_blocks = self.network.get_local_height()
self.num_nodes = len(self.network.get_interfaces()) self.num_nodes = len(self.network.get_interfaces())
host, port, protocol, proxy_config, auto_connect = self.network.get_parameters() net_params = self.network.get_parameters()
self.server_host = host self.server_host = net_params.host
self.server_port = port self.server_port = net_params.port
self.auto_connect = auto_connect self.auto_connect = net_params.auto_connect
self.proxy_config = proxy_config if proxy_config else {} self.proxy_config = net_params.proxy_config if net_params.proxy_config else {}
self.plugins = kwargs.get('plugins', []) self.plugins = kwargs.get('plugins', [])
self.gui_object = kwargs.get('gui_object', None) self.gui_object = kwargs.get('gui_object', None)

View file

@ -8,6 +8,7 @@ from electrum.i18n import languages
from electrum.gui.kivy.i18n import _ from electrum.gui.kivy.i18n import _
from electrum.plugin import run_hook from electrum.plugin import run_hook
from electrum import coinchooser from electrum import coinchooser
from electrum.network import NetworkProxy
from .choice_dialog import ChoiceDialog from .choice_dialog import ChoiceDialog
@ -154,13 +155,16 @@ class SettingsDialog(Factory.Popup):
self._coinselect_dialog.open() self._coinselect_dialog.open()
def proxy_status(self): def proxy_status(self):
server, port, protocol, proxy, auto_connect = self.app.network.get_parameters() net_params = self.app.network.get_parameters()
proxy = net_params.proxy
return proxy.get('host') +':' + proxy.get('port') if proxy else _('None') return proxy.get('host') +':' + proxy.get('port') if proxy else _('None')
def proxy_dialog(self, item, dt): def proxy_dialog(self, item, dt):
if self._proxy_dialog is None: if self._proxy_dialog is None:
server, port, protocol, proxy, auto_connect = self.app.network.get_parameters() net_params = self.app.network.get_parameters()
proxy = net_params.proxy
def callback(popup): def callback(popup):
nonlocal net_params
if popup.ids.mode.text != 'None': if popup.ids.mode.text != 'None':
proxy = { proxy = {
'mode':popup.ids.mode.text, 'mode':popup.ids.mode.text,
@ -171,7 +175,8 @@ class SettingsDialog(Factory.Popup):
} }
else: else:
proxy = None proxy = None
self.app.network.set_parameters(server, port, protocol, proxy, auto_connect) net_params = net_params._replace(proxy=proxy)
self.app.network.set_parameters(net_params)
item.status = self.proxy_status() item.status = self.proxy_status()
popup = Builder.load_file('electrum/gui/kivy/uix/ui_screens/proxy.kv') popup = Builder.load_file('electrum/gui/kivy/uix/ui_screens/proxy.kv')
popup.ids.mode.text = proxy.get('mode') if proxy else 'None' popup.ids.mode.text = proxy.get('mode') if proxy else 'None'

View file

@ -63,7 +63,7 @@ Popup:
height: '48dp' height: '48dp'
text: _('OK') text: _('OK')
on_release: on_release:
host, port, protocol, proxy, auto_connect = app.network.get_parameters() net_params = app.network.get_parameters()
proxy = {} proxy = {}
proxy['mode']=str(root.ids.mode.text).lower() proxy['mode']=str(root.ids.mode.text).lower()
proxy['host']=str(root.ids.host.text) proxy['host']=str(root.ids.host.text)
@ -71,6 +71,7 @@ Popup:
proxy['user']=str(root.ids.user.text) proxy['user']=str(root.ids.user.text)
proxy['password']=str(root.ids.password.text) proxy['password']=str(root.ids.password.text)
if proxy['mode']=='none': proxy = None if proxy['mode']=='none': proxy = None
app.network.set_parameters(host, port, protocol, proxy, auto_connect) net_params = net_params._replace(proxy=proxy)
app.network.set_parameters(net_params)
app.proxy_config = proxy if proxy else {} app.proxy_config = proxy if proxy else {}
nd.dismiss() nd.dismiss()

View file

@ -56,8 +56,8 @@ Popup:
height: '48dp' height: '48dp'
text: _('OK') text: _('OK')
on_release: on_release:
host, port, protocol, proxy, auto_connect = app.network.get_parameters() net_params = app.network.get_parameters()
host = str(root.ids.host.text) net_params = net_params._replace(host=str(root.ids.host.text),
port = str(root.ids.port.text) port=str(root.ids.port.text))
app.network.set_parameters(host, port, protocol, proxy, auto_connect) app.network.set_parameters(net_params)
nd.dismiss() nd.dismiss()

View file

@ -562,7 +562,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
def donate_to_server(self): def donate_to_server(self):
d = self.network.get_donation_address() d = self.network.get_donation_address()
if d: if d:
host = self.network.get_parameters()[0] host = self.network.get_parameters().host
self.pay_to_URI('bitcoin:%s?message=donation for %s'%(d, host)) self.pay_to_URI('bitcoin:%s?message=donation for %s'%(d, host))
else: else:
self.show_error(_('No donation address for this server')) self.show_error(_('No donation address for this server'))

View file

@ -335,7 +335,9 @@ class NetworkChoiceLayout(object):
w.setEnabled(False) w.setEnabled(False)
def update(self): def update(self):
host, port, protocol, proxy_config, auto_connect = self.network.get_parameters() net_params = self.network.get_parameters()
host, port, protocol = net_params.host, net_params.port, net_params.protocol
proxy_config, auto_connect = net_params.proxy, net_params.auto_connect
self.server_host.setText(host) self.server_host.setText(host)
self.server_port.setText(str(port)) self.server_port.setText(str(port))
self.autoconnect_cb.setChecked(auto_connect) self.autoconnect_cb.setChecked(auto_connect)
@ -368,7 +370,7 @@ class NetworkChoiceLayout(object):
self.nodes_list_widget.update(self.network) self.nodes_list_widget.update(self.network)
def fill_in_proxy_settings(self): def fill_in_proxy_settings(self):
host, port, protocol, proxy_config, auto_connect = self.network.get_parameters() proxy_config = self.network.get_parameters().proxy
if not proxy_config: if not proxy_config:
proxy_config = {"mode": "none", "host": "localhost", "port": "9050"} proxy_config = {"mode": "none", "host": "localhost", "port": "9050"}
@ -409,9 +411,10 @@ class NetworkChoiceLayout(object):
def follow_server(self, server): def follow_server(self, server):
self.network.switch_to_interface(server) self.network.switch_to_interface(server)
host, port, protocol, proxy, auto_connect = self.network.get_parameters() net_params = self.network.get_parameters()
host, port, protocol = deserialize_server(server) host, port, protocol = deserialize_server(server)
self.network.set_parameters(host, port, protocol, proxy, auto_connect) net_params = net_params._replace(host=host, port=port, protocol=protocol)
self.network.set_parameters(net_params)
self.update() self.update()
def server_changed(self, x): def server_changed(self, x):
@ -440,14 +443,14 @@ class NetworkChoiceLayout(object):
pass pass
def set_server(self): def set_server(self):
host, port, protocol, proxy, auto_connect = self.network.get_parameters() net_params = self.network.get_parameters()
host = str(self.server_host.text()) net_params = net_params._replace(host=str(self.server_host.text()),
port = str(self.server_port.text()) port=str(self.server_port.text()),
auto_connect = self.autoconnect_cb.isChecked() auto_connect=self.autoconnect_cb.isChecked())
self.network.set_parameters(host, port, protocol, proxy, auto_connect) self.network.set_parameters(net_params)
def set_proxy(self): def set_proxy(self):
host, port, protocol, proxy, auto_connect = self.network.get_parameters() net_params = self.network.get_parameters()
if self.proxy_cb.isChecked(): if self.proxy_cb.isChecked():
proxy = { 'mode':str(self.proxy_mode.currentText()).lower(), proxy = { 'mode':str(self.proxy_mode.currentText()).lower(),
'host':str(self.proxy_host.text()), 'host':str(self.proxy_host.text()),
@ -457,7 +460,8 @@ class NetworkChoiceLayout(object):
else: else:
proxy = None proxy = None
self.tor_cb.setChecked(False) self.tor_cb.setChecked(False)
self.network.set_parameters(host, port, protocol, proxy, auto_connect) net_params = net_params._replace(proxy=proxy)
self.network.set_parameters(net_params)
def suggest_proxy(self, found_proxy): def suggest_proxy(self, found_proxy):
self.tor_proxy = found_proxy self.tor_proxy = found_proxy

View file

@ -7,7 +7,10 @@ import electrum
from electrum.util import format_satoshis, set_verbosity from electrum.util import format_satoshis, set_verbosity
from electrum.bitcoin import is_address, COIN, TYPE_ADDRESS from electrum.bitcoin import is_address, COIN, TYPE_ADDRESS
from electrum.transaction import TxOutput from electrum.transaction import TxOutput
from .. import Wallet, WalletStorage from electrum.wallet import Wallet
from electrum.storage import WalletStorage
from electrum.network import NetworkParameters
from electrum.interface import deserialize_server
_ = lambda x:x _ = lambda x:x
@ -376,8 +379,9 @@ class ElectrumGui:
def network_dialog(self): def network_dialog(self):
if not self.network: if not self.network:
return return
params = self.network.get_parameters() net_params = self.network.get_parameters()
host, port, protocol, proxy_config, auto_connect = params host, port, protocol = net_params.host, net_params.port, net_params.protocol
proxy_config, auto_connect = net_params.proxy, net_params.auto_connect
srv = 'auto-connect' if auto_connect else self.network.default_server srv = 'auto-connect' if auto_connect else self.network.default_server
out = self.run_dialog('Network', [ out = self.run_dialog('Network', [
{'label':'server', 'type':'str', 'value':srv}, {'label':'server', 'type':'str', 'value':srv},
@ -389,13 +393,13 @@ class ElectrumGui:
auto_connect = server == 'auto-connect' auto_connect = server == 'auto-connect'
if not auto_connect: if not auto_connect:
try: try:
host, port, protocol = server.split(':') host, port, protocol = deserialize_server(server)
except Exception: except Exception:
self.show_message("Error:" + server + "\nIn doubt, type \"auto-connect\"") self.show_message("Error:" + server + "\nIn doubt, type \"auto-connect\"")
return False return False
if out.get('server') or out.get('proxy'): if out.get('server') or out.get('proxy'):
proxy = electrum.network.deserialize_proxy(out.get('proxy')) if out.get('proxy') else proxy_config proxy = electrum.network.deserialize_proxy(out.get('proxy')) if out.get('proxy') else proxy_config
self.network.set_parameters(host, port, protocol, proxy, auto_connect) self.network.set_parameters(NetworkParameters(host, port, protocol, proxy, auto_connect))
def settings_dialog(self): def settings_dialog(self):
fee = str(Decimal(self.config.fee_per_kb()) / COIN) fee = str(Decimal(self.config.fee_per_kb()) / COIN)

View file

@ -84,13 +84,13 @@ class CustomTaskGroup(TaskGroup):
return super().spawn(*args, **kwargs) return super().spawn(*args, **kwargs)
def deserialize_server(server_str: str) -> Tuple[str, int, str]: def deserialize_server(server_str: str) -> Tuple[str, str, str]:
# host might be IPv6 address, hence do rsplit: # host might be IPv6 address, hence do rsplit:
host, port, protocol = str(server_str).rsplit(':', 2) host, port, protocol = str(server_str).rsplit(':', 2)
if protocol not in ('s', 't'): if protocol not in ('s', 't'):
raise ValueError('invalid network protocol: {}'.format(protocol)) raise ValueError('invalid network protocol: {}'.format(protocol))
port = int(port) # Throw if cannot be converted to int int(port) # Throw if cannot be converted to int
if not (0 < port < 2**16): if not (0 < int(port) < 2**16):
raise ValueError('port {} is out of valid range'.format(port)) raise ValueError('port {} is out of valid range'.format(port))
return host, port, protocol return host, port, protocol
@ -106,6 +106,7 @@ class Interface(PrintError):
self.ready = asyncio.Future() self.ready = asyncio.Future()
self.server = server self.server = server
self.host, self.port, self.protocol = deserialize_server(self.server) self.host, self.port, self.protocol = deserialize_server(self.server)
self.port = int(self.port)
self.config_path = config_path self.config_path = config_path
self.cert_path = os.path.join(self.config_path, 'certs', self.host) self.cert_path = os.path.join(self.config_path, 'certs', self.host)
self.tip_header = None self.tip_header = None

View file

@ -32,6 +32,7 @@ import json
import sys import sys
import ipaddress import ipaddress
import asyncio import asyncio
from typing import NamedTuple, Optional
import dns import dns
import dns.resolver import dns.resolver
@ -44,6 +45,7 @@ from . import constants
from . import blockchain from . import blockchain
from .interface import Interface, serialize_server, deserialize_server from .interface import Interface, serialize_server, deserialize_server
from .version import PROTOCOL_VERSION from .version import PROTOCOL_VERSION
from .simple_config import SimpleConfig
NODES_RETRY_INTERVAL = 60 NODES_RETRY_INTERVAL = 60
SERVER_RETRY_INTERVAL = 10 SERVER_RETRY_INTERVAL = 10
@ -106,7 +108,12 @@ def pick_random_server(hostmap = None, protocol = 's', exclude_set = set()):
return random.choice(eligible) if eligible else None return random.choice(eligible) if eligible else None
from .simple_config import SimpleConfig NetworkParameters = NamedTuple("NetworkParameters", [("host", str),
("port", str),
("protocol", str),
("proxy", Optional[dict]),
("auto_connect", bool)])
proxy_modes = ['socks4', 'socks5'] proxy_modes = ['socks4', 'socks5']
@ -118,7 +125,7 @@ def serialize_proxy(p):
p.get('user', ''), p.get('password', '')]) p.get('user', ''), p.get('password', '')])
def deserialize_proxy(s): def deserialize_proxy(s: str) -> Optional[dict]:
if not isinstance(s, str): if not isinstance(s, str):
return None return None
if s.lower() == 'none': if s.lower() == 'none':
@ -369,9 +376,9 @@ class Network(PrintError):
else: else:
self.trigger_callback(key, self.get_status_value(key)) self.trigger_callback(key, self.get_status_value(key))
def get_parameters(self): def get_parameters(self) -> NetworkParameters:
host, port, protocol = deserialize_server(self.default_server) host, port, protocol = deserialize_server(self.default_server)
return host, port, protocol, self.proxy, self.auto_connect return NetworkParameters(host, port, protocol, self.proxy, self.auto_connect)
def get_donation_address(self): def get_donation_address(self):
if self.is_connected(): if self.is_connected():
@ -416,14 +423,13 @@ class Network(PrintError):
self.start_interface(server) self.start_interface(server)
return server return server
def set_proxy(self, proxy): def set_proxy(self, proxy: Optional[dict]):
self.proxy = proxy self.proxy = proxy
# Store these somewhere so we can un-monkey-patch # Store these somewhere so we can un-monkey-patch
if not hasattr(socket, "_socketobject"): if not hasattr(socket, "_socketobject"):
socket._getaddrinfo = socket.getaddrinfo socket._getaddrinfo = socket.getaddrinfo
if proxy: if proxy:
self.print_error('setting proxy', proxy) self.print_error('setting proxy', proxy)
proxy_mode = proxy_modes.index(proxy["mode"]) + 1
# prevent dns leaks, see http://stackoverflow.com/questions/13184205/dns-over-proxy # prevent dns leaks, see http://stackoverflow.com/questions/13184205/dns-over-proxy
socket.getaddrinfo = lambda *args: [(socket.AF_INET, socket.SOCK_STREAM, 6, '', (args[0], args[1]))] socket.getaddrinfo = lambda *args: [(socket.AF_INET, socket.SOCK_STREAM, 6, '', (args[0], args[1]))]
else: else:
@ -465,7 +471,7 @@ class Network(PrintError):
return socket._getaddrinfo(addr, *args, **kwargs) return socket._getaddrinfo(addr, *args, **kwargs)
@with_interface_lock @with_interface_lock
def start_network(self, protocol, proxy): def start_network(self, protocol: str, proxy: Optional[dict]):
assert not self.interface and not self.interfaces assert not self.interface and not self.interfaces
assert not self.connecting and self.socket_queue.empty() assert not self.connecting and self.socket_queue.empty()
self.print_error('starting network') self.print_error('starting network')
@ -487,9 +493,11 @@ class Network(PrintError):
# Get a new queue - no old pending connections thanks! # Get a new queue - no old pending connections thanks!
self.socket_queue = queue.Queue() self.socket_queue = queue.Queue()
def set_parameters(self, host, port, protocol, proxy, auto_connect): def set_parameters(self, net_params: NetworkParameters):
proxy = net_params.proxy
proxy_str = serialize_proxy(proxy) proxy_str = serialize_proxy(proxy)
server = serialize_server(host, port, protocol) host, port, protocol = net_params.host, net_params.port, net_params.protocol
server_str = serialize_server(host, port, protocol)
# sanitize parameters # sanitize parameters
try: try:
deserialize_server(serialize_server(host, port, protocol)) deserialize_server(serialize_server(host, port, protocol))
@ -498,21 +506,21 @@ class Network(PrintError):
int(proxy['port']) int(proxy['port'])
except: except:
return return
self.config.set_key('auto_connect', auto_connect, False) self.config.set_key('auto_connect', net_params.auto_connect, False)
self.config.set_key("proxy", proxy_str, False) self.config.set_key("proxy", proxy_str, False)
self.config.set_key("server", server, True) self.config.set_key("server", server_str, True)
# abort if changes were not allowed by config # abort if changes were not allowed by config
if self.config.get('server') != server or self.config.get('proxy') != proxy_str: if self.config.get('server') != server_str or self.config.get('proxy') != proxy_str:
return return
self.auto_connect = auto_connect self.auto_connect = net_params.auto_connect
if self.proxy != proxy or self.protocol != protocol: if self.proxy != proxy or self.protocol != protocol:
# Restart the network defaulting to the given server # Restart the network defaulting to the given server
with self.interface_lock: with self.interface_lock:
self.stop_network() self.stop_network()
self.default_server = server self.default_server = server_str
self.start_network(protocol, proxy) self.start_network(protocol, proxy)
elif self.default_server != server: elif self.default_server != server_str:
self.switch_to_interface(server) self.switch_to_interface(server_str)
else: else:
self.switch_lagging_interface() self.switch_lagging_interface()
self.notify('updated') self.notify('updated')
@ -781,10 +789,10 @@ class Network(PrintError):
with self.interface_lock: with self.interface_lock:
if self.interface: if self.interface:
server = self.interface.server net_params = self.get_parameters()
host, port, protocol, proxy, auto_connect = self.get_parameters() host, port, protocol = deserialize_server(self.interface.server)
host, port, protocol = deserialize_server(server) net_params = net_params._replace(host=host, port=port, protocol=protocol)
self.set_parameters(host, port, protocol, proxy, auto_connect) self.set_parameters(net_params)
def get_local_height(self): def get_local_height(self):
return self.blockchain().height() return self.blockchain().height()