mirror of
https://github.com/monero-project/monero-gui.git
synced 2024-12-23 12:09:57 +00:00
Merge pull request #1172
65ea07a
Add a shared ringdb management page - rings and blackballed outputs22a1114
Add segregation key reuse mitigation options
This commit is contained in:
commit
00e34f7846
11 changed files with 611 additions and 9 deletions
|
@ -48,6 +48,7 @@ Rectangle {
|
|||
signal transferClicked()
|
||||
signal receiveClicked()
|
||||
signal txkeyClicked()
|
||||
signal sharedringdbClicked()
|
||||
signal settingsClicked()
|
||||
signal addressBookClicked()
|
||||
signal miningClicked()
|
||||
|
@ -63,6 +64,7 @@ Rectangle {
|
|||
else if(pos === "AddressBook") menuColumn.previousButton = addressBookButton
|
||||
else if(pos === "Mining") menuColumn.previousButton = miningButton
|
||||
else if(pos === "TxKey") menuColumn.previousButton = txkeyButton
|
||||
else if(pos === "SharedRingDB") menuColumn.previousButton = sharedringdbButton
|
||||
else if(pos === "Sign") menuColumn.previousButton = signButton
|
||||
else if(pos === "Settings") menuColumn.previousButton = settingsButton
|
||||
else if(pos === "Advanced") menuColumn.previousButton = advancedButton
|
||||
|
@ -456,6 +458,30 @@ Rectangle {
|
|||
color: "#505050"
|
||||
height: 1
|
||||
}
|
||||
// ------------- Shared RingDB tab ---------------
|
||||
MenuButton {
|
||||
id: sharedringdbButton
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text: qsTr("Shared RingDB") + translationManager.emptyString
|
||||
symbol: qsTr("S") + translationManager.emptyString
|
||||
dotColor: "#FFD781"
|
||||
under: advancedButton
|
||||
onClicked: {
|
||||
parent.previousButton.checked = false
|
||||
parent.previousButton = sharedringdbButton
|
||||
panel.sharedringdbClicked()
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
visible: sharedringdbButton.present
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: 16
|
||||
color: "#505050"
|
||||
height: 1
|
||||
}
|
||||
|
||||
|
||||
// ------------- Sign/verify tab ---------------
|
||||
MenuButton {
|
||||
|
|
|
@ -54,6 +54,7 @@ Rectangle {
|
|||
property Transfer transferView: Transfer { }
|
||||
property Receive receiveView: Receive { }
|
||||
property TxKey txkeyView: TxKey { }
|
||||
property SharedRingDB sharedringdbView: SharedRingDB { }
|
||||
property History historyView: History { }
|
||||
property Sign signView: Sign { }
|
||||
property Settings settingsView: Settings { }
|
||||
|
@ -117,6 +118,10 @@ Rectangle {
|
|||
name: "TxKey"
|
||||
PropertyChanges { target: root; currentView: txkeyView }
|
||||
PropertyChanges { target: mainFlickable; contentHeight: minHeight }
|
||||
}, State {
|
||||
name: "SharedRingDB"
|
||||
PropertyChanges { target: root; currentView: sharedringdbView }
|
||||
PropertyChanges { target: mainFlickable; contentHeight: minHeight }
|
||||
}, State {
|
||||
name: "AddressBook"
|
||||
PropertyChanges { target: root; currentView: addressBookView }
|
||||
|
|
|
@ -39,7 +39,7 @@ ListView {
|
|||
property int rowSpacing: 12
|
||||
property var addressBookModel: null
|
||||
|
||||
function buildTxDetailsString(tx_id, paymentId, tx_key,tx_note, destinations) {
|
||||
function buildTxDetailsString(tx_id, paymentId, tx_key,tx_note, destinations, rings) {
|
||||
var trStart = '<tr><td width="85" style="padding-top:5px"><b>',
|
||||
trMiddle = '</b></td><td style="padding-left:10px;padding-top:5px;">',
|
||||
trEnd = "</td></tr>";
|
||||
|
@ -50,6 +50,7 @@ ListView {
|
|||
+ (tx_key ? trStart + qsTr("Tx key:") + trMiddle + tx_key + trEnd : "")
|
||||
+ (tx_note ? trStart + qsTr("Tx note:") + trMiddle + tx_note + trEnd : "")
|
||||
+ (destinations ? trStart + qsTr("Destinations:") + trMiddle + destinations + trEnd : "")
|
||||
+ (rings ? trStart + qsTr("Rings:") + trMiddle + rings + trEnd : "")
|
||||
+ "</table>"
|
||||
+ translationManager.emptyString;
|
||||
}
|
||||
|
@ -103,8 +104,11 @@ ListView {
|
|||
onClicked: {
|
||||
var tx_key = currentWallet.getTxKey(hash)
|
||||
var tx_note = currentWallet.getUserNote(hash)
|
||||
var rings = currentWallet.getRings(hash)
|
||||
if (rings)
|
||||
rings = rings.replace(/\|/g, '\n')
|
||||
informationPopup.title = "Transaction details";
|
||||
informationPopup.content = buildTxDetailsString(hash,paymentId,tx_key,tx_note,destinations);
|
||||
informationPopup.content = buildTxDetailsString(hash,paymentId,tx_key,tx_note,destinations, rings);
|
||||
informationPopup.onCloseCallback = null
|
||||
informationPopup.open();
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ ListView {
|
|||
property var previousItem
|
||||
property var addressBookModel: null
|
||||
|
||||
function buildTxDetailsString(tx_id, paymentId, tx_key,tx_note, destinations) {
|
||||
function buildTxDetailsString(tx_id, paymentId, tx_key,tx_note, destinations, rings) {
|
||||
var trStart = '<tr><td width="85" style="padding-top:5px"><b>',
|
||||
trMiddle = '</b></td><td style="padding-left:10px;padding-top:5px;">',
|
||||
trEnd = "</td></tr>";
|
||||
|
@ -49,6 +49,7 @@ ListView {
|
|||
+ (tx_key ? trStart + qsTr("Tx key:") + trMiddle + tx_key + trEnd : "")
|
||||
+ (tx_note ? trStart + qsTr("Tx note:") + trMiddle + tx_note + trEnd : "")
|
||||
+ (destinations ? trStart + qsTr("Destinations:") + trMiddle + destinations + trEnd : "")
|
||||
+ (rings ? trStart + qsTr("Rings:") + trMiddle + rings + trEnd : "")
|
||||
+ "</table>"
|
||||
+ translationManager.emptyString;
|
||||
}
|
||||
|
@ -91,9 +92,11 @@ ListView {
|
|||
onClicked: {
|
||||
var tx_key = currentWallet.getTxKey(hash)
|
||||
var tx_note = currentWallet.getUserNote(hash)
|
||||
|
||||
var rings = currentWallet.getRings(hash)
|
||||
if (rings)
|
||||
rings = rings.replace(/\|/g, '\n')
|
||||
informationPopup.title = "Transaction details";
|
||||
informationPopup.text = buildTxDetailsString(hash,paymentId,tx_key,tx_note,destinations);
|
||||
informationPopup.text = buildTxDetailsString(hash,paymentId,tx_key,tx_note,destinations, rings);
|
||||
informationPopup.open();
|
||||
informationPopup.onCloseCallback = null
|
||||
}
|
||||
|
|
|
@ -230,6 +230,9 @@ eval make -C $MONERO_DIR/build/$BUILD_TYPE/contrib/epee all install
|
|||
# install easylogging
|
||||
eval make -C $MONERO_DIR/build/$BUILD_TYPE/external/easylogging++ all install
|
||||
|
||||
# install lmdb
|
||||
eval make -C $MONERO_DIR/build/$BUILD_TYPE/external/db_drivers/liblmdb all install
|
||||
|
||||
# Install libunbound
|
||||
echo "Installing libunbound..."
|
||||
pushd $MONERO_DIR/build/$BUILD_TYPE/external/unbound
|
||||
|
|
17
main.qml
17
main.qml
|
@ -100,6 +100,7 @@ ApplicationWindow {
|
|||
if(seq === "Ctrl+S") middlePanel.state = "Transfer"
|
||||
else if(seq === "Ctrl+R") middlePanel.state = "Receive"
|
||||
else if(seq === "Ctrl+K") middlePanel.state = "TxKey"
|
||||
else if(seq === "Ctrl+S") middlePanel.state = "SharedRingDB"
|
||||
else if(seq === "Ctrl+H") middlePanel.state = "History"
|
||||
else if(seq === "Ctrl+B") middlePanel.state = "AddressBook"
|
||||
else if(seq === "Ctrl+M") middlePanel.state = "Mining"
|
||||
|
@ -111,7 +112,8 @@ ApplicationWindow {
|
|||
if(middlePanel.state === "Dashboard") middlePanel.state = "Transfer"
|
||||
else if(middlePanel.state === "Transfer") middlePanel.state = "Receive"
|
||||
else if(middlePanel.state === "Receive") middlePanel.state = "TxKey"
|
||||
else if(middlePanel.state === "TxKey") middlePanel.state = "History"
|
||||
else if(middlePanel.state === "TxKey") middlePanel.state = "SharedRingDB"
|
||||
else if(middlePanel.state === "SharedRingDB") middlePanel.state = "History"
|
||||
else if(middlePanel.state === "History") middlePanel.state = "AddressBook"
|
||||
else if(middlePanel.state === "AddressBook") middlePanel.state = "Mining"
|
||||
else if(middlePanel.state === "Mining") middlePanel.state = "Sign"
|
||||
|
@ -121,7 +123,8 @@ ApplicationWindow {
|
|||
if(middlePanel.state === "Settings") middlePanel.state = "Transfer"
|
||||
else if(middlePanel.state === "Transfer") middlePanel.state = "Receive"
|
||||
else if(middlePanel.state === "Receive") middlePanel.state = "TxKey"
|
||||
else if(middlePanel.state === "TxKey") middlePanel.state = "History"
|
||||
else if(middlePanel.state === "TxKey") middlePanel.state = "SharedRingDB"
|
||||
else if(middlePanel.state === "SharedRingDB") middlePanel.state = "History"
|
||||
else if(middlePanel.state === "History") middlePanel.state = "AddressBook"
|
||||
else if(middlePanel.state === "AddressBook") middlePanel.state = "Sign"
|
||||
else if(middlePanel.state === "Sign") middlePanel.state = "Settings"
|
||||
|
@ -132,7 +135,8 @@ ApplicationWindow {
|
|||
else if(middlePanel.state === "Sign") middlePanel.state = "Mining"
|
||||
else if(middlePanel.state === "Mining") middlePanel.state = "AddressBook"
|
||||
else if(middlePanel.state === "AddressBook") middlePanel.state = "History"
|
||||
else if(middlePanel.state === "History") middlePanel.state = "TxKey"
|
||||
else if(middlePanel.state === "History") middlePanel.state = "SharedRingDB"
|
||||
else if(middlePanel.state === "SharedRingDB") middlePanel.state = "TxKey"
|
||||
else if(middlePanel.state === "TxKey") middlePanel.state = "Receive"
|
||||
else if(middlePanel.state === "Receive") middlePanel.state = "Transfer"
|
||||
else if(middlePanel.state === "Transfer") middlePanel.state = "Dashboard"
|
||||
|
@ -140,7 +144,8 @@ ApplicationWindow {
|
|||
if(middlePanel.state === "Settings") middlePanel.state = "Sign"
|
||||
else if(middlePanel.state === "Sign") middlePanel.state = "AddressBook"
|
||||
else if(middlePanel.state === "AddressBook") middlePanel.state = "History"
|
||||
else if(middlePanel.state === "History") middlePanel.state = "TxKey"
|
||||
else if(middlePanel.state === "History") middlePanel.state = "SharedRingDB"
|
||||
else if(middlePanel.state === "SharedRingDB") middlePanel.state = "TxKey"
|
||||
else if(middlePanel.state === "TxKey") middlePanel.state = "Receive"
|
||||
else if(middlePanel.state === "Receive") middlePanel.state = "Transfer"
|
||||
else if(middlePanel.state === "Transfer") middlePanel.state = "Settings"
|
||||
|
@ -998,6 +1003,9 @@ ApplicationWindow {
|
|||
property bool useRemoteNode: false
|
||||
property string remoteNodeAddress: ""
|
||||
property string bootstrapNodeAddress: ""
|
||||
property bool segregatePreForkOutputs: true
|
||||
property bool keyReuseMitigation2: true
|
||||
property int segregationHeight: 0
|
||||
}
|
||||
|
||||
// Information dialog
|
||||
|
@ -1280,6 +1288,7 @@ ApplicationWindow {
|
|||
onTransferClicked: { middlePanel.state = "Transfer"; if(isMobile) hideMenu(); updateBalance(); }
|
||||
onReceiveClicked: { middlePanel.state = "Receive"; if(isMobile) hideMenu(); updateBalance(); }
|
||||
onTxkeyClicked: { middlePanel.state = "TxKey"; if(isMobile) hideMenu(); updateBalance(); }
|
||||
onSharedringdbClicked: { middlePanel.state = "SharedRingDB"; if(isMobile) hideMenu(); updateBalance(); }
|
||||
onHistoryClicked: { middlePanel.state = "History"; if(isMobile) hideMenu(); updateBalance(); }
|
||||
onAddressBookClicked: { middlePanel.state = "AddressBook"; if(isMobile) hideMenu(); updateBalance(); }
|
||||
onMiningClicked: { middlePanel.state = "Mining"; if(isMobile) hideMenu(); updateBalance(); }
|
||||
|
|
|
@ -102,6 +102,7 @@ ios:arm64 {
|
|||
!ios:!android {
|
||||
LIBS += -L$$WALLET_ROOT/lib \
|
||||
-lwallet_merged \
|
||||
-llmdb \
|
||||
-lepee \
|
||||
-lunbound \
|
||||
-leasylogging \
|
||||
|
@ -111,6 +112,7 @@ android {
|
|||
message("Host is Android")
|
||||
LIBS += -L$$WALLET_ROOT/lib \
|
||||
-lwallet_merged \
|
||||
-llmdb \
|
||||
-lepee \
|
||||
-lunbound \
|
||||
-leasylogging
|
||||
|
@ -129,6 +131,7 @@ ios {
|
|||
CONFIG += arm64
|
||||
LIBS += -L$$WALLET_ROOT/lib-ios \
|
||||
-lwallet_merged \
|
||||
-llmdb \
|
||||
-lepee \
|
||||
-lunbound \
|
||||
-leasylogging
|
||||
|
|
420
pages/SharedRingDB.qml
Normal file
420
pages/SharedRingDB.qml
Normal file
|
@ -0,0 +1,420 @@
|
|||
// Copyright (c) 2018, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Dialogs 1.2
|
||||
|
||||
import "../components"
|
||||
import moneroComponents.Clipboard 1.0
|
||||
|
||||
Rectangle {
|
||||
|
||||
color: "#F0EEEE"
|
||||
|
||||
Clipboard { id: clipboard }
|
||||
|
||||
function validHex32(s) {
|
||||
if (s.length != 64)
|
||||
return false
|
||||
for (var i = 0; i < s.length; ++i)
|
||||
if ("0123456789abcdefABCDEF".indexOf(s[i]) == -1)
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
function validRing(str, relative) {
|
||||
var outs = str.split(" ");
|
||||
if (outs.length == 0)
|
||||
return false
|
||||
for (var i = 1; i < outs.length; ++i) {
|
||||
if (relative) {
|
||||
if (outs[i] <= 0)
|
||||
return false
|
||||
}
|
||||
else {
|
||||
if (outs[i] <= outs[i-1])
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/* main layout */
|
||||
ColumnLayout {
|
||||
id: mainLayout
|
||||
anchors.margins: 40
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: 20
|
||||
property int labelWidth: 120
|
||||
property int editWidth: 400
|
||||
property int lineEditFontSize: 12
|
||||
|
||||
MessageDialog {
|
||||
id: sharedRingDBDialog
|
||||
standardButtons: StandardButton.Ok
|
||||
}
|
||||
|
||||
Text {
|
||||
text: qsTr("This page allows you to interact with the shared ring database.<br>" +
|
||||
"This database is meant for use by Monero wallets as well as wallets from Monero clones which reuse the Monero keys.") + translationManager.emptyString
|
||||
wrapMode: Text.Wrap
|
||||
Layout.fillWidth: true;
|
||||
}
|
||||
|
||||
Text {
|
||||
textFormat: Text.RichText
|
||||
text: "<style type='text/css'>a {text-decoration: none; color: #FF6C3C; font-size: 14px;}</style>" +
|
||||
"<font size='+2'>" + qsTr("Blackballed outputs") + "</font>" + "<font size='2'> (</font><a href='#'>" + qsTr("help") + "</a><font size='2'>)</font><br>" +
|
||||
qsTr("This sets which outputs are known to be spent, and thus not to be used as privacy placeholders in ring signatures.<br>") +
|
||||
qsTr("You should only have to load a file when you want to refresh the list. Manual adding/removing is possible if needed.") + translationManager.emptyString
|
||||
wrapMode: Text.Wrap
|
||||
Layout.fillWidth: true;
|
||||
onLinkActivated: {
|
||||
sharedRingDBDialog.title = qsTr("Blackballed outputs") + translationManager.emptyString;
|
||||
sharedRingDBDialog.text = qsTr(
|
||||
"In order to obscure which inputs in a Monero transaction are being spent, a third party should not be able " +
|
||||
"to tell which inputs in a ring are already known to be spent. Being able to do so would weaken the protection " +
|
||||
"afforded by ring signatures. If all but one of the inputs are known to be already spent, then the input being " +
|
||||
"actually spent becomes apparent, thereby nullifying the effect of ring signatures, one of the three main layers " +
|
||||
"of privacy protection Monero uses.<br>" +
|
||||
"To help transactions avoid those inputs, a list of known spent ones can be used to avoid using them in new " +
|
||||
"transactions. Such a list is maintained by the Monero project and is available on the getmonero.org website, " +
|
||||
"and you can import this list here.<br>" +
|
||||
"Alternatively, you can scan the blockchain (and the blockchain of key-reusing Monero clones) yourself " +
|
||||
"using the monero-blockchain-blackball tool to create a list of known spent outputs.<br>"
|
||||
)
|
||||
sharedRingDBDialog.icon = StandardIcon.Information
|
||||
sharedRingDBDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: loadBlackballFileRow
|
||||
anchors.topMargin: 17
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
FileDialog {
|
||||
id: loadBlackballFileDialog
|
||||
title: qsTr("Please choose a file to load blackballed outputs from") + translationManager.emptyString;
|
||||
folder: "file://"
|
||||
nameFilters: [ "*"]
|
||||
|
||||
onAccepted: {
|
||||
loadBlackballFileLine.text = walletManager.urlToLocalPath(loadBlackballFileDialog.fileUrl)
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
id: selectBlackballFileButton
|
||||
anchors.rightMargin: 17 * scaleRatio
|
||||
text: qsTr("Select") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
enabled: true
|
||||
onClicked: {
|
||||
loadBlackballFileDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
LineEdit {
|
||||
id: loadBlackballFileLine
|
||||
anchors.left: selectBlackballFileButton.right
|
||||
anchors.right: loadBlackballFileButton.left
|
||||
placeholderText: qsTr("Filename with outputs to blackball") + translationManager.emptyString;
|
||||
readOnly: false
|
||||
Layout.fillWidth: true
|
||||
|
||||
IconButton {
|
||||
imageSource: "../images/copyToClipboard.png"
|
||||
onClicked: {
|
||||
if (loadBlackballFileLine.text.length > 0) {
|
||||
clipboard.setText(loadBlackballFileLine.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
id: loadBlackballFileButton
|
||||
anchors.right: parent.right
|
||||
text: qsTr("Load") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
enabled: !!appWindow.currentWallet
|
||||
onClicked: appWindow.currentWallet.blackballOutputs(walletManager.urlToLocalPath(loadBlackballFileDialog.fileUrl), true)
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
fontSize: 14
|
||||
text: qsTr("Or manually blackball or unblackball a single output:") + translationManager.emptyString
|
||||
width: mainLayout.labelWidth
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
LineEdit {
|
||||
id: blackballOutputLine
|
||||
fontSize: mainLayout.lineEditFontSize
|
||||
placeholderText: qsTr("Paste output public key") + translationManager.emptyString
|
||||
readOnly: false
|
||||
width: mainLayout.editWidth
|
||||
Layout.fillWidth: true
|
||||
|
||||
IconButton {
|
||||
imageSource: "../images/copyToClipboard.png"
|
||||
onClicked: {
|
||||
if (blackballOutputLine.text.length > 0) {
|
||||
clipboard.setText(blackballOutputLine.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
id: blackballButton
|
||||
text: qsTr("Blackball") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
enabled: !!appWindow.currentWallet && validHex32(blackballOutputLine.text)
|
||||
onClicked: appWindow.currentWallet.blackballOutput(blackballOutputLine.text)
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
id: unblackballButton
|
||||
anchors.right: parent.right
|
||||
text: qsTr("Unblackball") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
enabled: !!appWindow.currentWallet && validHex32(blackballOutputLine.text)
|
||||
onClicked: appWindow.currentWallet.unblackballOutput(blackballOutputLine.text)
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
textFormat: Text.RichText
|
||||
text: "<style type='text/css'>a {text-decoration: none; color: #FF6C3C; font-size: 14px;}</style>" +
|
||||
"<font size='+2'>" + qsTr("Rings") + "</font>" + "<font size='2'> (</font><a href='#'>" + qsTr("help") + "</a><font size='2'>)</font><br>" +
|
||||
qsTr("This records rings used by outputs spent on Monero on a key reusing chain, so that the same ring may be reused to avoid privacy issues.") + translationManager.emptyString
|
||||
wrapMode: Text.Wrap
|
||||
Layout.fillWidth: true;
|
||||
onLinkActivated: {
|
||||
sharedRingDBDialog.title = qsTr("Rings") + translationManager.emptyString;
|
||||
sharedRingDBDialog.text = qsTr(
|
||||
"In order to avoid nullifying the protection afforded by Monero's ring signatures, an output should not " +
|
||||
"be spent with different rings on different blockchains. While this is normally not a concern, it can become one " +
|
||||
"when a key-reusing Monero clone allows you do spend existing outputs. In this case, you need to ensure this " +
|
||||
"existing outputs uses the same ring on both chains.<br>" +
|
||||
"This will be done automatically by Monero and any key-reusing software which is not trying to actively strip " +
|
||||
"you of your privacy.<br>" +
|
||||
"If you are using a key-reusing Monero clone too, and this clone does not include this protection, you can still " +
|
||||
"ensure your transactions are protected by spending on the clone first, then manually adding the ring on this page, " +
|
||||
"which allows you to then spend your Monero safely.<br>" +
|
||||
"If you do not use a key-reusing Monero clone without these safety features, then you do not need to do anything " +
|
||||
"as it is all automated.<br>"
|
||||
)
|
||||
sharedRingDBDialog.icon = StandardIcon.Information
|
||||
sharedRingDBDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
LineEdit {
|
||||
id: keyImageLine
|
||||
fontSize: mainLayout.lineEditFontSize
|
||||
placeholderText: qsTr("Paste key image") + translationManager.emptyString
|
||||
readOnly: false
|
||||
width: mainLayout.editWidth
|
||||
Layout.fillWidth: true
|
||||
|
||||
IconButton {
|
||||
imageSource: "../images/copyToClipboard.png"
|
||||
onClicked: {
|
||||
if (keyImageLine.text.length > 0) {
|
||||
clipboard.setText(keyImageLine.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
StandardButton {
|
||||
id: getRingButton
|
||||
text: qsTr("Get Ring") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
enabled: !!appWindow.currentWallet && validHex32(keyImageLine.text)
|
||||
onClicked: {
|
||||
var ring = appWindow.currentWallet.getRing(keyImageLine.text)
|
||||
if (ring === "")
|
||||
{
|
||||
getRingLine.text = qsTr("No ring found");
|
||||
}
|
||||
else
|
||||
{
|
||||
getRingLine.text = ring;
|
||||
}
|
||||
}
|
||||
}
|
||||
LineEdit {
|
||||
id: getRingLine
|
||||
fontSize: mainLayout.lineEditFontSize
|
||||
placeholderText: qsTr("") + translationManager.emptyString
|
||||
readOnly: true
|
||||
width: mainLayout.editWidth
|
||||
Layout.fillWidth: true
|
||||
|
||||
IconButton {
|
||||
imageSource: "../images/copyToClipboard.png"
|
||||
onClicked: {
|
||||
if (getRingLine.text.length > 0) {
|
||||
clipboard.setText(getRingLine.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
CheckBox {
|
||||
id: setRingRelative
|
||||
checked: true
|
||||
text: qsTr("Relative") + translationManager.emptyString
|
||||
checkedIcon: "../images/checkedVioletIcon.png"
|
||||
uncheckedIcon: "../images/uncheckedIcon.png"
|
||||
}
|
||||
LineEdit {
|
||||
id: setRingLine
|
||||
fontSize: mainLayout.lineEditFontSize
|
||||
placeholderText: qsTr("") + translationManager.emptyString
|
||||
readOnly: false
|
||||
width: mainLayout.editWidth
|
||||
|
||||
IconButton {
|
||||
imageSource: "../images/copyToClipboard.png"
|
||||
onClicked: {
|
||||
if (getRingLine.text.length > 0) {
|
||||
clipboard.setText(getRingLine.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
StandardButton {
|
||||
id: setRingButton
|
||||
text: qsTr("Set Ring") + translationManager.emptyString
|
||||
shadowReleasedColor: "#FF4304"
|
||||
shadowPressedColor: "#B32D00"
|
||||
releasedColor: "#FF6C3C"
|
||||
pressedColor: "#FF4304"
|
||||
enabled: !!appWindow.currentWallet && validHex32(keyImageLine.text) && validRing(setRingLine.text.trim(), setRingRelative.checked)
|
||||
onClicked: {
|
||||
var outs = setRingLine.text.trim()
|
||||
appWindow.currentWallet.setRing(keyImageLine.text, outs, setRingRelative.checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: segregatePreForkOutputs
|
||||
checked: persistentSettings.segregatePreForkOutputs
|
||||
text: qsTr("I intend to spend on key-reusing fork(s)") + translationManager.emptyString
|
||||
checkedIcon: "../images/checkedVioletIcon.png"
|
||||
uncheckedIcon: "../images/uncheckedIcon.png"
|
||||
onClicked: {
|
||||
persistentSettings.segregatePreForkOutputs = segregatePreForkOutputs.checked
|
||||
if (appWindow.currentWallet) {
|
||||
appWindow.currentWallet.segregatePreForkOutputs(segregatePreForkOutputs.checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
CheckBox {
|
||||
id: keyReuseMitigation2
|
||||
checked: persistentSettings.keyReuseMitigation2
|
||||
text: qsTr("I might want to spend on key-reusing fork(s)") + translationManager.emptyString
|
||||
checkedIcon: "../images/checkedVioletIcon.png"
|
||||
uncheckedIcon: "../images/uncheckedIcon.png"
|
||||
onClicked: {
|
||||
persistentSettings.keyReuseMitigation2 = keyReuseMitigation2.checked
|
||||
if (appWindow.currentWallet) {
|
||||
appWindow.currentWallet.keyReuseMitigation2(keyReuseMitigation2.checked)
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
id: segregationHeightRow
|
||||
anchors.topMargin: 17
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Label {
|
||||
id: segregationHeightLabel
|
||||
fontSize: 14
|
||||
text: qsTr("Segregation height:") + translationManager.emptyString
|
||||
}
|
||||
LineEdit {
|
||||
id: segregationHeightLine
|
||||
readOnly: false
|
||||
Layout.fillWidth: true
|
||||
validator: IntValidator { bottom: 0 }
|
||||
onEditingFinished: {
|
||||
persistentSettings.segregationHeight = segregationHeightLine.text
|
||||
if (appWindow.currentWallet) {
|
||||
appWindow.currentWallet.segregationHeight(segregationHeightLine.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onPageCompleted() {
|
||||
console.log("RingDB page loaded");
|
||||
appWindow.currentWallet.segregatePreForkOutputs(persistentSettings.segregatePreForkOutputs)
|
||||
appWindow.currentWallet.segregationHeight(persistentSettings.segregationHeight)
|
||||
segregationHeightLine.text = persistentSettings.segregationHeight
|
||||
appWindow.currentWallet.keyReuseMitigation2(persistentSettings.keyReuseMitigation2)
|
||||
}
|
||||
|
||||
}
|
1
qml.qrc
1
qml.qrc
|
@ -140,6 +140,7 @@
|
|||
<file>wizard/utils.js</file>
|
||||
<file>pages/Receive.qml</file>
|
||||
<file>pages/TxKey.qml</file>
|
||||
<file>pages/SharedRingDB.qml</file>
|
||||
<file>components/IconButton.qml</file>
|
||||
<file>components/PasswordDialog.qml</file>
|
||||
<file>components/NewPasswordDialog.qml</file>
|
||||
|
|
|
@ -733,6 +733,117 @@ QString Wallet::getWalletLogPath() const
|
|||
#endif
|
||||
}
|
||||
|
||||
bool Wallet::blackballOutput(const QString &pubkey)
|
||||
{
|
||||
QList<QString> list;
|
||||
list.push_back(pubkey);
|
||||
return blackballOutputs(list, true);
|
||||
}
|
||||
|
||||
bool Wallet::blackballOutputs(const QList<QString> &pubkeys, bool add)
|
||||
{
|
||||
std::vector<std::string> std_pubkeys;
|
||||
foreach (const QString &pubkey, pubkeys) {
|
||||
std_pubkeys.push_back(pubkey.toStdString());
|
||||
}
|
||||
return m_walletImpl->blackballOutputs(std_pubkeys, add);
|
||||
}
|
||||
|
||||
bool Wallet::blackballOutputs(const QString &filename, bool add)
|
||||
{
|
||||
QFile file(filename);
|
||||
|
||||
try {
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
return false;
|
||||
QList<QString> outputs;
|
||||
QTextStream in(&file);
|
||||
while (!in.atEnd()) {
|
||||
outputs.push_back(in.readLine());
|
||||
}
|
||||
file.close();
|
||||
return blackballOutputs(outputs, add);
|
||||
}
|
||||
catch (const std::exception &e) {
|
||||
file.close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Wallet::unblackballOutput(const QString &pubkey)
|
||||
{
|
||||
return m_walletImpl->unblackballOutput(pubkey.toStdString());
|
||||
}
|
||||
|
||||
QString Wallet::getRing(const QString &key_image)
|
||||
{
|
||||
std::vector<uint64_t> cring;
|
||||
if (!m_walletImpl->getRing(key_image.toStdString(), cring))
|
||||
return "";
|
||||
QString ring = "";
|
||||
for (uint64_t out: cring)
|
||||
{
|
||||
if (!ring.isEmpty())
|
||||
ring = ring + " ";
|
||||
QString s;
|
||||
s.setNum(out);
|
||||
ring = ring + s;
|
||||
}
|
||||
return ring;
|
||||
}
|
||||
|
||||
QString Wallet::getRings(const QString &txid)
|
||||
{
|
||||
std::vector<std::pair<std::string, std::vector<uint64_t>>> crings;
|
||||
if (!m_walletImpl->getRings(txid.toStdString(), crings))
|
||||
return "";
|
||||
QString ring = "";
|
||||
for (const auto &cring: crings)
|
||||
{
|
||||
if (!ring.isEmpty())
|
||||
ring = ring + "|";
|
||||
ring = ring + QString::fromStdString(cring.first) + " absolute";
|
||||
for (uint64_t out: cring.second)
|
||||
{
|
||||
ring = ring + " ";
|
||||
QString s;
|
||||
s.setNum(out);
|
||||
ring = ring + s;
|
||||
}
|
||||
}
|
||||
return ring;
|
||||
}
|
||||
|
||||
bool Wallet::setRing(const QString &key_image, const QString &ring, bool relative)
|
||||
{
|
||||
std::vector<uint64_t> cring;
|
||||
QStringList strOuts = ring.split(" ");
|
||||
foreach(QString str, strOuts)
|
||||
{
|
||||
uint64_t out;
|
||||
bool ok;
|
||||
out = str.toULong(&ok);
|
||||
if (ok)
|
||||
cring.push_back(out);
|
||||
}
|
||||
return m_walletImpl->setRing(key_image.toStdString(), cring, relative);
|
||||
}
|
||||
|
||||
void Wallet::segregatePreForkOutputs(bool segregate)
|
||||
{
|
||||
m_walletImpl->segregatePreForkOutputs(segregate);
|
||||
}
|
||||
|
||||
void Wallet::segregationHeight(quint64 height)
|
||||
{
|
||||
m_walletImpl->segregationHeight(height);
|
||||
}
|
||||
|
||||
void Wallet::keyReuseMitigation2(bool mitigation)
|
||||
{
|
||||
m_walletImpl->keyReuseMitigation2(mitigation);
|
||||
}
|
||||
|
||||
Wallet::Wallet(Monero::Wallet *w, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_walletImpl(w)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <QObject>
|
||||
#include <QTime>
|
||||
#include <QMutex>
|
||||
#include <QList>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
#include "wallet/api/wallet2_api.h" // we need to have an access to the Monero::Wallet::Status enum here;
|
||||
|
@ -276,6 +277,22 @@ public:
|
|||
QString getDaemonLogPath() const;
|
||||
QString getWalletLogPath() const;
|
||||
|
||||
// Blackalled outputs
|
||||
Q_INVOKABLE bool blackballOutput(const QString &pubkey);
|
||||
Q_INVOKABLE bool blackballOutputs(const QList<QString> &pubkeys, bool add);
|
||||
Q_INVOKABLE bool blackballOutputs(const QString &filename, bool add);
|
||||
Q_INVOKABLE bool unblackballOutput(const QString &pubkey);
|
||||
|
||||
// Rings
|
||||
Q_INVOKABLE QString getRing(const QString &key_image);
|
||||
Q_INVOKABLE QString getRings(const QString &txid);
|
||||
Q_INVOKABLE bool setRing(const QString &key_image, const QString &ring, bool relative);
|
||||
|
||||
// key reuse mitigation options
|
||||
Q_INVOKABLE void segregatePreForkOutputs(bool segregate);
|
||||
Q_INVOKABLE void segregationHeight(quint64 height);
|
||||
Q_INVOKABLE void keyReuseMitigation2(bool mitigation);
|
||||
|
||||
// TODO: setListenter() when it implemented in API
|
||||
signals:
|
||||
// emitted on every event happened with wallet
|
||||
|
|
Loading…
Reference in a new issue