electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 68dad21fb467ddb172884048a50f87978e11d3f8
parent dfdc1e1d25a87c3283f57e8bd98028352324ca8f
Author: SomberNight <somber.night@protonmail.com>
Date:   Sun,  1 Dec 2019 23:24:43 +0100

network: make best_effort_reliable smarter and a bit more lenient

related: #5815

Diffstat:
Melectrum/interface.py | 4++--
Melectrum/network.py | 23++++++++++++-----------
2 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/electrum/interface.py b/electrum/interface.py @@ -374,7 +374,7 @@ class Interface(Logger): self.logger.info(f'disconnecting due to: {repr(e)}') return - def mark_ready(self): + def _mark_ready(self) -> None: if self.ready.cancelled(): raise GracefulDisconnect('conn establishment was too slow; *ready* future was cancelled') if self.ready.done(): @@ -512,7 +512,7 @@ class Interface(Logger): self.tip = height if self.tip < constants.net.max_checkpoint(): raise GracefulDisconnect('server tip below max checkpoint') - self.mark_ready() + self._mark_ready() await self._process_header_at_tip() self.network.trigger_callback('network_updated') await self.network.switch_unwanted_fork_interface() diff --git a/electrum/network.py b/electrum/network.py @@ -290,6 +290,7 @@ class Network(Logger): self.nodes_retry_time = time.time() # the main server we are currently communicating with self.interface = None # type: Interface + self.default_server_changed_event = asyncio.Event() # set of servers we have an ongoing connection with self.interfaces = {} # type: Dict[str, Interface] self.auto_connect = self.config.get('auto_connect', True) @@ -730,10 +731,13 @@ class Network(Logger): i = self.interfaces[server] if old_interface != i: self.logger.info(f"switching to {server}") + assert i.ready.done(), "interface we are switching to is not ready yet" blockchain_updated = i.blockchain != self.blockchain() self.interface = i await i.group.spawn(self._request_server_info(i)) self.trigger_callback('default_server_changed') + self.default_server_changed_event.set() + self.default_server_changed_event.clear() self._set_status('connected') self.trigger_callback('network_updated') if blockchain_updated: self.trigger_callback('blockchain_updated') @@ -840,30 +844,27 @@ class Network(Logger): b.update_size() def best_effort_reliable(func): - async def make_reliable_wrapper(self, *args, **kwargs): + async def make_reliable_wrapper(self: 'Network', *args, **kwargs): for i in range(10): iface = self.interface # retry until there is a main interface if not iface: - await asyncio.sleep(0.1) - continue # try again - # wait for it to be usable - iface_ready = iface.ready - iface_disconnected = iface.got_disconnected - await asyncio.wait([iface_ready, iface_disconnected], return_when=asyncio.FIRST_COMPLETED) - if not iface_ready.done() or iface_ready.cancelled(): - await asyncio.sleep(0.1) + try: + await asyncio.wait_for(self.default_server_changed_event.wait(), 1) + except asyncio.TimeoutError: + pass continue # try again + assert iface.ready.done(), "interface not ready yet" # try actual request success_fut = asyncio.ensure_future(func(self, *args, **kwargs)) - await asyncio.wait([success_fut, iface_disconnected], return_when=asyncio.FIRST_COMPLETED) + await asyncio.wait([success_fut, iface.got_disconnected], return_when=asyncio.FIRST_COMPLETED) if success_fut.done() and not success_fut.cancelled(): if success_fut.exception(): try: raise success_fut.exception() except RequestTimedOut: await iface.close() - await iface_disconnected + await iface.got_disconnected continue # try again return success_fut.result() # otherwise; try again