diff --git a/contrib/requirements/requirements.txt b/contrib/requirements/requirements.txt index 2a9ff93c0..031fb19eb 100644 --- a/contrib/requirements/requirements.txt +++ b/contrib/requirements/requirements.txt @@ -9,6 +9,4 @@ aiohttp>=3.3.0,<4.0.0 aiohttp_socks>=0.3 certifi bitstring -jsonrpcserver -jsonrpcclient attrs diff --git a/electrum/daemon.py b/electrum/daemon.py index 62c314a09..7d26c8873 100644 --- a/electrum/daemon.py +++ b/electrum/daemon.py @@ -37,11 +37,8 @@ from concurrent import futures import aiohttp from aiohttp import web, client_exceptions -import jsonrpcclient -import jsonrpcserver -from jsonrpcserver import response -from jsonrpcclient.clients.aiohttp_client import AiohttpClient from aiorpcx import TaskGroup +import json from . import util from .network import Network @@ -107,10 +104,8 @@ def request(config: SimpleConfig, endpoint, args=(), timeout=60): loop = asyncio.get_event_loop() async def request_coroutine(): async with aiohttp.ClientSession(auth=auth) as session: - server = AiohttpClient(session, server_url, timeout=timeout) - f = getattr(server, endpoint) - response = await f(*args) - return response.data.result + c = util.myAiohttpClient(session, server_url) + return await c.request(endpoint, *args) try: fut = asyncio.run_coroutine_threadsafe(request_coroutine(), loop) return fut.result(timeout=timeout) @@ -184,16 +179,22 @@ class AuthenticatedServer(Logger): text='Unauthorized', status=401) except AuthenticationCredentialsInvalid: return web.Response(text='Forbidden', status=403) - request = await request.text() - response = await jsonrpcserver.async_dispatch(request, methods=self.methods) - if isinstance(response, jsonrpcserver.response.ExceptionResponse): - self.logger.error(f"error handling request: {request}", exc_info=response.exc) - # this exposes the error message to the client - response.message = str(response.exc) - if response.wanted: - return web.json_response(response.deserialized(), status=response.http_status) - else: - return web.Response() + try: + request = await request.text() + request = json.loads(request) + method = request['method'] + _id = request['id'] + params = request.get('params', []) + f = getattr(self, method) + assert f in self.methods + except: + return web.Response(text='Invalid Request', status=500) + response = {'id':_id} + try: + response['result'] = await f(*params) + except BaseException as e: + response['error'] = str(e) + return web.json_response(response) class CommandsServer(AuthenticatedServer): @@ -208,7 +209,7 @@ class CommandsServer(AuthenticatedServer): self.port = self.config.get('rpcport', 0) self.app = web.Application() self.app.router.add_post("/", self.handle) - self.methods = jsonrpcserver.methods.Methods() + self.methods = set() self.methods.add(self.ping) self.methods.add(self.gui) self.cmd_runner = Commands(config=self.config, network=self.daemon.network, daemon=self.daemon) @@ -276,7 +277,7 @@ class WatchTowerServer(AuthenticatedServer): self.lnwatcher = network.local_watchtower self.app = web.Application() self.app.router.add_post("/", self.handle) - self.methods = jsonrpcserver.methods.Methods() + self.methods = set() self.methods.add(self.get_ctn) self.methods.add(self.add_sweep_tx) diff --git a/electrum/lnworker.py b/electrum/lnworker.py index 7a8aa3810..d7e718914 100644 --- a/electrum/lnworker.py +++ b/electrum/lnworker.py @@ -10,6 +10,7 @@ import time from typing import Optional, Sequence, Tuple, List, Dict, TYPE_CHECKING, NamedTuple, Union, Mapping import threading import socket +import aiohttp import json from datetime import datetime, timezone from functools import partial @@ -25,7 +26,7 @@ from . import constants, util from . import keystore from .util import profiler from .invoices import PR_TYPE_LN, PR_UNPAID, PR_EXPIRED, PR_PAID, PR_INFLIGHT, PR_FAILED, PR_ROUTING, LNInvoice, LN_EXPIRY_NEVER -from .util import NetworkRetryManager +from .util import NetworkRetryManager, myAiohttpClient from .lnutil import LN_MAX_FUNDING_SAT from .keystore import BIP32_KeyStore from .bitcoin import COIN @@ -525,12 +526,6 @@ class LNWallet(LNWorker): @ignore_exceptions @log_exceptions async def sync_with_remote_watchtower(self): - import aiohttp - from jsonrpcclient.clients.aiohttp_client import AiohttpClient - class myAiohttpClient(AiohttpClient): - async def request(self, *args, **kwargs): - r = await super().request(*args, **kwargs) - return r.data.result while True: await asyncio.sleep(5) watchtower_url = self.config.get('watchtower_url') @@ -539,6 +534,8 @@ class LNWallet(LNWorker): try: async with make_aiohttp_session(proxy=self.network.proxy) as session: watchtower = myAiohttpClient(session, watchtower_url) + watchtower.add_method('get_ctn') + watchtower.add_method('add_sweep_tx') for chan in self.channels.values(): await self.sync_channel_with_watchtower(chan, watchtower) except aiohttp.client_exceptions.ClientConnectorError: diff --git a/electrum/util.py b/electrum/util.py index 36a8af1d1..b8401543b 100644 --- a/electrum/util.py +++ b/electrum/util.py @@ -1368,3 +1368,30 @@ class MySocksProxy(aiorpcx.SOCKSProxy): else: raise NotImplementedError # http proxy not available with aiorpcx return ret + + +class myAiohttpClient: + + def __init__(self, session, url): + self.session = session + self.url = url + + async def request(self, endpoint, *args): + data = '{"jsonrpc": "2.0", "id":"0", "method":"%s", "params": %s }' %(endpoint, json.dumps(args)) + async with self.session.post(self.url, data=data) as resp: + if resp.status == 200: + r = await resp.json() + result = r.get('result') + error = r.get('error') + if error: + return 'Error: ' + str(error) + else: + return result + else: + text = await resp.text() + return 'Error: ' + str(text) + + def add_method(self, endpoint): + async def coro(*args): + return await self.request(endpoint, *args) + setattr(self, endpoint, coro)