mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-23 17:47:31 +00:00
network: allow mixed protocols among interfaces
Previously all the interfaces used either "t" or "s". Now the network only tries to use "s" for all interfaces, except for the main interface, which the user can manually specify to use "t". (so e.g. if you run with "--server localhost:50002:t", the main server will use "t", but all the rest will use "s")
This commit is contained in:
parent
872380a525
commit
adc3784bc2
4 changed files with 34 additions and 41 deletions
|
@ -37,7 +37,7 @@ from PyQt5.QtGui import QFontMetrics
|
||||||
from electrum.i18n import _
|
from electrum.i18n import _
|
||||||
from electrum import constants, blockchain, util
|
from electrum import constants, blockchain, util
|
||||||
from electrum.interface import ServerAddr
|
from electrum.interface import ServerAddr
|
||||||
from electrum.network import Network
|
from electrum.network import Network, PREFERRED_NETWORK_PROTOCOL
|
||||||
from electrum.logging import get_logger
|
from electrum.logging import get_logger
|
||||||
|
|
||||||
from .util import (Buttons, CloseButton, HelpButton, read_QIcon, char_width_in_lineedit,
|
from .util import (Buttons, CloseButton, HelpButton, read_QIcon, char_width_in_lineedit,
|
||||||
|
@ -72,6 +72,8 @@ class NetworkDialog(QDialog):
|
||||||
|
|
||||||
|
|
||||||
class NodesListWidget(QTreeWidget):
|
class NodesListWidget(QTreeWidget):
|
||||||
|
"""List of connected servers."""
|
||||||
|
|
||||||
SERVER_ADDR_ROLE = Qt.UserRole + 100
|
SERVER_ADDR_ROLE = Qt.UserRole + 100
|
||||||
CHAIN_ID_ROLE = Qt.UserRole + 101
|
CHAIN_ID_ROLE = Qt.UserRole + 101
|
||||||
IS_SERVER_ROLE = Qt.UserRole + 102
|
IS_SERVER_ROLE = Qt.UserRole + 102
|
||||||
|
@ -129,6 +131,7 @@ class NodesListWidget(QTreeWidget):
|
||||||
item = QTreeWidgetItem([i.host + star, '%d'%i.tip])
|
item = QTreeWidgetItem([i.host + star, '%d'%i.tip])
|
||||||
item.setData(0, self.IS_SERVER_ROLE, 1)
|
item.setData(0, self.IS_SERVER_ROLE, 1)
|
||||||
item.setData(0, self.SERVER_ADDR_ROLE, i.server)
|
item.setData(0, self.SERVER_ADDR_ROLE, i.server)
|
||||||
|
item.setToolTip(0, str(i.server))
|
||||||
x.addChild(item)
|
x.addChild(item)
|
||||||
if n_chains > 1:
|
if n_chains > 1:
|
||||||
self.addTopLevelItem(x)
|
self.addTopLevelItem(x)
|
||||||
|
@ -143,6 +146,8 @@ class NodesListWidget(QTreeWidget):
|
||||||
|
|
||||||
|
|
||||||
class ServerListWidget(QTreeWidget):
|
class ServerListWidget(QTreeWidget):
|
||||||
|
"""List of all known servers."""
|
||||||
|
|
||||||
class Columns(IntEnum):
|
class Columns(IntEnum):
|
||||||
HOST = 0
|
HOST = 0
|
||||||
PORT = 1
|
PORT = 1
|
||||||
|
@ -182,8 +187,9 @@ class ServerListWidget(QTreeWidget):
|
||||||
pt.setX(50)
|
pt.setX(50)
|
||||||
self.customContextMenuRequested.emit(pt)
|
self.customContextMenuRequested.emit(pt)
|
||||||
|
|
||||||
def update(self, servers, protocol, use_tor):
|
def update(self, servers, use_tor):
|
||||||
self.clear()
|
self.clear()
|
||||||
|
protocol = PREFERRED_NETWORK_PROTOCOL
|
||||||
for _host, d in sorted(servers.items()):
|
for _host, d in sorted(servers.items()):
|
||||||
if _host.endswith('.onion') and not use_tor:
|
if _host.endswith('.onion') and not use_tor:
|
||||||
continue
|
continue
|
||||||
|
@ -207,7 +213,6 @@ class NetworkChoiceLayout(object):
|
||||||
def __init__(self, network: Network, config, wizard=False):
|
def __init__(self, network: Network, config, wizard=False):
|
||||||
self.network = network
|
self.network = network
|
||||||
self.config = config
|
self.config = config
|
||||||
self.protocol = None
|
|
||||||
self.tor_proxy = None
|
self.tor_proxy = None
|
||||||
|
|
||||||
self.tabs = tabs = QTabWidget()
|
self.tabs = tabs = QTabWidget()
|
||||||
|
@ -370,9 +375,8 @@ class NetworkChoiceLayout(object):
|
||||||
host = interface.host if interface else _('None')
|
host = interface.host if interface else _('None')
|
||||||
self.server_label.setText(host)
|
self.server_label.setText(host)
|
||||||
|
|
||||||
self.set_protocol(protocol)
|
|
||||||
self.servers = self.network.get_servers()
|
self.servers = self.network.get_servers()
|
||||||
self.servers_list.update(self.servers, self.protocol, self.tor_cb.isChecked())
|
self.servers_list.update(self.servers, self.tor_cb.isChecked())
|
||||||
self.enable_set_server()
|
self.enable_set_server()
|
||||||
|
|
||||||
height_str = "%d "%(self.network.get_local_height()) + _('blocks')
|
height_str = "%d "%(self.network.get_local_height()) + _('blocks')
|
||||||
|
@ -413,22 +417,6 @@ class NetworkChoiceLayout(object):
|
||||||
def layout(self):
|
def layout(self):
|
||||||
return self.layout_
|
return self.layout_
|
||||||
|
|
||||||
def set_protocol(self, protocol):
|
|
||||||
if protocol != self.protocol:
|
|
||||||
self.protocol = protocol
|
|
||||||
|
|
||||||
def change_protocol(self, use_ssl):
|
|
||||||
p = 's' if use_ssl else 't'
|
|
||||||
host = self.server_host.text()
|
|
||||||
pp = self.servers.get(host, constants.net.DEFAULT_PORTS)
|
|
||||||
if p not in pp.keys():
|
|
||||||
p = list(pp.keys())[0]
|
|
||||||
port = pp[p]
|
|
||||||
self.server_host.setText(host)
|
|
||||||
self.server_port.setText(port)
|
|
||||||
self.set_protocol(p)
|
|
||||||
self.set_server()
|
|
||||||
|
|
||||||
def follow_branch(self, chain_id):
|
def follow_branch(self, chain_id):
|
||||||
self.network.run_from_another_thread(self.network.follow_chain_given_id(chain_id))
|
self.network.run_from_another_thread(self.network.follow_chain_given_id(chain_id))
|
||||||
self.update()
|
self.update()
|
||||||
|
@ -437,10 +425,6 @@ class NetworkChoiceLayout(object):
|
||||||
self.network.run_from_another_thread(self.network.follow_chain_given_server(server))
|
self.network.run_from_another_thread(self.network.follow_chain_given_server(server))
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def server_changed(self, x):
|
|
||||||
if x:
|
|
||||||
self.change_server(str(x.text(0)), self.protocol)
|
|
||||||
|
|
||||||
def change_server(self, host, protocol):
|
def change_server(self, host, protocol):
|
||||||
pp = self.servers.get(host, constants.net.DEFAULT_PORTS)
|
pp = self.servers.get(host, constants.net.DEFAULT_PORTS)
|
||||||
if protocol and protocol not in protocol_letters:
|
if protocol and protocol not in protocol_letters:
|
||||||
|
|
|
@ -75,6 +75,10 @@ NUM_TARGET_CONNECTED_SERVERS = 10
|
||||||
NUM_STICKY_SERVERS = 4
|
NUM_STICKY_SERVERS = 4
|
||||||
NUM_RECENT_SERVERS = 20
|
NUM_RECENT_SERVERS = 20
|
||||||
|
|
||||||
|
_KNOWN_NETWORK_PROTOCOLS = {'t', 's'}
|
||||||
|
PREFERRED_NETWORK_PROTOCOL = 's'
|
||||||
|
assert PREFERRED_NETWORK_PROTOCOL in _KNOWN_NETWORK_PROTOCOLS
|
||||||
|
|
||||||
|
|
||||||
def parse_servers(result: Sequence[Tuple[str, str, List[str]]]) -> Dict[str, dict]:
|
def parse_servers(result: Sequence[Tuple[str, str, List[str]]]) -> Dict[str, dict]:
|
||||||
""" parse servers list into dict format"""
|
""" parse servers list into dict format"""
|
||||||
|
@ -115,23 +119,27 @@ def filter_noonion(servers):
|
||||||
return {k: v for k, v in servers.items() if not k.endswith('.onion')}
|
return {k: v for k, v in servers.items() if not k.endswith('.onion')}
|
||||||
|
|
||||||
|
|
||||||
def filter_protocol(hostmap, protocol='s') -> Sequence[ServerAddr]:
|
def filter_protocol(hostmap, *, allowed_protocols: Iterable[str] = None) -> Sequence[ServerAddr]:
|
||||||
"""Filters the hostmap for those implementing protocol."""
|
"""Filters the hostmap for those implementing protocol."""
|
||||||
|
if allowed_protocols is None:
|
||||||
|
allowed_protocols = {PREFERRED_NETWORK_PROTOCOL}
|
||||||
eligible = []
|
eligible = []
|
||||||
for host, portmap in hostmap.items():
|
for host, portmap in hostmap.items():
|
||||||
|
for protocol in allowed_protocols:
|
||||||
port = portmap.get(protocol)
|
port = portmap.get(protocol)
|
||||||
if port:
|
if port:
|
||||||
eligible.append(ServerAddr(host, port, protocol=protocol))
|
eligible.append(ServerAddr(host, port, protocol=protocol))
|
||||||
return eligible
|
return eligible
|
||||||
|
|
||||||
|
|
||||||
def pick_random_server(hostmap=None, *, protocol='s',
|
def pick_random_server(hostmap=None, *, allowed_protocols: Iterable[str],
|
||||||
exclude_set: Set[ServerAddr] = None) -> Optional[ServerAddr]:
|
exclude_set: Set[ServerAddr] = None) -> Optional[ServerAddr]:
|
||||||
if hostmap is None:
|
if hostmap is None:
|
||||||
hostmap = constants.net.DEFAULT_SERVERS
|
hostmap = constants.net.DEFAULT_SERVERS
|
||||||
if exclude_set is None:
|
if exclude_set is None:
|
||||||
exclude_set = set()
|
exclude_set = set()
|
||||||
eligible = list(set(filter_protocol(hostmap, protocol)) - exclude_set)
|
servers = set(filter_protocol(hostmap, allowed_protocols=allowed_protocols))
|
||||||
|
eligible = list(servers - exclude_set)
|
||||||
return random.choice(eligible) if eligible else None
|
return random.choice(eligible) if eligible else None
|
||||||
|
|
||||||
|
|
||||||
|
@ -273,6 +281,9 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
|
||||||
self.logger.info(f"blockchains {list(map(lambda b: b.forkpoint, blockchain.blockchains.values()))}")
|
self.logger.info(f"blockchains {list(map(lambda b: b.forkpoint, blockchain.blockchains.values()))}")
|
||||||
self._blockchain_preferred_block = self.config.get('blockchain_preferred_block', None) # type: Optional[Dict]
|
self._blockchain_preferred_block = self.config.get('blockchain_preferred_block', None) # type: Optional[Dict]
|
||||||
self._blockchain = blockchain.get_best_chain()
|
self._blockchain = blockchain.get_best_chain()
|
||||||
|
|
||||||
|
self._allowed_protocols = {PREFERRED_NETWORK_PROTOCOL}
|
||||||
|
|
||||||
# Server for addresses and transactions
|
# Server for addresses and transactions
|
||||||
self.default_server = self.config.get('server', None)
|
self.default_server = self.config.get('server', None)
|
||||||
# Sanitize default server
|
# Sanitize default server
|
||||||
|
@ -283,7 +294,7 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
|
||||||
self.logger.warning('failed to parse server-string; falling back to localhost.')
|
self.logger.warning('failed to parse server-string; falling back to localhost.')
|
||||||
self.default_server = ServerAddr.from_str("localhost:50002:s")
|
self.default_server = ServerAddr.from_str("localhost:50002:s")
|
||||||
else:
|
else:
|
||||||
self.default_server = pick_random_server()
|
self.default_server = pick_random_server(allowed_protocols=self._allowed_protocols)
|
||||||
assert isinstance(self.default_server, ServerAddr), f"invalid type for default_server: {self.default_server!r}"
|
assert isinstance(self.default_server, ServerAddr), f"invalid type for default_server: {self.default_server!r}"
|
||||||
|
|
||||||
self.taskgroup = None
|
self.taskgroup = None
|
||||||
|
@ -549,7 +560,7 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
|
||||||
# we only give priority to recent_servers up to NUM_STICKY_SERVERS.
|
# we only give priority to recent_servers up to NUM_STICKY_SERVERS.
|
||||||
with self.recent_servers_lock:
|
with self.recent_servers_lock:
|
||||||
recent_servers = list(self._recent_servers)
|
recent_servers = list(self._recent_servers)
|
||||||
recent_servers = [s for s in recent_servers if s.protocol == self.protocol]
|
recent_servers = [s for s in recent_servers if s.protocol in self._allowed_protocols]
|
||||||
if len(connected_servers & set(recent_servers)) < NUM_STICKY_SERVERS:
|
if len(connected_servers & set(recent_servers)) < NUM_STICKY_SERVERS:
|
||||||
for server in recent_servers:
|
for server in recent_servers:
|
||||||
if server in connected_servers:
|
if server in connected_servers:
|
||||||
|
@ -559,7 +570,7 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
|
||||||
return server
|
return server
|
||||||
# try all servers we know about, pick one at random
|
# try all servers we know about, pick one at random
|
||||||
hostmap = self.get_servers()
|
hostmap = self.get_servers()
|
||||||
servers = list(set(filter_protocol(hostmap, self.protocol)) - connected_servers)
|
servers = list(set(filter_protocol(hostmap, allowed_protocols=self._allowed_protocols)) - connected_servers)
|
||||||
random.shuffle(servers)
|
random.shuffle(servers)
|
||||||
for server in servers:
|
for server in servers:
|
||||||
if not self._can_retry_addr(server, now=now):
|
if not self._can_retry_addr(server, now=now):
|
||||||
|
@ -574,7 +585,7 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
|
||||||
util.trigger_callback('proxy_set', self.proxy)
|
util.trigger_callback('proxy_set', self.proxy)
|
||||||
|
|
||||||
@log_exceptions
|
@log_exceptions
|
||||||
async def set_parameters(self, net_params: NetworkParameters):
|
async def set_parameters(self, net_params: NetworkParameters): # TODO
|
||||||
proxy = net_params.proxy
|
proxy = net_params.proxy
|
||||||
proxy_str = serialize_proxy(proxy)
|
proxy_str = serialize_proxy(proxy)
|
||||||
host, port, protocol = net_params.host, net_params.port, net_params.protocol
|
host, port, protocol = net_params.host, net_params.port, net_params.protocol
|
||||||
|
@ -598,7 +609,7 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
|
||||||
|
|
||||||
async with self.restart_lock:
|
async with self.restart_lock:
|
||||||
self.auto_connect = net_params.auto_connect
|
self.auto_connect = net_params.auto_connect
|
||||||
if self.proxy != proxy or self.protocol != protocol or self.oneserver != net_params.oneserver:
|
if self.proxy != proxy or self.oneserver != net_params.oneserver:
|
||||||
# Restart the network defaulting to the given server
|
# Restart the network defaulting to the given server
|
||||||
await self._stop()
|
await self._stop()
|
||||||
self.default_server = server
|
self.default_server = server
|
||||||
|
@ -1138,7 +1149,6 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
|
||||||
assert not self._connecting
|
assert not self._connecting
|
||||||
self.logger.info('starting network')
|
self.logger.info('starting network')
|
||||||
self._clear_addr_retry_times()
|
self._clear_addr_retry_times()
|
||||||
self.protocol = self.default_server.protocol
|
|
||||||
self._set_proxy(deserialize_proxy(self.config.get('proxy')))
|
self._set_proxy(deserialize_proxy(self.config.get('proxy')))
|
||||||
self._set_oneserver(self.config.get('oneserver', False))
|
self._set_oneserver(self.config.get('oneserver', False))
|
||||||
await self.taskgroup.spawn(self._run_new_interface(self.default_server))
|
await self.taskgroup.spawn(self._run_new_interface(self.default_server))
|
||||||
|
@ -1282,7 +1292,7 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
|
||||||
session = self.interface.session
|
session = self.interface.session
|
||||||
return parse_servers(await session.send_request('server.peers.subscribe'))
|
return parse_servers(await session.send_request('server.peers.subscribe'))
|
||||||
|
|
||||||
async def send_multiple_requests(self, servers: List[str], method: str, params: Sequence):
|
async def send_multiple_requests(self, servers: Sequence[ServerAddr], method: str, params: Sequence):
|
||||||
responses = dict()
|
responses = dict()
|
||||||
async def get_response(server: ServerAddr):
|
async def get_response(server: ServerAddr):
|
||||||
interface = Interface(network=self, server=server, proxy=self.proxy)
|
interface = Interface(network=self, server=server, proxy=self.proxy)
|
||||||
|
@ -1299,6 +1309,5 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
|
||||||
responses[interface.server] = res
|
responses[interface.server] = res
|
||||||
async with TaskGroup() as group:
|
async with TaskGroup() as group:
|
||||||
for server in servers:
|
for server in servers:
|
||||||
server = ServerAddr.from_str(server)
|
|
||||||
await group.spawn(get_response(server))
|
await group.spawn(get_response(server))
|
||||||
return responses
|
return responses
|
||||||
|
|
|
@ -17,7 +17,7 @@ network.start()
|
||||||
async def f():
|
async def f():
|
||||||
try:
|
try:
|
||||||
peers = await network.get_peers()
|
peers = await network.get_peers()
|
||||||
peers = filter_protocol(peers, 's')
|
peers = filter_protocol(peers)
|
||||||
results = await network.send_multiple_requests(peers, 'blockchain.headers.subscribe', [])
|
results = await network.send_multiple_requests(peers, 'blockchain.headers.subscribe', [])
|
||||||
for server, header in sorted(results.items(), key=lambda x: x[1].get('height')):
|
for server, header in sorted(results.items(), key=lambda x: x[1].get('height')):
|
||||||
height = header.get('height')
|
height = header.get('height')
|
||||||
|
|
|
@ -23,7 +23,7 @@ network.start()
|
||||||
async def f():
|
async def f():
|
||||||
try:
|
try:
|
||||||
peers = await network.get_peers()
|
peers = await network.get_peers()
|
||||||
peers = filter_protocol(peers, 's')
|
peers = filter_protocol(peers)
|
||||||
results = await network.send_multiple_requests(peers, 'blockchain.transaction.get', [txid])
|
results = await network.send_multiple_requests(peers, 'blockchain.transaction.get', [txid])
|
||||||
r1, r2 = [], []
|
r1, r2 = [], []
|
||||||
for k, v in results.items():
|
for k, v in results.items():
|
||||||
|
|
Loading…
Add table
Reference in a new issue