1
0

wallet: add sighash check to class Abstract_Wallet

qml: use backend sighash check and add user confirmation path
qt: use backend sighash check and add user confirmation path
qml: include get_warning_for_risk_of_burning_coins_as_fees test in txdetails
qt: add warning icon to sighash warning
add sighash and fee checks to wallet.sign_transaction, making all warnings fatal unless ignore_warnings is set to True
tests: test sign_transaction on both code paths with ignore_warnings True and False,
raise specific exceptions TransactionPotentiallyDangerousException and TransactionDangerousException
This commit is contained in:
Sander van Grieken
2023-11-20 15:23:49 +01:00
committed by accumulator
parent 6467db0b7d
commit 7b96a83350
9 changed files with 167 additions and 22 deletions

View File

@@ -49,6 +49,16 @@ Pane {
text: qsTr('On-chain Transaction')
}
InfoTextArea {
id: warn
Layout.columnSpan: 2
Layout.fillWidth: true
Layout.bottomMargin: constants.paddingLarge
visible: txdetails.warning
text: txdetails.warning
iconStyle: InfoTextArea.IconStyle.Warn
}
InfoTextArea {
id: bumpfeeinfo
Layout.columnSpan: 2
@@ -336,7 +346,20 @@ Pane {
icon.source: '../../icons/key.png'
text: qsTr('Sign')
visible: txdetails.canSign
onClicked: txdetails.sign()
onClicked: {
if (txdetails.shouldConfirm) {
var dialog = app.messageDialog.createObject(app, {
text: qsTr('Confirm signing non-standard transaction?'),
yesno: true
})
dialog.accepted.connect(function() {
txdetails.sign()
})
dialog.open()
} else {
txdetails.sign()
}
}
}
FlatButton {

View File

@@ -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
from electrum.transaction import tx_from_any, Transaction, PartialTxInput, Sighash, PartialTransaction
from electrum.network import Network
from electrum.address_synchronizer import TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED, TX_HEIGHT_FUTURE
@@ -32,6 +32,7 @@ class QETxDetails(QObject, QtEventListener):
self._txid = ''
self._rawtx = ''
self._label = ''
self._warning = ''
self._tx = None # type: Optional[Transaction]
@@ -56,6 +57,7 @@ class QETxDetails(QObject, QtEventListener):
self._is_mined = False
self._is_final = False
self._lock_delay = 0
self._should_confirm = False
self._mempool_depth = ''
@@ -140,6 +142,10 @@ class QETxDetails(QObject, QtEventListener):
def status(self):
return self._status
@pyqtProperty(str, notify=detailsChanged)
def warning(self):
return self._warning
@pyqtProperty(QEAmount, notify=detailsChanged)
def amount(self):
return self._amount
@@ -240,6 +246,10 @@ class QETxDetails(QObject, QtEventListener):
def lockDelay(self):
return self._lock_delay
@pyqtProperty(bool, notify=detailsChanged)
def shouldConfirm(self):
return self._should_confirm
def update(self, from_txid: bool = False):
assert self._wallet
@@ -284,6 +294,9 @@ class QETxDetails(QObject, QtEventListener):
fee_per_kb = txinfo.fee / size * 1000
self._feerate_str = self._wallet.wallet.config.format_fee_rate(fee_per_kb)
self._should_confirm = False
should_reject = False
self._lock_delay = 0
self._is_mined = False if not txinfo.tx_mined_status else txinfo.tx_mined_status.height > 0
if self._is_mined:
@@ -293,6 +306,10 @@ class QETxDetails(QObject, QtEventListener):
self._mempool_depth = self._wallet.wallet.config.depth_tooltip(txinfo.mempool_depth_bytes)
elif txinfo.tx_mined_status.height == TX_HEIGHT_FUTURE:
self._lock_delay = txinfo.tx_mined_status.wanted_height - self._wallet.wallet.adb.get_local_height()
if isinstance(self._tx, PartialTransaction):
self._should_confirm, should_reject, message = self._wallet.wallet.check_sighash(self._tx)
if message:
self._warning = '\n'.join([_('Danger! This transaction is non-standard!'), message])
if self._wallet.wallet.lnworker:
# Calling lnworker.get_onchain_history and wallet.get_full_history here
@@ -320,7 +337,13 @@ class QETxDetails(QObject, QtEventListener):
self._can_cpfp = txinfo.can_cpfp and not txinfo.can_remove
self._can_save_as_local = txinfo.can_save_as_local and not txinfo.can_remove
self._can_remove = txinfo.can_remove
self._can_sign = not self._is_complete and self._wallet.wallet.can_sign(self._tx)
self._can_sign = not self._is_complete and self._wallet.wallet.can_sign(self._tx) and not should_reject
if isinstance(self._tx, PartialTransaction):
risk_of_burning_coins = (self._can_sign and txinfo.fee is not None
and self._wallet.wallet.get_warning_for_risk_of_burning_coins_as_fees(self._tx))
if risk_of_burning_coins:
self._warning = '\n'.join([self._warning, risk_of_burning_coins])
self.detailsChanged.emit()

View File

@@ -512,7 +512,8 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
def do_sign(self, tx, broadcast):
try:
tx = self.wallet.sign_transaction(tx, self.password)
# ignore_warnings=True, because UI checks and asks user confirmation itself
tx = self.wallet.sign_transaction(tx, self.password, ignore_warnings=True)
except BaseException as e:
self._logger.error(f'{e!r}')
self.signFailed.emit(str(e))