1
0

integrate PSBT support natively. WIP

This commit is contained in:
SomberNight
2019-10-23 17:09:41 +02:00
parent 6d12ebabbb
commit bafe8a2fff
61 changed files with 3405 additions and 3310 deletions

View File

@@ -25,23 +25,25 @@
import time
from xmlrpc.client import ServerProxy
from typing import TYPE_CHECKING, Union, List, Tuple
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtWidgets import QPushButton
from electrum import util, keystore, ecc, crypto
from electrum import transaction
from electrum.transaction import Transaction, PartialTransaction, tx_from_any
from electrum.bip32 import BIP32Node
from electrum.plugin import BasePlugin, hook
from electrum.i18n import _
from electrum.wallet import Multisig_Wallet
from electrum.util import bh2u, bfh
from electrum.gui.qt.transaction_dialog import show_transaction
from electrum.gui.qt.transaction_dialog import show_transaction, TxDialog
from electrum.gui.qt.util import WaitingDialog
import sys
import traceback
if TYPE_CHECKING:
from electrum.gui.qt.main_window import ElectrumWindow
server = ServerProxy('https://cosigner.electrum.org/', allow_none=True)
@@ -97,8 +99,8 @@ class Plugin(BasePlugin):
self.listener = None
self.obj = QReceiveSignalObject()
self.obj.cosigner_receive_signal.connect(self.on_receive)
self.keys = []
self.cosigner_list = []
self.keys = [] # type: List[Tuple[str, str, ElectrumWindow]]
self.cosigner_list = [] # type: List[Tuple[ElectrumWindow, str, bytes, str]]
@hook
def init_qt(self, gui):
@@ -116,10 +118,11 @@ class Plugin(BasePlugin):
def is_available(self):
return True
def update(self, window):
def update(self, window: 'ElectrumWindow'):
wallet = window.wallet
if type(wallet) != Multisig_Wallet:
return
assert isinstance(wallet, Multisig_Wallet) # only here for type-hints in IDE
if self.listener is None:
self.logger.info("starting listener")
self.listener = Listener(self)
@@ -131,7 +134,7 @@ class Plugin(BasePlugin):
self.keys = []
self.cosigner_list = []
for key, keystore in wallet.keystores.items():
xpub = keystore.get_master_public_key()
xpub = keystore.get_master_public_key() # type: str
pubkey = BIP32Node.from_xkey(xpub).eckey.get_public_key_bytes(compressed=True)
_hash = bh2u(crypto.sha256d(pubkey))
if not keystore.is_watching_only():
@@ -142,14 +145,14 @@ class Plugin(BasePlugin):
self.listener.set_keyhashes([t[1] for t in self.keys])
@hook
def transaction_dialog(self, d):
def transaction_dialog(self, d: 'TxDialog'):
d.cosigner_send_button = b = QPushButton(_("Send to cosigner"))
b.clicked.connect(lambda: self.do_send(d.tx))
d.buttons.insert(0, b)
self.transaction_dialog_update(d)
@hook
def transaction_dialog_update(self, d):
def transaction_dialog_update(self, d: 'TxDialog'):
if d.tx.is_complete() or d.wallet.can_sign(d.tx):
d.cosigner_send_button.hide()
return
@@ -160,17 +163,14 @@ class Plugin(BasePlugin):
else:
d.cosigner_send_button.hide()
def cosigner_can_sign(self, tx, cosigner_xpub):
from electrum.keystore import is_xpubkey, parse_xpubkey
xpub_set = set([])
for txin in tx.inputs():
for x_pubkey in txin['x_pubkeys']:
if is_xpubkey(x_pubkey):
xpub, s = parse_xpubkey(x_pubkey)
xpub_set.add(xpub)
return cosigner_xpub in xpub_set
def cosigner_can_sign(self, tx: Transaction, cosigner_xpub: str) -> bool:
if not isinstance(tx, PartialTransaction):
return False
if tx.is_complete():
return False
return cosigner_xpub in {bip32node.to_xpub() for bip32node in tx.xpubs}
def do_send(self, tx):
def do_send(self, tx: Union[Transaction, PartialTransaction]):
def on_success(result):
window.show_message(_("Your transaction was sent to the cosigning pool.") + '\n' +
_("Open your cosigner wallet to retrieve it."))
@@ -184,7 +184,7 @@ class Plugin(BasePlugin):
if not self.cosigner_can_sign(tx, xpub):
continue
# construct message
raw_tx_bytes = bfh(str(tx))
raw_tx_bytes = tx.serialize_as_bytes()
public_key = ecc.ECPubkey(K)
message = public_key.encrypt_message(raw_tx_bytes).decode('ascii')
# send message
@@ -223,12 +223,12 @@ class Plugin(BasePlugin):
return
try:
privkey = BIP32Node.from_xkey(xprv).eckey
message = bh2u(privkey.decrypt_message(message))
message = privkey.decrypt_message(message)
except Exception as e:
self.logger.exception('')
window.show_error(_('Error decrypting message') + ':\n' + repr(e))
return
self.listener.clear(keyhash)
tx = transaction.Transaction(message)
show_transaction(tx, window, prompt_if_unsaved=True)
tx = tx_from_any(message)
show_transaction(tx, parent=window, prompt_if_unsaved=True)