plugins: add proxy aware XMLRPCProxyTransport for xmlrpc.client calls in cosigner plugin
This commit is contained in:
@@ -22,9 +22,9 @@
|
|||||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
# SOFTWARE.
|
# SOFTWARE.
|
||||||
|
import asyncio
|
||||||
import time
|
import time
|
||||||
from xmlrpc.client import ServerProxy
|
from xmlrpc.client import ServerProxy, Transport
|
||||||
from typing import TYPE_CHECKING, Union, List, Tuple, Dict
|
from typing import TYPE_CHECKING, Union, List, Tuple, Dict
|
||||||
import ssl
|
import ssl
|
||||||
|
|
||||||
@@ -33,14 +33,14 @@ from PyQt5.QtWidgets import QPushButton
|
|||||||
import certifi
|
import certifi
|
||||||
|
|
||||||
from electrum import util, keystore, ecc, crypto
|
from electrum import util, keystore, ecc, crypto
|
||||||
from electrum import transaction
|
|
||||||
from electrum.transaction import Transaction, PartialTransaction, tx_from_any, SerializationError
|
from electrum.transaction import Transaction, PartialTransaction, tx_from_any, SerializationError
|
||||||
from electrum.bip32 import BIP32Node
|
from electrum.bip32 import BIP32Node
|
||||||
from electrum.plugin import BasePlugin, hook
|
from electrum.plugin import BasePlugin, hook
|
||||||
from electrum.i18n import _
|
from electrum.i18n import _
|
||||||
from electrum.wallet import Multisig_Wallet, Abstract_Wallet
|
from electrum.wallet import Multisig_Wallet, Abstract_Wallet
|
||||||
from electrum.util import bfh
|
from electrum.util import bfh, make_aiohttp_session
|
||||||
from electrum.logging import Logger
|
from electrum.logging import Logger
|
||||||
|
from electrum.network import Network
|
||||||
|
|
||||||
from electrum.gui.qt.transaction_dialog import show_transaction, TxDialog
|
from electrum.gui.qt.transaction_dialog import show_transaction, TxDialog
|
||||||
from electrum.gui.qt.util import WaitingDialog
|
from electrum.gui.qt.util import WaitingDialog
|
||||||
@@ -52,8 +52,26 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
ca_path = certifi.where()
|
ca_path = certifi.where()
|
||||||
ssl_context = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH, cafile=ca_path)
|
ssl_context = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH, cafile=ca_path)
|
||||||
server = ServerProxy('https://cosigner.electrum.org/', allow_none=True, context=ssl_context)
|
|
||||||
# FIXME this is not using the network proxy.
|
|
||||||
|
class XMLRPCProxyTransport(Transport):
|
||||||
|
def request(self, host, handler, request_body, verbose=False):
|
||||||
|
network = Network.get_instance()
|
||||||
|
if network is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
async def do_request(_host, _request_body):
|
||||||
|
async with make_aiohttp_session(network.proxy) as session:
|
||||||
|
async with session.post(f'https://{_host}', data=_request_body) as response:
|
||||||
|
response.raise_for_status()
|
||||||
|
p, u = self.getparser()
|
||||||
|
data = await response.read()
|
||||||
|
p.feed(data)
|
||||||
|
p.close()
|
||||||
|
return u.close()
|
||||||
|
|
||||||
|
fut = asyncio.run_coroutine_threadsafe(do_request(host, request_body), network.asyncio_loop)
|
||||||
|
return fut.result()
|
||||||
|
|
||||||
|
|
||||||
class Listener(util.DaemonThread):
|
class Listener(util.DaemonThread):
|
||||||
@@ -69,7 +87,7 @@ class Listener(util.DaemonThread):
|
|||||||
self.keyhashes = keyhashes
|
self.keyhashes = keyhashes
|
||||||
|
|
||||||
def clear(self, keyhash):
|
def clear(self, keyhash):
|
||||||
server.delete(keyhash)
|
self.cw.cosigner_service.delete(keyhash)
|
||||||
self.received.remove(keyhash)
|
self.received.remove(keyhash)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@@ -81,7 +99,7 @@ class Listener(util.DaemonThread):
|
|||||||
if keyhash in self.received:
|
if keyhash in self.received:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
message = server.get(keyhash)
|
message = self.cw.cosigner_service.get(keyhash)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.info(f"cannot contact cosigner pool. exc: {e!r}")
|
self.logger.info(f"cannot contact cosigner pool. exc: {e!r}")
|
||||||
time.sleep(30)
|
time.sleep(30)
|
||||||
@@ -89,10 +107,9 @@ class Listener(util.DaemonThread):
|
|||||||
if message:
|
if message:
|
||||||
self.received.add(keyhash)
|
self.received.add(keyhash)
|
||||||
self.logger.info(f"received message for {keyhash}")
|
self.logger.info(f"received message for {keyhash}")
|
||||||
self.cw.obj.cosigner_receive_signal.emit(
|
self.cw.obj.cosigner_receive_signal.emit(keyhash, message)
|
||||||
keyhash, message)
|
|
||||||
# poll every 30 seconds
|
time.sleep(30) # poll every 30 seconds
|
||||||
time.sleep(30)
|
|
||||||
|
|
||||||
|
|
||||||
class QReceiveSignalObject(QObject):
|
class QReceiveSignalObject(QObject):
|
||||||
@@ -106,6 +123,9 @@ class Plugin(BasePlugin):
|
|||||||
self._init_qt_received = False
|
self._init_qt_received = False
|
||||||
self.cosigner_wallets = {} # type: Dict[Abstract_Wallet, CosignerWallet]
|
self.cosigner_wallets = {} # type: Dict[Abstract_Wallet, CosignerWallet]
|
||||||
|
|
||||||
|
transport = XMLRPCProxyTransport()
|
||||||
|
self.cosigner_service = ServerProxy('https://cosigner.electrum.org/', transport, allow_none=True, context=ssl_context)
|
||||||
|
|
||||||
@hook
|
@hook
|
||||||
def init_qt(self, gui: 'ElectrumGui'):
|
def init_qt(self, gui: 'ElectrumGui'):
|
||||||
if self._init_qt_received: # only need/want the first signal
|
if self._init_qt_received: # only need/want the first signal
|
||||||
@@ -118,7 +138,7 @@ class Plugin(BasePlugin):
|
|||||||
def load_wallet(self, wallet: 'Abstract_Wallet', window: 'ElectrumWindow'):
|
def load_wallet(self, wallet: 'Abstract_Wallet', window: 'ElectrumWindow'):
|
||||||
if type(wallet) != Multisig_Wallet:
|
if type(wallet) != Multisig_Wallet:
|
||||||
return
|
return
|
||||||
self.cosigner_wallets[wallet] = CosignerWallet(wallet, window)
|
self.cosigner_wallets[wallet] = CosignerWallet(wallet, self.cosigner_service, window)
|
||||||
|
|
||||||
@hook
|
@hook
|
||||||
def on_close_window(self, window):
|
def on_close_window(self, window):
|
||||||
@@ -144,10 +164,11 @@ class Plugin(BasePlugin):
|
|||||||
class CosignerWallet(Logger):
|
class CosignerWallet(Logger):
|
||||||
# one for each open window
|
# one for each open window
|
||||||
|
|
||||||
def __init__(self, wallet: 'Multisig_Wallet', window: 'ElectrumWindow'):
|
def __init__(self, wallet: 'Multisig_Wallet', cosigner_service: 'ServerProxy', window: 'ElectrumWindow'):
|
||||||
assert isinstance(wallet, Multisig_Wallet)
|
assert isinstance(wallet, Multisig_Wallet)
|
||||||
self.wallet = wallet
|
self.wallet = wallet
|
||||||
self.window = window
|
self.window = window
|
||||||
|
self.cosigner_service = cosigner_service
|
||||||
Logger.__init__(self)
|
Logger.__init__(self)
|
||||||
self.obj = QReceiveSignalObject()
|
self.obj = QReceiveSignalObject()
|
||||||
self.obj.cosigner_receive_signal.connect(self.on_receive)
|
self.obj.cosigner_receive_signal.connect(self.on_receive)
|
||||||
@@ -226,7 +247,7 @@ class CosignerWallet(Logger):
|
|||||||
# note: we send all messages sequentially on the same thread
|
# note: we send all messages sequentially on the same thread
|
||||||
def send_messages_task():
|
def send_messages_task():
|
||||||
for _hash, message in buffer:
|
for _hash, message in buffer:
|
||||||
server.put(_hash, message)
|
self.cosigner_service.put(_hash, message)
|
||||||
msg = _('Sending transaction to cosigning pool...')
|
msg = _('Sending transaction to cosigning pool...')
|
||||||
WaitingDialog(self.window, msg, send_messages_task, on_success, on_failure)
|
WaitingDialog(self.window, msg, send_messages_task, on_success, on_failure)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user