Browse Source

Merge pull request #8772 from accumulator/qml_tx_inputs

qml: add transaction inputs in TxDetails and …
master
accumulator 2 years ago committed by GitHub
parent
commit
88058df409
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 37
      electrum/gui/qml/components/ConfirmTxDialog.qml
  2. 40
      electrum/gui/qml/components/CpfpBumpFeeDialog.qml
  3. 40
      electrum/gui/qml/components/RbfBumpFeeDialog.qml
  4. 40
      electrum/gui/qml/components/RbfCancelDialog.qml
  5. 35
      electrum/gui/qml/components/TxDetails.qml
  6. 17
      electrum/gui/qml/components/controls/ToggleLabel.qml
  7. 73
      electrum/gui/qml/components/controls/TxInput.qml
  8. 93
      electrum/gui/qml/components/controls/TxOutput.qml
  9. 11
      electrum/gui/qml/qetxdetails.py
  10. 41
      electrum/gui/qml/qetxfinalizer.py

37
electrum/gui/qml/components/ConfirmTxDialog.qml

@ -183,22 +183,53 @@ ElDialog {
iconStyle: InfoTextArea.IconStyle.Warn
}
Label {
text: qsTr('Outputs')
ToggleLabel {
id: inputs_label
Layout.columnSpan: 2
Layout.topMargin: constants.paddingMedium
labelText: qsTr('Inputs (%1)').arg(finalizer.inputs.length)
color: Material.accentColor
}
Repeater {
model: finalizer.outputs
model: inputs_label.collapsed
? undefined
: finalizer.inputs
delegate: TxInput {
Layout.columnSpan: 2
Layout.fillWidth: true
idx: index
model: modelData
}
}
ToggleLabel {
id: outputs_label
Layout.columnSpan: 2
Layout.topMargin: constants.paddingMedium
labelText: qsTr('Outputs (%1)').arg(finalizer.outputs.length)
color: Material.accentColor
}
Repeater {
model: outputs_label.collapsed
? undefined
: finalizer.outputs
delegate: TxOutput {
Layout.columnSpan: 2
Layout.fillWidth: true
allowShare: false
allowClickAddress: false
idx: index
model: modelData
}
}
}
}

40
electrum/gui/qml/components/CpfpBumpFeeDialog.qml

@ -176,23 +176,55 @@ ElDialog {
iconStyle: InfoTextArea.IconStyle.Warn
}
Label {
ToggleLabel {
id: inputs_label
Layout.columnSpan: 2
Layout.topMargin: constants.paddingMedium
visible: cpfpfeebumper.valid
text: qsTr('Outputs')
labelText: qsTr('Inputs (%1)').arg(cpfpfeebumper.inputs.length)
color: Material.accentColor
}
Repeater {
model: inputs_label.collapsed || !inputs_label.visible
? undefined
: cpfpfeebumper.inputs
delegate: TxInput {
Layout.columnSpan: 2
Layout.fillWidth: true
idx: index
model: modelData
}
}
ToggleLabel {
id: outputs_label
Layout.columnSpan: 2
Layout.topMargin: constants.paddingMedium
visible: cpfpfeebumper.valid
labelText: qsTr('Outputs (%1)').arg(cpfpfeebumper.outputs.length)
color: Material.accentColor
}
Repeater {
model: cpfpfeebumper.valid ? cpfpfeebumper.outputs : []
delegate: TxOutput {
model: outputs_label.collapsed || !outputs_label.visible
? undefined
: cpfpfeebumper.outputs
delegate: TxOutput {
Layout.columnSpan: 2
Layout.fillWidth: true
allowShare: false
allowClickAddress: false
idx: index
model: modelData
}
}
}
}

40
electrum/gui/qml/components/RbfBumpFeeDialog.qml

@ -188,23 +188,55 @@ ElDialog {
text: rbffeebumper.warning
}
Label {
ToggleLabel {
id: inputs_label
Layout.columnSpan: 2
Layout.topMargin: constants.paddingMedium
visible: rbffeebumper.valid
text: qsTr('Outputs')
labelText: qsTr('Inputs (%1)').arg(rbffeebumper.inputs.length)
color: Material.accentColor
}
Repeater {
model: inputs_label.collapsed || !inputs_label.visible
? undefined
: rbffeebumper.inputs
delegate: TxInput {
Layout.columnSpan: 2
Layout.fillWidth: true
idx: index
model: modelData
}
}
ToggleLabel {
id: outputs_label
Layout.columnSpan: 2
Layout.topMargin: constants.paddingMedium
visible: rbffeebumper.valid
labelText: qsTr('Outputs (%1)').arg(rbffeebumper.outputs.length)
color: Material.accentColor
}
Repeater {
model: rbffeebumper.valid ? rbffeebumper.outputs : []
delegate: TxOutput {
model: outputs_label.collapsed || !outputs_label.visible
? undefined
: rbffeebumper.outputs
delegate: TxOutput {
Layout.columnSpan: 2
Layout.fillWidth: true
allowShare: false
allowClickAddress: false
idx: index
model: modelData
}
}
}
}

40
electrum/gui/qml/components/RbfCancelDialog.qml

@ -151,23 +151,55 @@ ElDialog {
text: txcanceller.warning
}
Label {
ToggleLabel {
id: inputs_label
Layout.columnSpan: 2
Layout.topMargin: constants.paddingMedium
visible: txcanceller.valid
text: qsTr('Outputs')
labelText: qsTr('Inputs (%1)').arg(txcanceller.inputs.length)
color: Material.accentColor
}
Repeater {
model: inputs_label.collapsed || !inputs_label.visible
? undefined
: txcanceller.inputs
delegate: TxInput {
Layout.columnSpan: 2
Layout.fillWidth: true
idx: index
model: modelData
}
}
ToggleLabel {
id: outputs_label
Layout.columnSpan: 2
Layout.topMargin: constants.paddingMedium
visible: txcanceller.valid
labelText: qsTr('Outputs (%1)').arg(txcanceller.outputs.length)
color: Material.accentColor
}
Repeater {
model: txcanceller.valid ? txcanceller.outputs : []
delegate: TxOutput {
model: outputs_label.collapsed || !outputs_label.visible
? undefined
: txcanceller.outputs
delegate: TxOutput {
Layout.columnSpan: 2
Layout.fillWidth: true
allowShare: false
allowClickAddress: false
idx: index
model: modelData
}
}
}
}

35
electrum/gui/qml/components/TxDetails.qml

@ -302,19 +302,46 @@ Pane {
}
}
Label {
ToggleLabel {
id: inputs_label
Layout.columnSpan: 2
Layout.topMargin: constants.paddingSmall
text: qsTr('Outputs')
Layout.topMargin: constants.paddingMedium
labelText: qsTr('Inputs (%1)').arg(txdetails.inputs.length)
color: Material.accentColor
}
Repeater {
model: inputs_label.collapsed
? undefined
: txdetails.inputs
delegate: TxInput {
Layout.columnSpan: 2
Layout.fillWidth: true
idx: index
model: modelData
}
}
ToggleLabel {
id: outputs_label
Layout.columnSpan: 2
Layout.topMargin: constants.paddingMedium
labelText: qsTr('Outputs (%1)').arg(txdetails.outputs.length)
color: Material.accentColor
}
Repeater {
model: txdetails.outputs
model: outputs_label.collapsed
? undefined
: txdetails.outputs
delegate: TxOutput {
Layout.columnSpan: 2
Layout.fillWidth: true
idx: index
model: modelData
}
}

17
electrum/gui/qml/components/controls/ToggleLabel.qml

@ -0,0 +1,17 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Material
Label {
id: root
property bool collapsed: true
property string labelText
text: (collapsed ? '▷' : '▽') + ' ' + labelText
TapHandler {
onTapped: {
root.collapsed = !root.collapsed
}
}
}

73
electrum/gui/qml/components/controls/TxInput.qml

@ -0,0 +1,73 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.Material
import org.electrum 1.0
TextHighlightPane {
id: root
property variant model
property int idx: -1
ColumnLayout {
width: parent.width
RowLayout {
Layout.fillWidth: true
Label {
Layout.rightMargin: constants.paddingMedium
text: '#' + idx
font.family: FixedFont
font.bold: true
}
Label {
Layout.fillWidth: true
text: model.short_id
font.family: FixedFont
}
Label {
id: txin_value
text: model.value != undefined
? Config.formatSats(model.value)
: '<' + qsTr('unknown amount') + '>'
font.pixelSize: constants.fontSizeMedium
font.family: FixedFont
}
Label {
text: Config.baseUnit
visible: model.value != undefined
font.pixelSize: constants.fontSizeMedium
color: Material.accentColor
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
antialiasing: true
color: constants.mutedForeground
}
RowLayout {
Layout.fillWidth: true
Label {
Layout.fillWidth: true
text: model.address
? model.address
: '<' + qsTr('address unknown') + '>'
font.family: FixedFont
font.pixelSize: constants.fontSizeMedium
color: model.is_mine
? model.is_change
? constants.colorAddressInternal
: constants.colorAddressExternal
: Material.foreground
elide: Text.ElideMiddle
}
}
}
}

93
electrum/gui/qml/components/controls/TxOutput.qml

@ -11,41 +11,77 @@ TextHighlightPane {
property variant model
property bool allowShare: true
property bool allowClickAddress: true
property int idx: -1
RowLayout {
width: parent.width
Label {
text: model.address
ColumnLayout {
Layout.fillWidth: true
wrapMode: Text.Wrap
font.pixelSize: constants.fontSizeLarge
font.family: FixedFont
color: model.is_mine
? model.is_change
? constants.colorAddressInternal
: constants.colorAddressExternal
: model.is_billing
? constants.colorAddressBilling
: Material.foreground
TapHandler {
enabled: allowClickAddress && model.is_mine
onTapped: {
app.stack.push(Qt.resolvedUrl('../AddressDetails.qml'), {
address: model.address
})
RowLayout {
Layout.fillWidth: true
Label {
Layout.rightMargin: constants.paddingLarge
text: '#' + idx
visible: idx >= 0
font.family: FixedFont
font.pixelSize: constants.fontSizeMedium
font.bold: true
}
Label {
Layout.fillWidth: true
font.family: FixedFont
text: model.short_id
}
Label {
text: Config.formatSats(model.value)
font.pixelSize: constants.fontSizeMedium
font.family: FixedFont
}
Label {
text: Config.baseUnit
font.pixelSize: constants.fontSizeMedium
color: Material.accentColor
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
antialiasing: true
color: constants.mutedForeground
}
RowLayout {
Layout.fillWidth: true
Label {
text: model.address
Layout.fillWidth: true
wrapMode: Text.Wrap
font.pixelSize: constants.fontSizeMedium
font.family: FixedFont
color: model.is_mine
? model.is_change
? constants.colorAddressInternal
: constants.colorAddressExternal
: model.is_billing
? constants.colorAddressBilling
: Material.foreground
TapHandler {
enabled: allowClickAddress && model.is_mine
onTapped: {
app.stack.push(Qt.resolvedUrl('../AddressDetails.qml'), {
address: model.address
})
}
}
}
}
}
Label {
text: Config.formatSats(model.value)
font.pixelSize: constants.fontSizeMedium
font.family: FixedFont
}
Label {
text: Config.baseUnit
font.pixelSize: constants.fontSizeMedium
color: Material.accentColor
}
ToolButton {
visible: allowShare
icon.source: Qt.resolvedUrl('../../../icons/share.png')
@ -58,6 +94,7 @@ TextHighlightPane {
dialog.open()
}
}
}
}

11
electrum/gui/qml/qetxdetails.py

@ -5,7 +5,7 @@ from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
from electrum.i18n import _
from electrum.logging import get_logger
from electrum.util import format_time, TxMinedInfo
from electrum.transaction import tx_from_any, Transaction, PartialTxInput, Sighash, PartialTransaction
from electrum.transaction import tx_from_any, Transaction, PartialTxInput, Sighash, PartialTransaction, TxOutpoint
from electrum.network import Network
from electrum.address_synchronizer import TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_FUTURE
from electrum.wallet import TxSighashDanger
@ -270,10 +270,17 @@ class QETxDetails(QObject, QtEventListener):
Network.run_from_another_thread(
self._tx.add_info_from_network(self._wallet.wallet.network, timeout=10)) # FIXME is this needed?...
self._inputs = list(map(lambda x: x.to_json(), self._tx.inputs()))
self._inputs = list(map(lambda x: {
'short_id': x.prevout.short_name(),
'value': x.value_sats(),
'address': x.address,
'is_mine': self._wallet.wallet.is_mine(x.address),
'is_change': self._wallet.wallet.is_change(x.address)
}, self._tx.inputs()))
self._outputs = list(map(lambda x: {
'address': x.get_ui_address_str(),
'value': QEAmount(amount_sat=x.value),
'short_id': '', # TODO
'is_mine': self._wallet.wallet.is_mine(x.get_ui_address_str()),
'is_change': self._wallet.wallet.is_change(x.get_ui_address_str()),
'is_billing': self._wallet.wallet.is_billing_address(x.get_ui_address_str())

41
electrum/gui/qml/qetxfinalizer.py

@ -6,7 +6,7 @@ from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
from electrum.logging import get_logger
from electrum.i18n import _
from electrum.transaction import PartialTxOutput, PartialTransaction, Transaction
from electrum.transaction import PartialTxOutput, PartialTransaction, Transaction, TxOutpoint
from electrum.util import NotEnoughFunds, profiler
from electrum.wallet import CannotBumpFee, CannotDoubleSpendTx, CannotCPFP, BumpFeeStrategy
from electrum.plugin import run_hook
@ -137,6 +137,7 @@ class TxFeeSlider(FeeSlider):
self._feeRate = ''
self._rbf = False
self._tx = None
self._inputs = []
self._outputs = []
self._valid = False
self._warning = ''
@ -175,6 +176,17 @@ class TxFeeSlider(FeeSlider):
self.update()
self.rbfChanged.emit()
inputsChanged = pyqtSignal()
@pyqtProperty('QVariantList', notify=inputsChanged)
def inputs(self):
return self._inputs
@inputs.setter
def inputs(self, inputs):
if self._inputs != inputs:
self._inputs = inputs
self.inputsChanged.emit()
outputsChanged = pyqtSignal()
@pyqtProperty('QVariantList', notify=outputsChanged)
def outputs(self):
@ -210,14 +222,38 @@ class TxFeeSlider(FeeSlider):
self.fee = QEAmount(amount_sat=int(fee))
self.feeRate = f'{feerate:.1f}'
self.update_inputs_from_tx(tx)
self.update_outputs_from_tx(tx)
def update_inputs_from_tx(self, tx):
inputs = []
for inp in tx.inputs():
# addr
# addr = self.wallet.adb.get_txin_address(txin)
addr = inp.address
address_str = '<address unknown>' if addr is None else addr
txin_value = inp.value_sats() if inp.value_sats() else 0
#self.wallet.adb.get_txin_value(txin)
inputs.append({
'address': address_str,
'short_id': str(inp.short_id),
'value': QEAmount(amount_sat=txin_value),
'is_coinbase': inp.is_coinbase_input(),
'is_mine': self._wallet.wallet.is_mine(addr),
'is_change': self._wallet.wallet.is_change(addr),
'prevout_txid': inp.prevout.txid.hex()
})
self.inputs = inputs
def update_outputs_from_tx(self, tx):
outputs = []
for o in tx.outputs():
for idx, o in enumerate(tx.outputs()):
outputs.append({
'address': o.get_ui_address_str(),
'value': o.value,
'short_id': str(TxOutpoint(bytes.fromhex(tx.txid()), idx).short_name()),
'is_mine': self._wallet.wallet.is_mine(o.get_ui_address_str()),
'is_change': self._wallet.wallet.is_change(o.get_ui_address_str()),
'is_billing': self._wallet.wallet.is_billing_address(o.get_ui_address_str())
@ -829,6 +865,7 @@ class QETxCpfpFeeBumper(TxFeeSlider, TxMonMixin):
self.warning = str(e)
return
self.update_inputs_from_tx(self._new_tx)
self.update_outputs_from_tx(self._new_tx)
self._valid = True

Loading…
Cancel
Save