diff --git a/server/daemon.py b/server/daemon.py index a0f6e32..c5dd565 100644 --- a/server/daemon.py +++ b/server/daemon.py @@ -20,6 +20,7 @@ import aiohttp from lib.util import LoggedClass, int_to_varint, hex_to_bytes from lib.hash import hex_str_to_hash +from lib.jsonrpc import JSONRPC class DaemonError(Exception): @@ -30,6 +31,7 @@ class Daemon(LoggedClass): '''Handles connections to a daemon at the given URL.''' WARMING_UP = -28 + RPC_MISC_ERROR = -1 class DaemonWarmingUpError(Exception): '''Raised when the daemon returns an error in its results.''' @@ -54,6 +56,7 @@ class Daemon(LoggedClass): else: self.ClientHttpProcessingError = asyncio.TimeoutError self.ClientPayloadError = aiohttp.ClientPayloadError + self._available_rpcs = {} # caches results for _is_rpc_available() def next_req_id(self): '''Retrns the next request ID.''' @@ -97,7 +100,7 @@ class Daemon(LoggedClass): # If bitcoind can't find a tx, for some reason # it returns 500 but fills out the JSON. # Should still return 200 IMO. - if resp.status in (200, 500): + if resp.status in (200, 404, 500): return await resp.json() return (resp.status, resp.reason) @@ -194,6 +197,34 @@ class Daemon(LoggedClass): return await self._send(payload, processor) return [] + async def _is_rpc_available(self, method): + '''Return whether given RPC method is available in the daemon. + + Results are cached and the daemon will generally not be queried with + the same method more than once.''' + available = self._available_rpcs.get(method, None) + if available is None: + try: + await self._send_single(method) + available = True + except DaemonError as e: + err = e.args[0] + error_code = err.get("code") + if error_code == JSONRPC.METHOD_NOT_FOUND: + available = False + elif error_code == self.RPC_MISC_ERROR: + # method found but exception was thrown in command handling + # probably because we did not provide arguments + available = True + else: + self.logger.warning('unexpected error (code {:d}: {}) when ' + 'testing RPC availability of method {}' + .format(error_code, err.get("message"), + method)) + available = False + self._available_rpcs[method] = available + return available + async def block_hex_hashes(self, first, count): '''Return the hex hashes of count block starting at height first.''' params_iterable = ((h, ) for h in range(first, first + count)) @@ -216,6 +247,9 @@ class Daemon(LoggedClass): async def estimatefee(self, params): '''Return the fee estimate for the given parameters.''' + if await self._is_rpc_available('estimatesmartfee'): + estimate = await self._send_single('estimatesmartfee', params) + return estimate['feerate'] return await self._send_single('estimatefee', params) async def getnetworkinfo(self):