From e1f4865844ce1bbdbd91a76b8ba245ae04b6300e Mon Sep 17 00:00:00 2001 From: Janus Date: Fri, 7 Dec 2018 19:19:40 +0100 Subject: [PATCH] digitalbitbox, trustedcoin: proxied http client use common cross-thread HTTP method, which is put in network.py, since that is where the proxy is. TrustedCoin tested successfully, but DigitalBitbox can't be tested completely due to #4903 before this commit, digitalbitbox would not use any proxying --- electrum/network.py | 36 ++++++++++- .../plugins/digitalbitbox/digitalbitbox.py | 60 ++++++++++--------- electrum/plugins/trustedcoin/trustedcoin.py | 35 +++++------ 3 files changed, 80 insertions(+), 51 deletions(-) diff --git a/electrum/network.py b/electrum/network.py index 9fe78a5e8..b55834e88 100644 --- a/electrum/network.py +++ b/electrum/network.py @@ -38,9 +38,12 @@ import traceback import dns import dns.resolver from aiorpcx import TaskGroup +from aiohttp import ClientResponse from . import util -from .util import PrintError, print_error, log_exceptions, ignore_exceptions, bfh, SilentTaskGroup +from .util import (PrintError, print_error, log_exceptions, ignore_exceptions, + bfh, SilentTaskGroup, make_aiohttp_session) + from .bitcoin import COIN from . import constants from . import blockchain @@ -903,3 +906,34 @@ class Network(PrintError): await self.interface.group.spawn(self._request_fee_estimates, self.interface) await asyncio.sleep(0.1) + + + async def _send_http_on_proxy(self, method: str, url: str, params: str = None, body: bytes = None, json: dict = None, headers=None, on_finish=None): + async def default_on_finish(resp: ClientResponse): + resp.raise_for_status() + return await resp.text() + if headers is None: + headers = {} + if on_finish is None: + on_finish = default_on_finish + async with make_aiohttp_session(self.proxy) as session: + if method == 'get': + async with session.get(url, params=params, headers=headers) as resp: + return await on_finish(resp) + elif method == 'post': + assert body is not None or json is not None, 'body or json must be supplied if method is post' + if body is not None: + async with session.post(url, data=body, headers=headers) as resp: + return await on_finish(resp) + elif json is not None: + async with session.post(url, json=json, headers=headers) as resp: + return await on_finish(resp) + else: + assert False + + @staticmethod + def send_http_on_proxy(method, url, **kwargs): + network = Network.get_instance() + assert network._loop_thread is not threading.currentThread() + coro = asyncio.run_coroutine_threadsafe(network._send_http_on_proxy(method, url, **kwargs), network.asyncio_loop) + return coro.result(5) diff --git a/electrum/plugins/digitalbitbox/digitalbitbox.py b/electrum/plugins/digitalbitbox/digitalbitbox.py index df931dec6..32445e707 100644 --- a/electrum/plugins/digitalbitbox/digitalbitbox.py +++ b/electrum/plugins/digitalbitbox/digitalbitbox.py @@ -3,35 +3,36 @@ # digitalbitbox.com # +import base64 +import binascii +import hashlib +import hmac +import json +import math +import os +import re +import struct +import sys +import time + +from electrum.crypto import sha256d, EncodeAES_base64, EncodeAES_bytes, DecodeAES_bytes, hmac_oneshot +from electrum.bitcoin import (TYPE_ADDRESS, push_script, var_int, public_key_to_p2pkh, + is_address) +from electrum.bip32 import serialize_xpub, deserialize_xpub +from electrum import ecc +from electrum.ecc import msg_magic +from electrum.wallet import Standard_Wallet +from electrum import constants +from electrum.transaction import Transaction +from electrum.i18n import _ +from electrum.keystore import Hardware_KeyStore +from ..hw_wallet import HW_PluginBase +from electrum.util import print_error, to_string, UserCancelled, UserFacingException +from electrum.base_wizard import ScriptTypeNotSupported, HWD_SETUP_NEW_WALLET +from electrum.network import Network + try: - from electrum.crypto import sha256d, EncodeAES_base64, EncodeAES_bytes, DecodeAES_bytes, hmac_oneshot - from electrum.bitcoin import (TYPE_ADDRESS, push_script, var_int, public_key_to_p2pkh, - is_address) - from electrum.bip32 import serialize_xpub, deserialize_xpub - from electrum import ecc - from electrum.ecc import msg_magic - from electrum.wallet import Standard_Wallet - from electrum import constants - from electrum.transaction import Transaction - from electrum.i18n import _ - from electrum.keystore import Hardware_KeyStore - from ..hw_wallet import HW_PluginBase - from electrum.util import print_error, to_string, UserCancelled, UserFacingException - from electrum.base_wizard import ScriptTypeNotSupported, HWD_SETUP_NEW_WALLET - - import time import hid - import json - import math - import binascii - import struct - import hashlib - import requests - import base64 - import os - import sys - import re - import hmac DIGIBOX = True except ImportError as e: DIGIBOX = False @@ -744,9 +745,10 @@ class DigitalBitboxPlugin(HW_PluginBase): EncodeAES_base64(key_s, json.dumps(payload).encode('ascii')).decode('ascii'), ) try: - requests.post(url, args) + text = Network.send_http_on_proxy('post', url, body=args.encode('ascii'), headers={'content-type': 'application/x-www-form-urlencoded'}) + print_error('digitalbitbox reply from server', text) except Exception as e: - self.handler.show_error(str(e)) + self.handler.show_error(repr(e)) # repr because str(Exception()) == '' def get_xpub(self, device_id, derivation, xtype, wizard): diff --git a/electrum/plugins/trustedcoin/trustedcoin.py b/electrum/plugins/trustedcoin/trustedcoin.py index 84c4313a6..03a7ca832 100644 --- a/electrum/plugins/trustedcoin/trustedcoin.py +++ b/electrum/plugins/trustedcoin/trustedcoin.py @@ -31,6 +31,7 @@ import hashlib from urllib.parse import urljoin from urllib.parse import quote +from aiohttp import ClientResponse from electrum import ecc, constants, keystore, version, bip32 from electrum.bitcoin import TYPE_ADDRESS, is_new_seed, public_key_to_p2pkh @@ -42,7 +43,7 @@ from electrum.mnemonic import Mnemonic from electrum.wallet import Multisig_Wallet, Deterministic_Wallet from electrum.i18n import _ from electrum.plugin import BasePlugin, hook -from electrum.util import NotEnoughFunds, make_aiohttp_session +from electrum.util import NotEnoughFunds from electrum.storage import STO_EV_USER_PW from electrum.network import Network @@ -108,14 +109,7 @@ class TrustedCoinCosignerClient(object): self.debug = False self.user_agent = user_agent - def send_request(self, method, relative_url, data=None): - network = Network.get_instance() - if network: - return asyncio.run_coroutine_threadsafe(self._send_request(method, relative_url, data), network.asyncio_loop).result() - else: - raise ErrorConnectingServer('You are offline.') - - async def handle_response(self, resp): + async def handle_response(self, resp: ClientResponse): if resp.status != 200: try: r = await resp.json() @@ -128,7 +122,10 @@ class TrustedCoinCosignerClient(object): except: return await resp.text() - async def _send_request(self, method, relative_url, data): + def send_request(self, method, relative_url, data=None): + network = Network.get_instance() + if not network: + raise ErrorConnectingServer('You are offline.') url = urljoin(self.base_url, relative_url) if self.debug: print('%s %s %s' % (method, url, data)) @@ -136,16 +133,12 @@ class TrustedCoinCosignerClient(object): if self.user_agent: headers['user-agent'] = self.user_agent try: - proxy = Network.get_instance().proxy - async with make_aiohttp_session(proxy) as session: - if method == 'get': - async with session.get(url, params=data, headers=headers) as resp: - return await self.handle_response(resp) - elif method == 'post': - async with session.post(url, json=data, headers=headers) as resp: - return await self.handle_response(resp) - else: - assert False + if method == 'get': + return Network.send_http_on_proxy(method, url, params=data, headers=headers, on_finish=self.handle_response) + elif method == 'post': + return Network.send_http_on_proxy(method, url, json=data, headers=headers, on_finish=self.handle_response) + else: + assert False except TrustedCoinException: raise except Exception as e: @@ -434,7 +427,7 @@ class TrustedCoinPlugin(BasePlugin): try: billing_info = server.get(wallet.get_user_id()[1]) except ErrorConnectingServer as e: - self.print_error('cannot connect to TrustedCoin server: {}'.format(e)) + self.print_error('cannot connect to TrustedCoin server: {}'.format(repr(e))) return billing_index = billing_info['billing_index'] billing_address = make_billing_address(wallet, billing_index)