qml: accept raw tx from send page paste/qrscan and show TxDetails
This commit is contained in:
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user