This commit is contained in:
stoffu 2017-09-12 17:42:00 +09:00
parent da020fd0be
commit d79fe0f457
No known key found for this signature in database
GPG key ID: 41DAB8343A9EC012
8 changed files with 285 additions and 133 deletions

View file

@ -424,7 +424,7 @@ Rectangle {
id: txkeyButton id: txkeyButton
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
text: qsTr("Check payment") + translationManager.emptyString text: qsTr("Prove/check") + translationManager.emptyString
symbol: qsTr("K") + translationManager.emptyString symbol: qsTr("K") + translationManager.emptyString
dotColor: "#FFD781" dotColor: "#FFD781"
under: advancedButton under: advancedButton

View file

@ -65,7 +65,8 @@ Rectangle {
signal paymentClicked(string address, string paymentId, string amount, int mixinCount, int priority, string description) signal paymentClicked(string address, string paymentId, string amount, int mixinCount, int priority, string description)
signal sweepUnmixableClicked() signal sweepUnmixableClicked()
signal generatePaymentIdInvoked() signal generatePaymentIdInvoked()
signal checkPaymentClicked(string address, string txid, string txkey); signal getTxProofClicked(string txid, string address, string message);
signal checkTxProofClicked(string txid, string address, string message, string signature);
color: "#F0EEEE" color: "#F0EEEE"

View file

@ -243,7 +243,8 @@ ApplicationWindow {
currentWallet.connectionStatusChanged.disconnect(onWalletConnectionStatusChanged) currentWallet.connectionStatusChanged.disconnect(onWalletConnectionStatusChanged)
middlePanel.paymentClicked.disconnect(handlePayment); middlePanel.paymentClicked.disconnect(handlePayment);
middlePanel.sweepUnmixableClicked.disconnect(handleSweepUnmixable); middlePanel.sweepUnmixableClicked.disconnect(handleSweepUnmixable);
middlePanel.checkPaymentClicked.disconnect(handleCheckPayment); middlePanel.getTxProofClicked.disconnect(handleGetTxProof);
middlePanel.checkTxProofClicked.disconnect(handleCheckTxProof);
} }
currentWallet = undefined; currentWallet = undefined;
@ -275,7 +276,8 @@ ApplicationWindow {
currentWallet.connectionStatusChanged.connect(onWalletConnectionStatusChanged) currentWallet.connectionStatusChanged.connect(onWalletConnectionStatusChanged)
middlePanel.paymentClicked.connect(handlePayment); middlePanel.paymentClicked.connect(handlePayment);
middlePanel.sweepUnmixableClicked.connect(handleSweepUnmixable); middlePanel.sweepUnmixableClicked.connect(handleSweepUnmixable);
middlePanel.checkPaymentClicked.connect(handleCheckPayment); middlePanel.getTxProofClicked.connect(handleGetTxProof);
middlePanel.checkTxProofClicked.connect(handleCheckTxProof);
console.log("Recovering from seed: ", persistentSettings.is_recovering) console.log("Recovering from seed: ", persistentSettings.is_recovering)
@ -755,38 +757,54 @@ ApplicationWindow {
currentWallet.store(); currentWallet.store();
} }
// called on "checkPayment" // called on "getTxProof"
function handleCheckPayment(address, txid, txkey) { function handleGetTxProof(txid, address, message) {
console.log("Checking payment: ") console.log("Getting payment proof: ")
console.log("\taddress: ", address, console.log("\ttxid: ", txid,
", txid: ", txid, ", address: ", address,
", txkey: ", txkey); ", message: ", message);
var result = walletManager.checkPayment(address, txid, txkey, currentDaemonAddress); var result = currentWallet.getTxProof(txid, address, message);
var results = result.split("|"); informationPopup.title = qsTr("Payment proof") + translationManager.emptyString;
if (results.length < 4) { if (result.startsWith("error|")) {
informationPopup.title = qsTr("Error") + translationManager.emptyString; var errorString = result.split("|")[1];
informationPopup.text = "internal error"; informationPopup.text = qsTr("Couldn't generate a proof because of the following reason: \n") + errorString + translationManager.emptyString;
informationPopup.icon = StandardIcon.Critical informationPopup.icon = StandardIcon.Critical;
informationPopup.onCloseCallback = null } else {
informationPopup.open() informationPopup.text = result;
return informationPopup.icon = StandardIcon.Critical;
} }
var success = results[0] == "true"; informationPopup.onCloseCallback = null
var received = results[1] informationPopup.open()
var height = results[2] }
var error = results[3]
if (success) { // called on "checkTxProof"
informationPopup.title = qsTr("Payment check") + translationManager.emptyString; function handleCheckTxProof(txid, address, message, signature) {
console.log("Checking payment proof: ")
console.log("\ttxid: ", txid,
", address: ", address,
", message: ", message,
", signature: ", signature);
var result = currentWallet.checkTxProof(txid, address, message, signature);
var results = result.split("|");
if (results.length == 5 && results[0] == "true") {
var good = results[1] == "true";
var received = results[2];
var in_pool = results[3] == "true";
var confirmations = results[4];
informationPopup.title = qsTr("Payment proof check") + translationManager.emptyString;
informationPopup.icon = StandardIcon.Information informationPopup.icon = StandardIcon.Information
if (received > 0) { if (!good) {
informationPopup.text = qsTr("Bad signature");
informationPopup.icon = StandardIcon.Critical;
} else if (received > 0) {
received = received / 1e12 received = received / 1e12
if (height == 0) { if (in_pool) {
informationPopup.text = qsTr("This address received %1 monero, but the transaction is not yet mined").arg(received); informationPopup.text = qsTr("This address received %1 monero, but the transaction is not yet mined").arg(received);
} }
else { else {
var dCurrentBlock = currentWallet.daemonBlockChainHeight();
var confirmations = dCurrentBlock - height
informationPopup.text = qsTr("This address received %1 monero, with %2 confirmation(s).").arg(received).arg(confirmations); informationPopup.text = qsTr("This address received %1 monero, with %2 confirmation(s).").arg(received).arg(confirmations);
} }
} }
@ -796,9 +814,10 @@ ApplicationWindow {
} }
else { else {
informationPopup.title = qsTr("Error") + translationManager.emptyString; informationPopup.title = qsTr("Error") + translationManager.emptyString;
informationPopup.text = error; informationPopup.text = currentWallet.errorString;
informationPopup.icon = StandardIcon.Critical informationPopup.icon = StandardIcon.Critical
} }
informationPopup.onCloseCallback = null
informationPopup.open() informationPopup.open()
} }

View file

@ -37,9 +37,6 @@ import moneroComponents.Clipboard 1.0
Rectangle { Rectangle {
color: "#F0EEEE" color: "#F0EEEE"
property alias addressText : addressLine.text
property alias txIdText : txIdLine.text
property alias txKeyText : txKeyLine.text
Clipboard { id: clipboard } Clipboard { id: clipboard }
@ -47,10 +44,10 @@ Rectangle {
return walletManager.addressValid(address, testnet) return walletManager.addressValid(address, testnet)
} }
function check256(str) { function check256(str, length) {
if (str.length != 64) if (str.length != length)
return false; return false;
for (var i = 0; i < 64; ++i) { for (var i = 0; i < length; ++i) {
if (str[i] >= '0' && str[i] <= '9') if (str[i] >= '0' && str[i] <= '9')
continue; continue;
if (str[i] >= 'a' && str[i] <= 'z') if (str[i] >= 'a' && str[i] <= 'z')
@ -63,11 +60,12 @@ Rectangle {
} }
function checkTxID(txid) { function checkTxID(txid) {
return check256(txid) return check256(txid, 64)
} }
function checkTxKey(txid) { function checkSignature(signature) {
return check256(txid) return signature.startsWith("OutProofV") && check256(signature, 142) ||
signature.startsWith("InProofV") && check256(signature, 141)
} }
/* main layout */ /* main layout */
@ -83,93 +81,32 @@ Rectangle {
property int editWidth: 400 property int editWidth: 400
property int lineEditFontSize: 12 property int lineEditFontSize: 12
RowLayout { Text {
ColumnLayout { text: qsTr("Generate a proof of your incoming/outgoing payment by supplying the transaction ID, the recipient address and an optional message:") + translationManager.emptyString
wrapMode: Text.Wrap
Text { Layout.fillWidth: true;
text: qsTr("Verify that a third party made a payment by supplying:") + translationManager.emptyString
wrapMode: Text.Wrap
Layout.fillWidth: true;
}
Text {
text: qsTr(" - the recipient address") + translationManager.emptyString
wrapMode: Text.Wrap
Layout.fillWidth: true;
}
Text {
text: qsTr(" - the transaction ID") + translationManager.emptyString
wrapMode: Text.Wrap
Layout.fillWidth: true;
}
Text {
text: qsTr(" - the secret transaction key supplied by the sender") + translationManager.emptyString
wrapMode: Text.Wrap
Layout.fillWidth: true;
}
Text {
text: qsTr("If a payment had several transactions then each must be checked and the results combined.") + translationManager.emptyString
wrapMode: Text.Wrap
Layout.fillWidth: true;
}
}
} }
RowLayout { RowLayout {
id: addressRow
Label { 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 fontSize: 14
text: qsTr("Transaction ID") + translationManager.emptyString text: qsTr("Transaction ID") + translationManager.emptyString
width: mainLayout.labelWidth width: mainLayout.labelWidth
} }
LineEdit { LineEdit {
id: getProofTxIdLine
id: txIdLine
fontSize: mainLayout.lineEditFontSize fontSize: mainLayout.lineEditFontSize
placeholderText: qsTr("Paste tx ID") + translationManager.emptyString placeholderText: qsTr("Paste tx ID") + translationManager.emptyString
readOnly: false readOnly: false
width: mainLayout.editWidth width: mainLayout.editWidth
Layout.fillWidth: true Layout.fillWidth: true
onTextChanged: cursorPosition = 0
IconButton { IconButton {
imageSource: "../images/copyToClipboard.png" imageSource: "../images/copyToClipboard.png"
onClicked: { onClicked: {
if (txIdLine.text.length > 0) { if (getProofTxIdLine.text.length > 0) {
clipboard.setText(txIdLine.text) clipboard.setText(getProofTxIdLine.text)
} }
} }
} }
@ -178,29 +115,190 @@ Rectangle {
} }
RowLayout { RowLayout {
id: txKeyRow
Label { Label {
id: paymentIdLabel
fontSize: 14 fontSize: 14
text: qsTr("Transaction key") + translationManager.emptyString text: qsTr("Address") + translationManager.emptyString
width: mainLayout.labelWidth width: mainLayout.labelWidth
} }
LineEdit { LineEdit {
id: txKeyLine id: getProofAddressLine
fontSize: mainLayout.lineEditFontSize fontSize: mainLayout.lineEditFontSize
placeholderText: qsTr("Paste tx key") + translationManager.emptyString; placeholderText: qsTr("Recipient's wallet address") + translationManager.emptyString;
readOnly: false readOnly: false
width: mainLayout.editWidth width: mainLayout.editWidth
Layout.fillWidth: true Layout.fillWidth: true
IconButton { IconButton {
imageSource: "../images/copyToClipboard.png" imageSource: "../images/copyToClipboard.png"
onClicked: { onClicked: {
if (TxKeyLine.text.length > 0) { if (getProofAddressLine.text.length > 0) {
clipboard.setText(TxKeyLine.text) clipboard.setText(getProofAddressLine.text)
}
}
}
}
}
RowLayout {
Label {
fontSize: 14
text: qsTr("Message") + translationManager.emptyString
width: mainLayout.labelWidth
}
LineEdit {
id: getProofMessageLine
fontSize: mainLayout.lineEditFontSize
placeholderText: qsTr("Optional message against which the signature is signed") + translationManager.emptyString;
readOnly: false
width: mainLayout.editWidth
Layout.fillWidth: true
IconButton {
imageSource: "../images/copyToClipboard.png"
onClicked: {
if (getProofMessageLine.text.length > 0) {
clipboard.setText(getProofMessageLine.text)
}
}
}
}
}
StandardButton {
anchors.left: parent.left
anchors.topMargin: 17
width: 60
text: qsTr("Generate") + translationManager.emptyString
shadowReleasedColor: "#FF4304"
shadowPressedColor: "#B32D00"
releasedColor: "#FF6C3C"
pressedColor: "#FF4304"
enabled: checkTxID(getProofTxIdLine.text) && checkAddress(getProofAddressLine.text, appWindow.persistentSettings.testnet)
onClicked: {
console.log("getProof: Generate clicked: txid " + getProofTxIdLine.text + ", address " + getProofAddressLine.text + ", message: " + getProofMessageLine.text);
root.getProofClicked(getProofTxIdLine.text, getProofAddressLine.text, getProofMessageLine.text)
}
}
// underline
Rectangle {
height: 1
color: "#DBDBDB"
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
anchors.bottomMargin: 3
}
Text {
text: qsTr("Verify that funds were paid to an address by supplying the transaction ID, the recipient address, the message used for signing and the signature:") + translationManager.emptyString
wrapMode: Text.Wrap
Layout.fillWidth: true;
}
RowLayout {
Label {
fontSize: 14
text: qsTr("Transaction ID") + translationManager.emptyString
width: mainLayout.labelWidth
}
LineEdit {
id: checkProofTxIdLine
fontSize: mainLayout.lineEditFontSize
placeholderText: qsTr("Paste tx ID") + translationManager.emptyString
readOnly: false
width: mainLayout.editWidth
Layout.fillWidth: true
IconButton {
imageSource: "../images/copyToClipboard.png"
onClicked: {
if (checkProofTxIdLine.text.length > 0) {
clipboard.setText(checkProofTxIdLine.text)
}
}
}
}
}
RowLayout {
Label {
fontSize: 14
text: qsTr("Address") + translationManager.emptyString
width: mainLayout.labelWidth
}
LineEdit {
id: checkProofAddressLine
fontSize: mainLayout.lineEditFontSize
placeholderText: qsTr("Recipient's wallet address") + translationManager.emptyString;
readOnly: false
width: mainLayout.editWidth
Layout.fillWidth: true
IconButton {
imageSource: "../images/copyToClipboard.png"
onClicked: {
if (checkProofAddressLine.text.length > 0) {
clipboard.setText(checkProofAddressLine.text)
}
}
}
}
}
RowLayout {
Label {
fontSize: 14
text: qsTr("Message") + translationManager.emptyString
width: mainLayout.labelWidth
}
LineEdit {
id: checkProofMessageLine
fontSize: mainLayout.lineEditFontSize
placeholderText: qsTr("Optional message against which the signature is signed") + translationManager.emptyString;
readOnly: false
width: mainLayout.editWidth
Layout.fillWidth: true
IconButton {
imageSource: "../images/copyToClipboard.png"
onClicked: {
if (checkProofMessageLine.text.length > 0) {
clipboard.setText(checkProofMessageLine.text)
}
}
}
}
}
RowLayout {
Label {
fontSize: 14
text: qsTr("Signature") + translationManager.emptyString
width: mainLayout.labelWidth
}
LineEdit {
id: checkProofSignatureLine
fontSize: mainLayout.lineEditFontSize
placeholderText: qsTr("Paste tx proof") + translationManager.emptyString;
readOnly: false
width: mainLayout.editWidth
Layout.fillWidth: true
IconButton {
imageSource: "../images/copyToClipboard.png"
onClicked: {
if (checkProofSignatureLine.text.length > 0) {
clipboard.setText(checkProofSignatureLine.text)
} }
} }
} }
@ -208,9 +306,7 @@ Rectangle {
} }
StandardButton { StandardButton {
id: checkButton
anchors.left: parent.left anchors.left: parent.left
anchors.top: txKeyRow.bottom
anchors.topMargin: 17 anchors.topMargin: 17
width: 60 width: 60
text: qsTr("Check") + translationManager.emptyString text: qsTr("Check") + translationManager.emptyString
@ -218,13 +314,28 @@ Rectangle {
shadowPressedColor: "#B32D00" shadowPressedColor: "#B32D00"
releasedColor: "#FF6C3C" releasedColor: "#FF6C3C"
pressedColor: "#FF4304" pressedColor: "#FF4304"
enabled: checkAddress(addressLine.text, appWindow.persistentSettings.testnet) && checkTxID(txIdLine.text) && checkTxKey(txKeyLine.text) enabled: checkTxID(checkProofTxIdLine.text) && checkAddress(checkProofAddressLine.text, appWindow.persistentSettings.testnet) && checkSignature(checkProofSignatureLine.text)
onClicked: { onClicked: {
console.log("TxKey: Check clicked: address " + addressLine.text + ", txid " << txIdLine.text + ", tx key " + txKeyLine.text); console.log("checkProof: Check clicked: txid " + checkProofTxIdLine.text + ", address " + checkProofAddressLine.text + ", message " + checkProofMessageLine.text + ", signature " + checkProofSignatureLine.text);
root.checkPaymentClicked(addressLine.text, txIdLine.text, txKeyLine.text) root.checkProofClicked(checkProofTxIdLine.text, checkProofAddressLine.text, checkProofMessageLine.text, checkProofSignatureLine.text)
} }
} }
// underline
Rectangle {
height: 1
color: "#DBDBDB"
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
anchors.bottomMargin: 3
}
Text {
text: qsTr("If a payment had several transactions then each must be checked and the results combined.") + translationManager.emptyString
wrapMode: Text.Wrap
Layout.fillWidth: true;
}
} }
function onPageCompleted() { function onPageCompleted() {

View file

@ -494,6 +494,36 @@ QString Wallet::getTxKey(const QString &txid) const
return QString::fromStdString(m_walletImpl->getTxKey(txid.toStdString())); return QString::fromStdString(m_walletImpl->getTxKey(txid.toStdString()));
} }
QString Wallet::checkTxKey(const QString &txid, const QString &tx_key, const QString &address)
{
uint64_t received;
bool in_pool;
uint64_t confirmations;
bool success = m_walletImpl->checkTxKey(txid.toStdString(), tx_key.toStdString(), address.toStdString(), received, in_pool, confirmations);
std::string result = std::string(success ? "true" : "false") + "|" + QString::number(received).toStdString() + "|" + std::string(in_pool ? "true" : "false") + "|" + QString::number(confirmations).toStdString();
return QString::fromStdString(result);
}
QString Wallet::getTxProof(const QString &txid, const QString &address, const QString &message) const
{
std::string error_str;
QString result = QString::fromStdString(m_walletImpl->getTxProof(txid.toStdString(), address.toStdString(), message.toStdString(), error_str));
if (!error_str.empty())
result = QString::fromStdString("error|" + error_str);
return result;
}
QString Wallet::checkTxProof(const QString &txid, const QString &address, const QString &message, const QString &signature)
{
bool good;
uint64_t received;
bool in_pool;
uint64_t confirmations;
bool success = m_walletImpl->checkTxProof(txid.toStdString(), address.toStdString(), message.toStdString(), signature.toStdString(), good, received, in_pool, confirmations);
std::string result = std::string(success ? "true" : "false") + "|" + std::string(good ? "true" : "false") + "|" + QString::number(received).toStdString() + "|" + std::string(in_pool ? "true" : "false") + "|" + QString::number(confirmations).toStdString();
return QString::fromStdString(result);
}
QString Wallet::signMessage(const QString &message, bool filename) const QString Wallet::signMessage(const QString &message, bool filename) const
{ {
if (filename) { if (filename) {

View file

@ -232,6 +232,9 @@ public:
Q_INVOKABLE bool setUserNote(const QString &txid, const QString &note); Q_INVOKABLE bool setUserNote(const QString &txid, const QString &note);
Q_INVOKABLE QString getUserNote(const QString &txid) const; Q_INVOKABLE QString getUserNote(const QString &txid) const;
Q_INVOKABLE QString getTxKey(const QString &txid) const; Q_INVOKABLE QString getTxKey(const QString &txid) const;
Q_INVOKABLE QString checkTxKey(const QString &txid, const QString &tx_key, const QString &address);
Q_INVOKABLE QString getTxProof(const QString &txid, const QString &address, const QString &message) const;
Q_INVOKABLE QString checkTxProof(const QString &txid, const QString &address, const QString &message, const QString &signature);
// Rescan spent outputs // Rescan spent outputs
Q_INVOKABLE bool rescanSpent(); Q_INVOKABLE bool rescanSpent();

View file

@ -216,16 +216,6 @@ QString WalletManager::paymentIdFromAddress(const QString &address, bool testnet
return QString::fromStdString(Monero::Wallet::paymentIdFromAddress(address.toStdString(), testnet)); return QString::fromStdString(Monero::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::setDaemonAddress(const QString &address) void WalletManager::setDaemonAddress(const QString &address)
{ {
m_pimpl->setDaemonAddress(address.toStdString()); m_pimpl->setDaemonAddress(address.toStdString());

View file

@ -104,8 +104,6 @@ public:
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;
Q_INVOKABLE void setDaemonAddress(const QString &address); Q_INVOKABLE void setDaemonAddress(const QString &address);
Q_INVOKABLE bool connected() const; Q_INVOKABLE bool connected() const;
Q_INVOKABLE quint64 networkDifficulty() const; Q_INVOKABLE quint64 networkDifficulty() const;