From 0ad504bdf0afe24167f27e0922f5e53b77d205fd Mon Sep 17 00:00:00 2001 From: SomberNight Date: Thu, 6 Sep 2018 16:45:43 +0200 Subject: [PATCH] interface: catch many common exceptions explicitly --- electrum/interface.py | 29 +++++++++++++++++++++++++---- electrum/network.py | 2 +- electrum/util.py | 9 +++++++++ 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/electrum/interface.py b/electrum/interface.py index 19581bf94..d0e63f2f9 100644 --- a/electrum/interface.py +++ b/electrum/interface.py @@ -36,7 +36,7 @@ from aiorpcx import ClientSession, Notification import requests -from .util import PrintError, aiosafe, bfh +from .util import PrintError, aiosafe, bfh, AIOSafeSilentException ca_path = requests.certs.where() @@ -68,6 +68,10 @@ class NotificationSession(ClientSession): assert False, request.method + +class GracefulDisconnect(AIOSafeSilentException): pass + + class Interface(PrintError): def __init__(self, network, server, config_path, proxy): @@ -134,7 +138,12 @@ class Interface(PrintError): os.unlink(self.cert_path) exists = False if not exists: - ca_signed = await self.is_server_ca_signed(ca_sslc) + try: + ca_signed = await self.is_server_ca_signed(ca_sslc) + except (ConnectionRefusedError, socket.gaierror) as e: + self.print_error('disconnecting due to: {}'.format(e)) + self.exception = e + return if ca_signed: with open(self.cert_path, 'w') as f: # empty file means this is CA signed, not self-signed @@ -147,7 +156,13 @@ class Interface(PrintError): else: sslc = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=self.cert_path) sslc.check_hostname = 0 - await self.open_session(sslc, exit_early=False) + try: + await self.open_session(sslc, exit_early=False) + except (asyncio.CancelledError, ConnectionRefusedError, socket.gaierror, ssl.SSLError, TimeoutError) as e: + self.print_error('disconnecting due to: {}'.format(e)) + self.exception = e + return + # should never get here (can only exit via exception) assert False def mark_ready(self): @@ -204,7 +219,10 @@ class Interface(PrintError): header_queue = asyncio.Queue() self.session = NotificationSession(None, header_queue, self.host, self.port, ssl=sslc, proxy=self.proxy) async with self.session as session: - ver = await session.send_request('server.version', [ELECTRUM_VERSION, PROTOCOL_VERSION]) + try: + ver = await session.send_request('server.version', [ELECTRUM_VERSION, PROTOCOL_VERSION]) + except aiorpcx.jsonrpc.RPCError as e: + raise GracefulDisconnect(e) # probably 'unsupported protocol version' if exit_early: return self.print_error(ver, self.host) @@ -228,6 +246,9 @@ class Interface(PrintError): @aiosafe async def run_fetch_blocks(self, sub_reply, replies): + if self.tip < self.network.max_checkpoint(): + raise GracefulDisconnect('server tip below max checkpoint') + async with self.network.bhi_lock: height = self.blockchain.height()+1 await replies.put(blockchain.deserialize_header(bfh(sub_reply['hex']), sub_reply['height'])) diff --git a/electrum/network.py b/electrum/network.py index f0aa4a61f..631178aed 100644 --- a/electrum/network.py +++ b/electrum/network.py @@ -817,7 +817,7 @@ class Network(PrintError): try: raise i.exception except BaseException as e: - self.print_error(i.server, "errored because", str(e), str(type(e))) + self.print_error(i.server, "errored because:", str(e), str(type(e))) remove.append(k) changed = False for k in remove: diff --git a/electrum/util.py b/electrum/util.py index 4a3629cd4..571c57f63 100644 --- a/electrum/util.py +++ b/electrum/util.py @@ -34,6 +34,7 @@ import hmac import stat import inspect from locale import localeconv +import asyncio from .i18n import _ @@ -925,6 +926,10 @@ def make_dir(path, allow_symlink=True): os.mkdir(path) os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) + +class AIOSafeSilentException(Exception): pass + + def aiosafe(f): # save exception in object. # f must be a method of a PrintError instance. @@ -933,6 +938,10 @@ def aiosafe(f): self = args[0] try: return await f(*args, **kwargs) + except AIOSafeSilentException as e: + self.exception = e + except asyncio.CancelledError as e: + self.exception = e except BaseException as e: self.print_error("Exception in", f.__name__, ":", e.__class__.__name__, str(e)) traceback.print_exc(file=sys.stderr)