Browse Source

introduce NetworkParameters namedtuple

3.3.3.1
SomberNight 7 years ago
parent
commit
3d424077fd
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 15
      electrum/gui/kivy/main_window.py
  2. 11
      electrum/gui/kivy/uix/dialogs/settings.py
  3. 5
      electrum/gui/kivy/uix/ui_screens/proxy.kv
  4. 8
      electrum/gui/kivy/uix/ui_screens/server.kv
  5. 2
      electrum/gui/qt/main_window.py
  6. 26
      electrum/gui/qt/network_dialog.py
  7. 14
      electrum/gui/text.py
  8. 7
      electrum/interface.py
  9. 48
      electrum/network.py

15
electrum/gui/kivy/main_window.py

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

11
electrum/gui/kivy/uix/dialogs/settings.py

@ -8,6 +8,7 @@ from electrum.i18n import languages
from electrum.gui.kivy.i18n import _
from electrum.plugin import run_hook
from electrum import coinchooser
from electrum.network import NetworkProxy
from .choice_dialog import ChoiceDialog
@ -154,13 +155,16 @@ class SettingsDialog(Factory.Popup):
self._coinselect_dialog.open()
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')
def proxy_dialog(self, item, dt):
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):
nonlocal net_params
if popup.ids.mode.text != 'None':
proxy = {
'mode':popup.ids.mode.text,
@ -171,7 +175,8 @@ class SettingsDialog(Factory.Popup):
}
else:
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()
popup = Builder.load_file('electrum/gui/kivy/uix/ui_screens/proxy.kv')
popup.ids.mode.text = proxy.get('mode') if proxy else 'None'

5
electrum/gui/kivy/uix/ui_screens/proxy.kv

@ -63,7 +63,7 @@ Popup:
height: '48dp'
text: _('OK')
on_release:
host, port, protocol, proxy, auto_connect = app.network.get_parameters()
net_params = app.network.get_parameters()
proxy = {}
proxy['mode']=str(root.ids.mode.text).lower()
proxy['host']=str(root.ids.host.text)
@ -71,6 +71,7 @@ Popup:
proxy['user']=str(root.ids.user.text)
proxy['password']=str(root.ids.password.text)
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 {}
nd.dismiss()

8
electrum/gui/kivy/uix/ui_screens/server.kv

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

2
electrum/gui/qt/main_window.py

@ -562,7 +562,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
def donate_to_server(self):
d = self.network.get_donation_address()
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))
else:
self.show_error(_('No donation address for this server'))

26
electrum/gui/qt/network_dialog.py

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

14
electrum/gui/text.py

@ -7,7 +7,10 @@ import electrum
from electrum.util import format_satoshis, set_verbosity
from electrum.bitcoin import is_address, COIN, TYPE_ADDRESS
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
@ -376,8 +379,9 @@ class ElectrumGui:
def network_dialog(self):
if not self.network:
return
params = self.network.get_parameters()
host, port, protocol, proxy_config, auto_connect = params
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
srv = 'auto-connect' if auto_connect else self.network.default_server
out = self.run_dialog('Network', [
{'label':'server', 'type':'str', 'value':srv},
@ -389,13 +393,13 @@ class ElectrumGui:
auto_connect = server == 'auto-connect'
if not auto_connect:
try:
host, port, protocol = server.split(':')
host, port, protocol = deserialize_server(server)
except Exception:
self.show_message("Error:" + server + "\nIn doubt, type \"auto-connect\"")
return False
if out.get('server') or out.get('proxy'):
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):
fee = str(Decimal(self.config.fee_per_kb()) / COIN)

7
electrum/interface.py

@ -84,13 +84,13 @@ class CustomTaskGroup(TaskGroup):
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, 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):
int(port) # Throw if cannot be converted to int
if not (0 < int(port) < 2**16):
raise ValueError('port {} is out of valid range'.format(port))
return host, port, protocol
@ -106,6 +106,7 @@ class Interface(PrintError):
self.ready = asyncio.Future()
self.server = server
self.host, self.port, self.protocol = deserialize_server(self.server)
self.port = int(self.port)
self.config_path = config_path
self.cert_path = os.path.join(self.config_path, 'certs', self.host)
self.tip_header = None

48
electrum/network.py

@ -32,6 +32,7 @@ import json
import sys
import ipaddress
import asyncio
from typing import NamedTuple, Optional
import dns
import dns.resolver
@ -44,6 +45,7 @@ from . import constants
from . import blockchain
from .interface import Interface, serialize_server, deserialize_server
from .version import PROTOCOL_VERSION
from .simple_config import SimpleConfig
NODES_RETRY_INTERVAL = 60
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
from .simple_config import SimpleConfig
NetworkParameters = NamedTuple("NetworkParameters", [("host", str),
("port", str),
("protocol", str),
("proxy", Optional[dict]),
("auto_connect", bool)])
proxy_modes = ['socks4', 'socks5']
@ -118,7 +125,7 @@ def serialize_proxy(p):
p.get('user', ''), p.get('password', '')])
def deserialize_proxy(s):
def deserialize_proxy(s: str) -> Optional[dict]:
if not isinstance(s, str):
return None
if s.lower() == 'none':
@ -369,9 +376,9 @@ class Network(PrintError):
else:
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)
return host, port, protocol, self.proxy, self.auto_connect
return NetworkParameters(host, port, protocol, self.proxy, self.auto_connect)
def get_donation_address(self):
if self.is_connected():
@ -416,14 +423,13 @@ class Network(PrintError):
self.start_interface(server)
return server
def set_proxy(self, proxy):
def set_proxy(self, proxy: Optional[dict]):
self.proxy = proxy
# Store these somewhere so we can un-monkey-patch
if not hasattr(socket, "_socketobject"):
socket._getaddrinfo = socket.getaddrinfo
if 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
socket.getaddrinfo = lambda *args: [(socket.AF_INET, socket.SOCK_STREAM, 6, '', (args[0], args[1]))]
else:
@ -465,7 +471,7 @@ class Network(PrintError):
return socket._getaddrinfo(addr, *args, **kwargs)
@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.connecting and self.socket_queue.empty()
self.print_error('starting network')
@ -487,9 +493,11 @@ class Network(PrintError):
# Get a new queue - no old pending connections thanks!
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)
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
try:
deserialize_server(serialize_server(host, port, protocol))
@ -498,21 +506,21 @@ class Network(PrintError):
int(proxy['port'])
except:
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("server", server, True)
self.config.set_key("server", server_str, True)
# 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
self.auto_connect = auto_connect
self.auto_connect = net_params.auto_connect
if self.proxy != proxy or self.protocol != protocol:
# Restart the network defaulting to the given server
with self.interface_lock:
self.stop_network()
self.default_server = server
self.default_server = server_str
self.start_network(protocol, proxy)
elif self.default_server != server:
self.switch_to_interface(server)
elif self.default_server != server_str:
self.switch_to_interface(server_str)
else:
self.switch_lagging_interface()
self.notify('updated')
@ -781,10 +789,10 @@ class Network(PrintError):
with self.interface_lock:
if self.interface:
server = self.interface.server
host, port, protocol, proxy, auto_connect = self.get_parameters()
host, port, protocol = deserialize_server(server)
self.set_parameters(host, port, protocol, proxy, auto_connect)
net_params = self.get_parameters()
host, port, protocol = deserialize_server(self.interface.server)
net_params = net_params._replace(host=host, port=port, protocol=protocol)
self.set_parameters(net_params)
def get_local_height(self):
return self.blockchain().height()

Loading…
Cancel
Save