Browse Source

qml: accept raw tx from send page paste/qrscan and show TxDetails

master
Sander van Grieken 3 years ago
parent
commit
fad9f87303
  1. 23
      electrum/gui/qml/components/Send.qml
  2. 36
      electrum/gui/qml/components/TxDetails.qml
  3. 11
      electrum/gui/qml/qebitcoin.py
  4. 41
      electrum/gui/qml/qetxdetails.py

23
electrum/gui/qml/components/Send.qml

@ -63,7 +63,16 @@ Pane {
icon.source: '../../icons/paste.png' icon.source: '../../icons/paste.png'
icon.height: constants.iconSizeMedium icon.height: constants.iconSizeMedium
icon.width: constants.iconSizeMedium icon.width: constants.iconSizeMedium
onClicked: invoice.recipient = AppController.clipboardToText() onClicked: {
var text = AppController.clipboardToText()
if (bitcoin.verify_raw_tx(text)) {
app.stack.push(Qt.resolvedUrl('TxDetails.qml'),
{ rawtx: text }
)
} else {
invoice.recipient = text
}
}
} }
ToolButton { ToolButton {
icon.source: '../../icons/qrcode.png' icon.source: '../../icons/qrcode.png'
@ -73,7 +82,14 @@ Pane {
onClicked: { onClicked: {
var page = app.stack.push(Qt.resolvedUrl('Scan.qml')) var page = app.stack.push(Qt.resolvedUrl('Scan.qml'))
page.onFound.connect(function() { page.onFound.connect(function() {
invoice.recipient = page.scanData var text = page.scanData
if (bitcoin.verify_raw_tx(text)) {
app.stack.push(Qt.resolvedUrl('TxDetails.qml'),
{ rawtx: text }
)
} else {
invoice.recipient = text
}
}) })
} }
} }
@ -379,4 +395,7 @@ Pane {
} }
} }
Bitcoin {
id: bitcoin
}
} }

36
electrum/gui/qml/components/TxDetails.qml

@ -15,6 +15,7 @@ Pane {
property string title: qsTr("Transaction details") property string title: qsTr("Transaction details")
property string txid property string txid
property string rawtx
property alias label: txdetails.label property alias label: txdetails.label
@ -50,9 +51,24 @@ Pane {
width: parent.width width: parent.width
columns: 2 columns: 2
RowLayout {
Layout.fillWidth: true
Layout.columnSpan: 2
visible: txdetails.isUnrelated
Image {
source: '../../icons/warning.png'
Layout.preferredWidth: constants.iconSizeSmall
Layout.preferredHeight: constants.iconSizeSmall
}
Label {
text: qsTr('Transaction is unrelated to this wallet')
color: Material.accentColor
}
}
Label { Label {
Layout.fillWidth: true Layout.fillWidth: true
visible: txdetails.lnAmount.satsInt == 0 visible: !txdetails.isUnrelated && txdetails.lnAmount.satsInt == 0
text: txdetails.amount.satsInt > 0 text: txdetails.amount.satsInt > 0
? qsTr('Amount received') ? qsTr('Amount received')
: qsTr('Amount sent') : qsTr('Amount sent')
@ -61,7 +77,7 @@ Pane {
Label { Label {
Layout.fillWidth: true Layout.fillWidth: true
visible: txdetails.lnAmount.satsInt != 0 visible: !txdetails.isUnrelated && txdetails.lnAmount.satsInt != 0
text: txdetails.lnAmount.satsInt > 0 text: txdetails.lnAmount.satsInt > 0
? qsTr('Amount received in channels') ? qsTr('Amount received in channels')
: qsTr('Amount withdrawn from channels') : qsTr('Amount withdrawn from channels')
@ -70,6 +86,7 @@ Pane {
} }
RowLayout { RowLayout {
visible: !txdetails.isUnrelated
Layout.fillWidth: true Layout.fillWidth: true
Label { Label {
visible: txdetails.lnAmount.satsInt == 0 visible: txdetails.lnAmount.satsInt == 0
@ -88,30 +105,30 @@ Pane {
} }
Item { Item {
visible: Daemon.fx.enabled; Layout.preferredWidth: 1; Layout.preferredHeight: 1 visible: !txdetails.isUnrelated && Daemon.fx.enabled; Layout.preferredWidth: 1; Layout.preferredHeight: 1
} }
Label { Label {
visible: Daemon.fx.enabled && txdetails.lnAmount.satsInt == 0 visible: !txdetails.isUnrelated && Daemon.fx.enabled && txdetails.lnAmount.satsInt == 0
text: Daemon.fx.fiatValue(txdetails.amount, false) + ' ' + Daemon.fx.fiatCurrency text: Daemon.fx.fiatValue(txdetails.amount, false) + ' ' + Daemon.fx.fiatCurrency
} }
Label { Label {
visible: Daemon.fx.enabled && txdetails.lnAmount.satsInt != 0 visible: !txdetails.isUnrelated && Daemon.fx.enabled && txdetails.lnAmount.satsInt != 0
text: Daemon.fx.fiatValue(txdetails.lnAmount, false) + ' ' + Daemon.fx.fiatCurrency text: Daemon.fx.fiatValue(txdetails.lnAmount, false) + ' ' + Daemon.fx.fiatCurrency
} }
Label { Label {
Layout.fillWidth: true Layout.fillWidth: true
visible: txdetails.amount.satsInt < 0 visible: txdetails.fee.satsInt != 0
text: qsTr('Transaction fee') text: qsTr('Transaction fee')
color: Material.accentColor color: Material.accentColor
} }
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
visible: txdetails.amount.satsInt < 0 visible: txdetails.fee.satsInt != 0
Label { Label {
text: Config.formatSats(txdetails.fee) text: Config.formatSats(txdetails.fee)
font.family: FixedFont font.family: FixedFont
@ -135,12 +152,12 @@ Pane {
Label { Label {
text: qsTr('Mempool depth') text: qsTr('Mempool depth')
color: Material.accentColor color: Material.accentColor
visible: !txdetails.isMined visible: !txdetails.isMined && txdetails.canBroadcast
} }
Label { Label {
text: txdetails.mempoolDepth text: txdetails.mempoolDepth
visible: !txdetails.isMined visible: !txdetails.isMined && txdetails.canBroadcast
} }
Label { Label {
@ -314,6 +331,7 @@ Pane {
id: txdetails id: txdetails
wallet: Daemon.currentWallet wallet: Daemon.currentWallet
txid: root.txid txid: root.txid
rawtx: root.rawtx
onLabelChanged: root.detailsChanged() onLabelChanged: root.detailsChanged()
} }
} }

11
electrum/gui/qml/qebitcoin.py

@ -9,8 +9,9 @@ from electrum.bip32 import is_bip32_derivation, xpub_type
from electrum.logging import get_logger from electrum.logging import get_logger
from electrum.slip39 import decode_mnemonic, Slip39Error from electrum.slip39 import decode_mnemonic, Slip39Error
from electrum.util import parse_URI, create_bip21_uri, InvalidBitcoinURI, get_asyncio_loop from electrum.util import parse_URI, create_bip21_uri, InvalidBitcoinURI, get_asyncio_loop
from .qetypes import QEAmount from electrum.transaction import tx_from_any
from .qetypes import QEAmount
class QEBitcoin(QObject): class QEBitcoin(QObject):
def __init__(self, config, parent=None): def __init__(self, config, parent=None):
@ -152,3 +153,11 @@ class QEBitcoin(QObject):
extra_params['exp'] = str(expiry) extra_params['exp'] = str(expiry)
return create_bip21_uri(address, satoshis.satsInt, message, extra_query_params=extra_params) return create_bip21_uri(address, satoshis.satsInt, message, extra_query_params=extra_params)
@pyqtSlot(str, result=bool)
def verify_raw_tx(self, rawtx):
try:
tx_from_any(rawtx)
return True
except:
return False

41
electrum/gui/qml/qetxdetails.py

@ -2,6 +2,7 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
from electrum.logging import get_logger from electrum.logging import get_logger
from electrum.util import format_time from electrum.util import format_time
from electrum.transaction import tx_from_any
from .qewallet import QEWallet from .qewallet import QEWallet
from .qetypes import QEAmount from .qetypes import QEAmount
@ -13,9 +14,12 @@ class QETxDetails(QObject):
_logger = get_logger(__name__) _logger = get_logger(__name__)
_wallet = None _wallet = None
_txid = None _txid = ''
_rawtx = ''
_label = '' _label = ''
_tx = None
_status = '' _status = ''
_amount = QEAmount(amount_sat=0) _amount = QEAmount(amount_sat=0)
_lnamount = QEAmount(amount_sat=0) _lnamount = QEAmount(amount_sat=0)
@ -30,6 +34,7 @@ class QETxDetails(QObject):
_can_cpfp = False _can_cpfp = False
_can_save_as_local = False _can_save_as_local = False
_can_remove = False _can_remove = False
_is_unrelated = False
_is_mined = False _is_mined = False
@ -67,6 +72,22 @@ class QETxDetails(QObject):
self.txidChanged.emit() self.txidChanged.emit()
self.update() self.update()
@pyqtProperty(str, notify=detailsChanged)
def rawtx(self):
return self._rawtx
@rawtx.setter
def rawtx(self, rawtx: str):
if self._rawtx != rawtx:
self._logger.debug('rawtx set -> %s' % rawtx)
self._rawtx = rawtx
try:
self._tx = tx_from_any(rawtx, deserialize=True)
self._logger.debug('tx type is %s' % str(type(self._tx)))
self.txid = self._tx.txid() # triggers update()
except Exception as e:
self._logger.error(repr(e))
labelChanged = pyqtSignal() labelChanged = pyqtSignal()
@pyqtProperty(str, notify=labelChanged) @pyqtProperty(str, notify=labelChanged)
def label(self): def label(self):
@ -159,24 +180,29 @@ class QETxDetails(QObject):
def canRemove(self): def canRemove(self):
return self._can_remove return self._can_remove
@pyqtProperty(bool, notify=detailsChanged)
def isUnrelated(self):
return self._is_unrelated
def update(self): def update(self):
if self._wallet is None: if self._wallet is None:
self._logger.error('wallet undefined') self._logger.error('wallet undefined')
return return
# abusing get_input_tx to get tx from txid if not self._rawtx:
tx = self._wallet.wallet.get_input_tx(self._txid) # abusing get_input_tx to get tx from txid
self._tx = self._wallet.wallet.get_input_tx(self._txid)
#self._logger.debug(repr(tx.to_json())) #self._logger.debug(repr(self._tx.to_json()))
self._inputs = list(map(lambda x: x.to_json(), tx.inputs())) self._inputs = list(map(lambda x: x.to_json(), self._tx.inputs()))
self._outputs = list(map(lambda x: { self._outputs = list(map(lambda x: {
'address': x.get_ui_address_str(), 'address': x.get_ui_address_str(),
'value': QEAmount(amount_sat=x.value), 'value': QEAmount(amount_sat=x.value),
'is_mine': self._wallet.wallet.is_mine(x.get_ui_address_str()) 'is_mine': self._wallet.wallet.is_mine(x.get_ui_address_str())
}, tx.outputs())) }, self._tx.outputs()))
txinfo = self._wallet.wallet.get_tx_info(tx) txinfo = self._wallet.wallet.get_tx_info(self._tx)
#self._logger.debug(repr(txinfo)) #self._logger.debug(repr(txinfo))
@ -204,6 +230,7 @@ class QETxDetails(QObject):
else: else:
self._lnamount.satsInt = 0 self._lnamount.satsInt = 0
self._is_unrelated = txinfo.amount is None and self._lnamount.isEmpty
self._is_lightning_funding_tx = txinfo.is_lightning_funding_tx self._is_lightning_funding_tx = txinfo.is_lightning_funding_tx
self._can_bump = txinfo.can_bump self._can_bump = txinfo.can_bump
self._can_dscancel = txinfo.can_dscancel self._can_dscancel = txinfo.can_dscancel

Loading…
Cancel
Save