Add a page for checking payments via the tx key

This commit is contained in:
moneromooo.monero 2016-11-05 14:58:49 +00:00
parent c9bb2f5718
commit 06e94e2fbe
7 changed files with 315 additions and 0 deletions

View file

@ -42,6 +42,7 @@ Rectangle {
signal historyClicked() signal historyClicked()
signal transferClicked() signal transferClicked()
signal receiveClicked() signal receiveClicked()
signal txkeyClicked()
signal settingsClicked() signal settingsClicked()
signal addressBookClicked() signal addressBookClicked()
signal miningClicked() signal miningClicked()
@ -54,6 +55,7 @@ Rectangle {
else if(pos === "Receive") menuColumn.previousButton = receiveButton else if(pos === "Receive") menuColumn.previousButton = receiveButton
else if(pos === "AddressBook") menuColumn.previousButton = addressBookButton else if(pos === "AddressBook") menuColumn.previousButton = addressBookButton
else if(pos === "Mining") menuColumn.previousButton = miningButton else if(pos === "Mining") menuColumn.previousButton = miningButton
else if(pos === "TxKey") menuColumn.previousButton = txkeyButton
else if(pos === "Settings") menuColumn.previousButton = settingsButton else if(pos === "Settings") menuColumn.previousButton = settingsButton
menuColumn.previousButton.checked = true menuColumn.previousButton.checked = true
@ -256,6 +258,21 @@ Rectangle {
} }
} }
// ------------- TxKey tab ---------------
MenuButton {
id: txkeyButton
anchors.left: parent.left
anchors.right: parent.right
text: qsTr("Tx Key") + translationManager.emptyString
symbol: qsTr("K") + translationManager.emptyString
dotColor: "#AAFFBB"
onClicked: {
parent.previousButton.checked = false
parent.previousButton = txkeyButton
panel.txkeyClicked()
}
}
Rectangle { Rectangle {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right

View file

@ -45,12 +45,14 @@ Rectangle {
property Transfer transferView: Transfer { } property Transfer transferView: Transfer { }
property Receive receiveView: Receive { } property Receive receiveView: Receive { }
property TxKey txkeyView: TxKey { }
property History historyView: History { } property History historyView: History { }
property Settings settingsView: Settings { } property Settings settingsView: Settings { }
signal paymentClicked(string address, string paymentId, double amount, int mixinCount, int priority) signal paymentClicked(string address, string paymentId, double amount, int mixinCount, int priority)
signal generatePaymentIdInvoked() signal generatePaymentIdInvoked()
signal checkPaymentClicked(string address, string txid, string txkey);
// Disable transfer page if daemon isnt fully synced // Disable transfer page if daemon isnt fully synced
enabled: (currentView !== transferView || appWindow.daemonSynced) enabled: (currentView !== transferView || appWindow.daemonSynced)
@ -109,6 +111,9 @@ Rectangle {
}, State { }, State {
name: "Receive" name: "Receive"
PropertyChanges { target: root; currentView: receiveView } PropertyChanges { target: root; currentView: receiveView }
}, State {
name: "TxKey"
PropertyChanges { target: root; currentView: txkeyView }
}, State { }, State {
name: "AddressBook" name: "AddressBook"
PropertyChanges { /*TODO*/ } PropertyChanges { /*TODO*/ }

View file

@ -149,6 +149,7 @@ ApplicationWindow {
middlePanel.paymentClicked.connect(handlePayment); middlePanel.paymentClicked.connect(handlePayment);
// basicPanel.paymentClicked.connect(handlePayment); // basicPanel.paymentClicked.connect(handlePayment);
middlePanel.checkPaymentClicked.connect(handleCheckPayment);
// currentWallet is defined on daemon address change - close/reopen // currentWallet is defined on daemon address change - close/reopen
// TODO: strict comparison here (!==) causes crash after passwordDialog on previously crashed unsynced wallets // TODO: strict comparison here (!==) causes crash after passwordDialog on previously crashed unsynced wallets
@ -432,6 +433,53 @@ ApplicationWindow {
currentWallet.disposeTransaction(transaction) currentWallet.disposeTransaction(transaction)
} }
// called on "checkPayment"
function handleCheckPayment(address, txid, txkey) {
console.log("Checking payment: ")
console.log("\taddress: ", address,
", txid: ", txid,
", txkey: ", txkey);
var result = walletManager.checkPayment(address, txid, txkey, persistentSettings.daemon_address);
var results = result.split("|");
if (results.length < 4) {
informationPopup.title = qsTr("Error") + translationManager.emptyString;
informationPopup.text = "internal error";
informationPopup.icon = StandardIcon.Critical
informationPopup.open()
return
}
var success = results[0] == "true";
var received = results[1]
var height = results[2]
var error = results[3]
if (success) {
informationPopup.title = qsTr("Payment check") + translationManager.emptyString;
informationPopup.icon = StandardIcon.Information
if (received > 0) {
received = received / 1e12
if (height == 0) {
informationPopup.text = qsTr("This address received %1 monero, but the transaction is not yet mined").arg(received);
}
else {
var dCurrentBlock = currentWallet.daemonBlockChainHeight();
var confirmations = dCurrentBlock - height
informationPopup.text = qsTr("This address received %1 monero, with %2 confirmations").arg(received).arg(confirmations);
}
}
else {
informationPopup.text = qsTr("This address received nothing");
}
}
else {
informationPopup.title = qsTr("Error") + translationManager.emptyString;
informationPopup.text = error;
informationPopup.icon = StandardIcon.Critical
}
informationPopup.open()
}
// blocks UI if wallet can't be opened or no connection to the daemon // blocks UI if wallet can't be opened or no connection to the daemon
function enableUI(enable) { function enableUI(enable) {
middlePanel.enabled = enable; middlePanel.enabled = enable;
@ -628,6 +676,7 @@ ApplicationWindow {
onHistoryClicked: middlePanel.state = "History" onHistoryClicked: middlePanel.state = "History"
onTransferClicked: middlePanel.state = "Transfer" onTransferClicked: middlePanel.state = "Transfer"
onReceiveClicked: middlePanel.state = "Receive" onReceiveClicked: middlePanel.state = "Receive"
onTxkeyClicked: middlePanel.state = "TxKey"
onAddressBookClicked: middlePanel.state = "AddressBook" onAddressBookClicked: middlePanel.state = "AddressBook"
onMiningClicked: middlePanel.state = "Minning" onMiningClicked: middlePanel.state = "Minning"
onSettingsClicked: middlePanel.state = "Settings" onSettingsClicked: middlePanel.state = "Settings"

231
pages/TxKey.qml Normal file
View file

@ -0,0 +1,231 @@
// Copyright (c) 2014-2015, 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 "../components"
import moneroComponents.Clipboard 1.0
Rectangle {
color: "#F0EEEE"
property alias addressText : addressLine.text
property alias txIdText : txIdLine.text
property alias txKeyText : txKeyLine.text
Clipboard { id: clipboard }
function checkAddress(address, testnet) {
return walletManager.addressValid(address, testnet)
}
function check256(str) {
if (str.length != 64)
return false;
for (var i = 0; i < 64; ++i) {
if (str[i] >= '0' && str[i] <= '9')
continue;
if (str[i] >= 'a' && str[i] <= 'z')
continue;
if (str[i] >= 'A' && str[i] <= 'Z')
continue;
return false;
}
return true;
}
function checkTxID(txid) {
return check256(txid)
}
function checkTxKey(txid) {
return check256(txid)
}
/* 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
RowLayout {
ColumnLayout {
Text {
text: qsTr("You can verify a third party made a payment by supplying:") + translationManager.emptyString
wrapMode: Text.Wrap
}
Text {
text: qsTr(" - the recipient address,") + translationManager.emptyString
wrapMode: Text.Wrap
}
Text {
text: qsTr(" - the transaction ID,") + translationManager.emptyString
wrapMode: Text.Wrap
}
Text {
text: qsTr(" - the tx secret key supplied by the sender") + translationManager.emptyString
wrapMode: Text.Wrap
}
Text {
text: qsTr("If a payment was made up of several tranactions, each transaction must be checked, and the results added") + translationManager.emptyString
wrapMode: Text.Wrap
}
}
}
RowLayout {
id: addressRow
Label {
id: addressLabel
fontSize: 14
text: qsTr("Address") + translationManager.emptyString
width: mainLayout.labelWidth
}
LineEdit {
id: addressLine
fontSize: mainLayout.lineEditFontSize
placeholderText: qsTr("Recipient's wallet address") + translationManager.emptyString;
readOnly: false
width: mainLayout.editWidth
Layout.fillWidth: true
onTextChanged: cursorPosition = 0
IconButton {
imageSource: "../images/copyToClipboard.png"
onClicked: {
if (addressLine.text.length > 0) {
clipboard.setText(addressLine.text)
}
}
}
}
}
RowLayout {
id: txIdRow
Label {
id: txIdLabel
fontSize: 14
text: qsTr("Transaction ID") + translationManager.emptyString
width: mainLayout.labelWidth
}
LineEdit {
id: txIdLine
fontSize: mainLayout.lineEditFontSize
placeholderText: qsTr("Transaction ID here") + translationManager.emptyString
readOnly: false
width: mainLayout.editWidth
Layout.fillWidth: true
onTextChanged: cursorPosition = 0
IconButton {
imageSource: "../images/copyToClipboard.png"
onClicked: {
if (txIdLine.text.length > 0) {
clipboard.setText(txIdLine.text)
}
}
}
}
}
RowLayout {
id: txKeyRow
Label {
id: paymentIdLabel
fontSize: 14
text: qsTr("Transaction key") + translationManager.emptyString
width: mainLayout.labelWidth
}
LineEdit {
id: txKeyLine
fontSize: mainLayout.lineEditFontSize
placeholderText: qsTr("Transaction key here") + translationManager.emptyString;
readOnly: false
width: mainLayout.editWidth
Layout.fillWidth: true
IconButton {
imageSource: "../images/copyToClipboard.png"
onClicked: {
if (TxKeyLine.text.length > 0) {
clipboard.setText(TxKeyLine.text)
}
}
}
}
}
StandardButton {
id: checkButton
anchors.left: parent.left
anchors.top: txKeyRow.bottom
anchors.leftMargin: 17
anchors.topMargin: 17
width: 60
text: qsTr("CHECK") + translationManager.emptyString
shadowReleasedColor: "#FF4304"
shadowPressedColor: "#B32D00"
releasedColor: "#FF6C3C"
pressedColor: "#FF4304"
enabled: checkAddress(addressLine.text, appWindow.persistentSettings.testnet) && checkTxID(txIdLine.text) && checkTxKey(txKeyLine.text)
onClicked: {
console.log("TxKey: Check clicked: address " + addressLine.text + ", txid " << txIdLine.text + ", tx key " + txKeyLine.text);
root.checkPaymentClicked(addressLine.text, txIdLine.text, txKeyLine.text)
}
}
}
function onPageCompleted() {
console.log("TxKey page loaded");
}
}

View file

@ -112,6 +112,7 @@
<file>wizard/WizardMemoTextInput.qml</file> <file>wizard/WizardMemoTextInput.qml</file>
<file>wizard/utils.js</file> <file>wizard/utils.js</file>
<file>pages/Receive.qml</file> <file>pages/Receive.qml</file>
<file>pages/TxKey.qml</file>
<file>components/IconButton.qml</file> <file>components/IconButton.qml</file>
<file>lang/flags/italy.png</file> <file>lang/flags/italy.png</file>
<file>components/PasswordDialog.qml</file> <file>components/PasswordDialog.qml</file>

View file

@ -164,6 +164,16 @@ QString WalletManager::paymentIdFromAddress(const QString &address, bool testnet
return QString::fromStdString(Bitmonero::Wallet::paymentIdFromAddress(address.toStdString(), testnet)); return QString::fromStdString(Bitmonero::Wallet::paymentIdFromAddress(address.toStdString(), testnet));
} }
QString WalletManager::checkPayment(const QString &address, const QString &txid, const QString &txkey, const QString &daemon_address) const
{
uint64_t received = 0, height = 0;
std::string error = "";
bool ret = m_pimpl->checkPayment(address.toStdString(), txid.toStdString(), txkey.toStdString(), daemon_address.toStdString(), received, height, error);
// bypass qml being unable to pass structures without preposterous complexity
std::string result = std::string(ret ? "true" : "false") + "|" + QString::number(received).toStdString() + "|" + QString::number(height).toStdString() + "|" + error;
return QString::fromStdString(result);
}
void WalletManager::setLogLevel(int logLevel) void WalletManager::setLogLevel(int logLevel)
{ {
Bitmonero::WalletManagerFactory::setLogLevel(logLevel); Bitmonero::WalletManagerFactory::setLogLevel(logLevel);

View file

@ -93,6 +93,8 @@ public:
Q_INVOKABLE bool addressValid(const QString &address, bool testnet) const; Q_INVOKABLE bool addressValid(const QString &address, bool testnet) const;
Q_INVOKABLE QString paymentIdFromAddress(const QString &address, bool testnet) const; Q_INVOKABLE QString paymentIdFromAddress(const QString &address, bool testnet) const;
Q_INVOKABLE QString checkPayment(const QString &address, const QString &txid, const QString &txkey, const QString &daemon_address) const;
// QML missing such functionality, implementing these helpers here // QML missing such functionality, implementing these helpers here
Q_INVOKABLE QString urlToLocalPath(const QUrl &url) const; Q_INVOKABLE QString urlToLocalPath(const QUrl &url) const;
Q_INVOKABLE QUrl localPathToUrl(const QString &path) const; Q_INVOKABLE QUrl localPathToUrl(const QString &path) const;