tnetwork: make best_effort_reliable smarter and a bit more lenient - electrum - Electrum Bitcoin wallet
 (HTM) git clone https://git.parazyd.org/electrum
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
       ---
 (DIR) commit 68dad21fb467ddb172884048a50f87978e11d3f8
 (DIR) parent dfdc1e1d25a87c3283f57e8bd98028352324ca8f
 (HTM) 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:
         M electrum/interface.py               |       4 ++--
         M electrum/network.py                 |      23 ++++++++++++-----------
       
       2 files changed, 14 insertions(+), 13 deletions(-)
       ---
 (DIR) diff --git a/electrum/interface.py b/electrum/interface.py
       t@@ -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():
       t@@ -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()
 (DIR) diff --git a/electrum/network.py b/electrum/network.py
       t@@ -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)
       t@@ -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')
       t@@ -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