Browse Source

qt tx dialog: add export options for coinjoins and for coldcard

patch-1
SomberNight 5 years ago
parent
commit
c8c1ea9c86
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 76
      electrum/gui/qt/transaction_dialog.py
  2. 28
      electrum/plugins/coldcard/qt.py

76
electrum/gui/qt/transaction_dialog.py

@ -28,7 +28,7 @@ import copy
import datetime import datetime
import traceback import traceback
import time import time
from typing import TYPE_CHECKING from typing import TYPE_CHECKING, Callable
from PyQt5.QtCore import QSize, Qt from PyQt5.QtCore import QSize, Qt
from PyQt5.QtGui import QTextCharFormat, QBrush, QFont from PyQt5.QtGui import QTextCharFormat, QBrush, QFont
@ -144,16 +144,13 @@ class TxDialog(QDialog, MessageBoxMixin):
b.clicked.connect(self.close) b.clicked.connect(self.close)
b.setDefault(True) b.setDefault(True)
export_actions_menu = QMenu() self.export_actions_menu = export_actions_menu = QMenu()
action = QAction(_("Copy to clipboard"), self) self.add_export_actions_to_menu(export_actions_menu)
action.triggered.connect(lambda: parent.app.clipboard().setText((lambda: str(self.tx))())) export_actions_menu.addSeparator()
export_actions_menu.addAction(action) if isinstance(tx, PartialTransaction):
action = QAction(read_QIcon(qr_icon), _("Show as QR code"), self) export_for_coinjoin_submenu = export_actions_menu.addMenu(_("For CoinJoin; strip privates"))
action.triggered.connect(self.show_qr) self.add_export_actions_to_menu(export_for_coinjoin_submenu, gettx=self._gettx_for_coinjoin)
export_actions_menu.addAction(action)
action = QAction(_("Export to file"), self)
action.triggered.connect(self.export)
export_actions_menu.addAction(action)
self.export_actions_button = QToolButton() self.export_actions_button = QToolButton()
self.export_actions_button.setText(_("Export")) self.export_actions_button.setText(_("Export"))
self.export_actions_button.setMenu(export_actions_menu) self.export_actions_button.setMenu(export_actions_menu)
@ -167,7 +164,7 @@ class TxDialog(QDialog, MessageBoxMixin):
ptx_join_txs_action.triggered.connect(self.join_tx_with_another) ptx_join_txs_action.triggered.connect(self.join_tx_with_another)
partial_tx_actions_menu.addAction(ptx_join_txs_action) partial_tx_actions_menu.addAction(ptx_join_txs_action)
self.partial_tx_actions_button = QToolButton() self.partial_tx_actions_button = QToolButton()
self.partial_tx_actions_button.setText(_("Combine with other")) self.partial_tx_actions_button.setText(_("Combine"))
self.partial_tx_actions_button.setMenu(partial_tx_actions_menu) self.partial_tx_actions_button.setMenu(partial_tx_actions_menu)
self.partial_tx_actions_button.setPopupMode(QToolButton.InstantPopup) self.partial_tx_actions_button.setPopupMode(QToolButton.InstantPopup)
@ -212,8 +209,39 @@ class TxDialog(QDialog, MessageBoxMixin):
# Override escape-key to close normally (and invoke closeEvent) # Override escape-key to close normally (and invoke closeEvent)
self.close() self.close()
def show_qr(self): def add_export_actions_to_menu(self, menu: QMenu, *, gettx: Callable[[], Transaction] = None) -> None:
text = self.tx.serialize_as_bytes() if gettx is None:
gettx = lambda: None
action = QAction(_("Copy to clipboard"), self)
action.triggered.connect(lambda: self.copy_to_clipboard(tx=gettx()))
menu.addAction(action)
qr_icon = "qrcode_white.png" if ColorScheme.dark_scheme else "qrcode.png"
action = QAction(read_QIcon(qr_icon), _("Show as QR code"), self)
action.triggered.connect(lambda: self.show_qr(tx=gettx()))
menu.addAction(action)
action = QAction(_("Export to file"), self)
action.triggered.connect(lambda: self.export_to_file(tx=gettx()))
menu.addAction(action)
def _gettx_for_coinjoin(self) -> PartialTransaction:
if not isinstance(self.tx, PartialTransaction):
raise Exception("Can only export partial transactions for coinjoins.")
tx = copy.deepcopy(self.tx)
tx.prepare_for_export_for_coinjoin()
return tx
def copy_to_clipboard(self, *, tx: Transaction = None):
if tx is None:
tx = self.tx
self.main_window.app.clipboard().setText(str(tx))
def show_qr(self, *, tx: Transaction = None):
if tx is None:
tx = self.tx
text = tx.serialize_as_bytes()
text = base_encode(text, base=43) text = base_encode(text, base=43)
try: try:
self.main_window.show_qrcode(text, 'Transaction', parent=self) self.main_window.show_qrcode(text, 'Transaction', parent=self)
@ -245,24 +273,26 @@ class TxDialog(QDialog, MessageBoxMixin):
self.saved = True self.saved = True
self.main_window.pop_top_level_window(self) self.main_window.pop_top_level_window(self)
def export(self): def export_to_file(self, *, tx: Transaction = None):
if isinstance(self.tx, PartialTransaction): if tx is None:
self.tx.finalize_psbt() tx = self.tx
if self.tx.is_complete(): if isinstance(tx, PartialTransaction):
name = 'signed_%s.txn' % (self.tx.txid()[0:8]) tx.finalize_psbt()
if tx.is_complete():
name = 'signed_%s.txn' % (tx.txid()[0:8])
else: else:
name = self.wallet.basename() + time.strftime('-%Y%m%d-%H%M.psbt') name = self.wallet.basename() + time.strftime('-%Y%m%d-%H%M.psbt')
fileName = self.main_window.getSaveFileName(_("Select where to save your signed transaction"), name, "*.txn;;*.psbt") fileName = self.main_window.getSaveFileName(_("Select where to save your signed transaction"), name, "*.txn;;*.psbt")
if not fileName: if not fileName:
return return
if self.tx.is_complete(): # network tx hex if tx.is_complete(): # network tx hex
with open(fileName, "w+") as f: with open(fileName, "w+") as f:
network_tx_hex = self.tx.serialize_to_network() network_tx_hex = tx.serialize_to_network()
f.write(network_tx_hex + '\n') f.write(network_tx_hex + '\n')
else: # if partial: PSBT bytes else: # if partial: PSBT bytes
assert isinstance(self.tx, PartialTransaction) assert isinstance(tx, PartialTransaction)
with open(fileName, "wb+") as f: with open(fileName, "wb+") as f:
f.write(self.tx.serialize_as_bytes()) f.write(tx.serialize_as_bytes())
self.show_message(_("Transaction exported successfully")) self.show_message(_("Transaction exported successfully"))
self.saved = True self.saved = True

28
electrum/plugins/coldcard/qt.py

@ -1,15 +1,17 @@
import time, os import time, os
from functools import partial from functools import partial
import copy
from PyQt5.QtCore import Qt, pyqtSignal from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtWidgets import QPushButton, QLabel, QVBoxLayout, QWidget, QGridLayout from PyQt5.QtWidgets import QPushButton, QLabel, QVBoxLayout, QWidget, QGridLayout
from PyQt5.QtWidgets import QFileDialog
from electrum.gui.qt.util import WindowModalDialog, CloseButton, get_parent_main_window, Buttons
from electrum.gui.qt.transaction_dialog import TxDialog
from electrum.i18n import _ from electrum.i18n import _
from electrum.plugin import hook from electrum.plugin import hook
from electrum.wallet import Standard_Wallet, Multisig_Wallet from electrum.wallet import Multisig_Wallet
from electrum.gui.qt.util import WindowModalDialog, CloseButton, get_parent_main_window, Buttons from electrum.transaction import PartialTransaction
from electrum.transaction import Transaction
from .coldcard import ColdcardPlugin, xfp2str from .coldcard import ColdcardPlugin, xfp2str
from ..hw_wallet.qt import QtHandlerBase, QtPluginBase from ..hw_wallet.qt import QtHandlerBase, QtPluginBase
@ -68,6 +70,24 @@ class Plugin(ColdcardPlugin, QtPluginBase):
ColdcardPlugin.export_ms_wallet(wallet, f, basename) ColdcardPlugin.export_ms_wallet(wallet, f, basename)
main_window.show_message(_("Wallet setup file exported successfully")) main_window.show_message(_("Wallet setup file exported successfully"))
@hook
def transaction_dialog(self, dia: TxDialog):
# if not a Coldcard wallet, hide feature
if not any(type(ks) == self.keystore_class for ks in dia.wallet.get_keystores()):
return
def gettx_for_coldcard_export() -> PartialTransaction:
if not isinstance(dia.tx, PartialTransaction):
raise Exception("Can only export partial transactions for coinjoins.")
tx = copy.deepcopy(dia.tx)
tx.add_info_from_wallet(dia.wallet, include_xpubs_and_full_paths=True)
return tx
# add a new "export" option
if isinstance(dia.tx, PartialTransaction):
export_submenu = dia.export_actions_menu.addMenu(_("For {}; include xpubs").format(self.device))
dia.add_export_actions_to_menu(export_submenu, gettx=gettx_for_coldcard_export)
def show_settings_dialog(self, window, keystore): def show_settings_dialog(self, window, keystore):
# When they click on the icon for CC we come here. # When they click on the icon for CC we come here.
# - doesn't matter if device not connected, continue # - doesn't matter if device not connected, continue

Loading…
Cancel
Save