diff --git a/block.py b/block.py index 79d0aee..6156189 100644 --- a/block.py +++ b/block.py @@ -10,6 +10,7 @@ import asyncio import view from util import isoformatseconds +from rpc import RPCError class BlockStore(object): diff --git a/main.py b/main.py index 95faa1d..bd4009d 100644 --- a/main.py +++ b/main.py @@ -54,8 +54,13 @@ async def poll_client(client, method, callback, sleeptime, params=None): await asyncio.sleep(0.1) while True: - j = await client.request(method, params=params) - await callback(method, j) + try: + d = await client.request(method, params=params) + except rpc.RPCError: + await asyncio.sleep(sleeptime) + continue + + await callback(method, d) await asyncio.sleep(sleeptime) @@ -89,17 +94,28 @@ def initialize(): return client, args.nosplash -def check_disablewallet(client): +def wallet_enabled(client): """ Check if the wallet is enabled. """ + async def check_getwalletinfo(client): + try: + await client.request("getwalletinfo") + except rpc.RPCError: + return False + + try: + x.result()[0]["result"]["walletname"] + except (KeyError, TypeError): + return False + + return True + # Ugly, a synchronous RPC request mechanism would be nice here. - x = asyncio.gather(client.request("getwalletinfo")) + check = asyncio.gather(check_getwalletinfo(client)) loop = asyncio.get_event_loop() - loop.run_until_complete(x) + loop.run_until_complete(check) - try: - x.result()[0]["result"]["walletname"] - except (KeyError, TypeError): + if check.result(): return True return False @@ -210,7 +226,7 @@ def create_tasks(client, window, nosplash): splashview.draw(nosplash), ] - if not check_disablewallet(client): + if wallet_enabled(client): tasks.append( poll_client(client, "getwalletinfo", headerview.on_walletinfo, 1.0) ) diff --git a/rpc.py b/rpc.py index 31200b2..7609f67 100644 --- a/rpc.py +++ b/rpc.py @@ -81,6 +81,11 @@ def get_auth_from_datadir(datadir): return craft_auth_from_credentials(rpcuser, rpcpassword) +class RPCError(BaseException): + # TODO: include the error code, etc. + pass + + class BitcoinRPCClient(object): def __init__(self, url, auth): self._url = url @@ -109,8 +114,32 @@ class BitcoinRPCClient(object): async with session.post(self._url, headers=self._headers, data=req) as response: return await response.text() + @staticmethod + async def _json_loads(j): + return json.loads(j) + async def request(self, method, params=None, ident=None, callback=None): async with aiohttp.ClientSession() as session: req = await self._craft_request(method, params, ident) - html = await self._fetch(session, req) - return json.loads(html) + j = await self._fetch(session, req) + d = await self._json_loads(j) + + try: + error = d["error"] + except KeyError: + raise RPCError("RPC response seems malformed (no error field)") + + if error is not None: + # TODO: pass the error up the stack; tweak RPCError + raise RPCError("RPC response returned error {}".format(error)) + + try: + result = d["result"] + except KeyError: + raise RPCError("RPC response seems malformed (no result field)") + + if result is None: + # Is there a case in which a query can return None? + raise RPCError("RPC response returned a null result") + + return d