Transfer: disable offline tx creation with long payment id
What changed, and why it matters
This change updates the Monero GUI wallet so that users can no longer create offline transaction files when a long payment ID is involved. Long payment IDs are an older, privacy-weaker way to label a Monero payment. The patch also refactors the send/offline-tx enable logic into clearer conditions. The direct security risk is modest: it mainly prevents users from accidentally using an obsolete, less-private feature in a specific workflow, rather than fixing an active exploit or remote code flaw.
Treat as a minor privacy-hardening UI change. No urgent action is required. Users relying on offline transaction signing should avoid long payment IDs, which are already deprecated. Reviewers may want to confirm that disabling offline creation is intentional and that no other workflows still allow long payment IDs in cold-wallet signing.
Security signals we found
Disables a feature path (offline transaction creation) for long payment IDs
Long payment IDs are described in code comments/user text as 'obsolete' and 'not encrypted on the blockchain', which harms privacy
Refactors UI state guards to share a single warning predicate
No cryptographic, network, or parsing code is changed
Evidence from the diff
In pages/Transfer.qml, the patch introduces boolean properties (paymentIdBlocked, hasTransferWarnings, hasValidTransferInfo, canSendTransaction, canCreateOfflineTx) and replaces inline checks with them. The key behavioral change is that canCreateOfflineTx now includes !hasTransferWarnings, which is true when a long payment ID is present. Therefore the ‘Create’ button for offline transaction signing is disabled if a long payment ID is used. The ordinary Send button was already disabled in this situation via paymentIdWarningBox.visible; the patch makes that consistent by using the same hasTransferWarnings guard. The old pageRoot.checkInformation() helper is removed and its balance/amount/address checks are folded into hasValidTransferInfo.
Changed components
pages/Transfer.qmlMonero GUI offline transaction signing workflowLong payment ID handling in the transfer pageInspect captured patch +17 / −11
diff --git a/pages/Transfer.qml b/pages/Transfer.qml
index 66e0b66..48ce4b8 100644
--- a/pages/Transfer.qml
+++ b/pages/Transfer.qml
@@ -81,6 +81,19 @@ Rectangle {
}
property string startLinkText: "<style type='text/css'>a {text-decoration: none; color: #FF6C3C; font-size: 14px;}</style><a href='#'>(%1)</a>".arg(qsTr("Start daemon")) + translationManager.emptyString
property bool warningLongPidDescription: descriptionLine.text.match(/^[0-9a-f]{64}$/i)
+ property bool paymentIdBlocked: paymentIdCheckbox.checked || warningLongPidDescription
+ property bool hasTransferWarnings: warningContent !== "" || paymentIdBlocked
+ property bool hasValidTransferInfo: !recipientModel.hasEmptyAddress()
+ && !recipientModel.hasEmptyAmount()
+ && !recipientModel.hasInvalidAddress()
+ && recipientModel.getAmountTotal() <= appWindow.getUnlockedBalance()
+ property bool canSendTransaction: !sendButtonWarningBox.visible
+ && !hasTransferWarnings
+ && hasValidTransferInfo
+ property bool canCreateOfflineTx: appWindow.viewOnly
+ && appWindow.daemonSynced
+ && !hasTransferWarnings
+ && hasValidTransferInfo
Clipboard { id: clipboard }
@@ -819,7 +832,7 @@ Rectangle {
text: qsTr("Long payment IDs are obsolete. \
Long payment IDs were not encrypted on the blockchain and would harm your privacy. \
If the party you're sending to still requires a long payment ID, please notify them.") + translationManager.emptyString;
- visible: paymentIdCheckbox.checked || warningLongPidDescription
+ visible: root.paymentIdBlocked
}
MoneroComponents.WarningBox {
@@ -834,7 +847,7 @@ Rectangle {
rightIcon: "qrc:///images/rightArrow.png"
Layout.topMargin: 4
text: qsTr("Send") + translationManager.emptyString
- enabled: !sendButtonWarningBox.visible && !warningContent && !recipientModel.hasEmptyAddress() && !paymentIdWarningBox.visible
+ enabled: root.canSendTransaction
onClicked: {
console.log("Transfer: paymentClicked")
var priority = priorityModelV5.get(priorityDropdown.currentIndex).priority
@@ -844,13 +857,6 @@ Rectangle {
}
}
}
-
- function checkInformation() {
- return !recipientModel.hasEmptyAmount() &&
- recipientModel.getAmountTotal() <= appWindow.getUnlockedBalance() &&
- !recipientModel.hasInvalidAddress();
- }
-
} // pageRoot
ColumnLayout {
@@ -934,7 +940,7 @@ Rectangle {
visible: persistentSettings.transferShowAdvanced && appWindow.walletMode >= 2
title: qsTr("Offline transaction signing") + translationManager.emptyString
button1.text: qsTr("Create") + translationManager.emptyString
- button1.enabled: appWindow.viewOnly && pageRoot.checkInformation() && appWindow.daemonSynced
+ button1.enabled: root.canCreateOfflineTx
button1.onClicked: {
console.log("Transfer: saveTx Clicked")
var priority = priorityModelV5.get(priorityDropdown.currentIndex).priority
@@ -956,7 +962,7 @@ Rectangle {
}
tooltip: {
var errorMessage = "";
- if (appWindow.viewOnly && !pageRoot.checkInformation()) {
+ if (appWindow.viewOnly && !root.hasValidTransferInfo) {
errorMessage = "<p class='orange'>" + qsTr("* To create a transaction file, please enter address and amount above") + "</p>";
}
var header = qsTr("Spend XMR from a cold (offline) wallet") + translationManager.emptyString;
Why this scored 33/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.