mirror of
https://github.com/monero-project/monero-gui.git
synced 2024-12-23 03:59:38 +00:00
Advanced: ReserveProof: Add support for reserve proof
This change adds the ability to prove and check a reserve proof to this existing "Prove/check" advanced menu. If the amount line has a valid amount less than that of the current account index this input will drive the logic for generating a reserve proof. Checking a reserve proof is accomplished by validating the address and verifying that signature.indexOf("ReserveProofV") === 0. The result displays the total and spent amount of the proof.
This commit is contained in:
parent
a959919b8a
commit
0f67580e8f
6 changed files with 103 additions and 29 deletions
|
@ -66,7 +66,7 @@ Rectangle {
|
||||||
signal paymentClicked(var recipients, string paymentId, int mixinCount, int priority, string description)
|
signal paymentClicked(var recipients, string paymentId, int mixinCount, int priority, string description)
|
||||||
signal sweepUnmixableClicked()
|
signal sweepUnmixableClicked()
|
||||||
signal generatePaymentIdInvoked()
|
signal generatePaymentIdInvoked()
|
||||||
signal getProofClicked(string txid, string address, string message);
|
signal getProofClicked(string txid, string address, string message, string amount);
|
||||||
signal checkProofClicked(string txid, string address, string message, string signature);
|
signal checkProofClicked(string txid, string address, string message, string signature);
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
|
|
@ -61,6 +61,10 @@ function checkSignature(signature) {
|
||||||
if ((signature.length - 12) % 88 != 0)
|
if ((signature.length - 12) % 88 != 0)
|
||||||
return false;
|
return false;
|
||||||
return check256(signature, signature.length);
|
return check256(signature, signature.length);
|
||||||
|
} else if (signature.indexOf("ReserveProofV") === 0) {
|
||||||
|
if ((signature.length - 14) % 447 != 0)
|
||||||
|
return false;
|
||||||
|
return check256(signature, signature.length);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
28
main.qml
28
main.qml
|
@ -985,12 +985,15 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
// called on "getProof"
|
// called on "getProof"
|
||||||
function handleGetProof(txid, address, message) {
|
function handleGetProof(txid, address, message, amount) {
|
||||||
|
if (amount.length > 0) {
|
||||||
|
var result = currentWallet.getReserveProof(false, currentWallet.currentSubaddressAccount, walletManager.amountFromString(amount), message)
|
||||||
|
txProofComputed(null, result)
|
||||||
|
} else {
|
||||||
console.log("Getting payment proof: ")
|
console.log("Getting payment proof: ")
|
||||||
console.log("\ttxid: ", txid,
|
console.log("\ttxid: ", txid,
|
||||||
", address: ", address,
|
", address: ", address,
|
||||||
", message: ", message);
|
", message: ", message);
|
||||||
|
|
||||||
function spendProofFallback(txid, result){
|
function spendProofFallback(txid, result){
|
||||||
if (!result || result.indexOf("error|") === 0) {
|
if (!result || result.indexOf("error|") === 0) {
|
||||||
currentWallet.getSpendProofAsync(txid, message, txProofComputed);
|
currentWallet.getSpendProofAsync(txid, message, txProofComputed);
|
||||||
|
@ -998,12 +1001,13 @@ ApplicationWindow {
|
||||||
txProofComputed(txid, result);
|
txProofComputed(txid, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (address.length > 0)
|
if (address.length > 0)
|
||||||
currentWallet.getTxProofAsync(txid, address, message, spendProofFallback);
|
currentWallet.getTxProofAsync(txid, address, message, spendProofFallback);
|
||||||
else
|
else
|
||||||
spendProofFallback(txid, null);
|
spendProofFallback(txid, null);
|
||||||
}
|
}
|
||||||
|
informationPopup.open()
|
||||||
|
}
|
||||||
|
|
||||||
function txProofComputed(txid, result){
|
function txProofComputed(txid, result){
|
||||||
if (result.indexOf("error|") === 0) {
|
if (result.indexOf("error|") === 0) {
|
||||||
|
@ -1025,12 +1029,18 @@ ApplicationWindow {
|
||||||
", signature: ", signature);
|
", signature: ", signature);
|
||||||
|
|
||||||
var result;
|
var result;
|
||||||
if (address.length > 0)
|
var isReserveProof = signature.indexOf("ReserveProofV") === 0;
|
||||||
|
if (address.length > 0 && !isReserveProof) {
|
||||||
result = currentWallet.checkTxProof(txid, address, message, signature);
|
result = currentWallet.checkTxProof(txid, address, message, signature);
|
||||||
else
|
}
|
||||||
|
else if (isReserveProof) {
|
||||||
|
result = currentWallet.checkReserveProof(address, message, signature);
|
||||||
|
}
|
||||||
|
else {
|
||||||
result = currentWallet.checkSpendProof(txid, message, signature);
|
result = currentWallet.checkSpendProof(txid, message, signature);
|
||||||
|
}
|
||||||
var results = result.split("|");
|
var results = result.split("|");
|
||||||
if (address.length > 0 && results.length == 5 && results[0] === "true") {
|
if (address.length > 0 && results.length == 5 && results[0] === "true" && !isReserveProof) {
|
||||||
var good = results[1] === "true";
|
var good = results[1] === "true";
|
||||||
var received = results[2];
|
var received = results[2];
|
||||||
var in_pool = results[3] === "true";
|
var in_pool = results[3] === "true";
|
||||||
|
@ -1059,6 +1069,12 @@ ApplicationWindow {
|
||||||
informationPopup.icon = good ? StandardIcon.Information : StandardIcon.Critical;
|
informationPopup.icon = good ? StandardIcon.Information : StandardIcon.Critical;
|
||||||
informationPopup.text = good ? qsTr("Good signature") : qsTr("Bad signature");
|
informationPopup.text = good ? qsTr("Good signature") : qsTr("Bad signature");
|
||||||
}
|
}
|
||||||
|
else if (isReserveProof && results[0] === "true") {
|
||||||
|
var good = results[1] === "true";
|
||||||
|
informationPopup.title = qsTr("Reserve proof check") + translationManager.emptyString;
|
||||||
|
informationPopup.icon = good ? StandardIcon.Information : StandardIcon.Critical;
|
||||||
|
informationPopup.text = good ? qsTr("Good signature on ") + results[2] + qsTr(" total and ") + results[3] + qsTr(" spent.") : qsTr("Bad signature");
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
informationPopup.title = qsTr("Error") + translationManager.emptyString;
|
informationPopup.title = qsTr("Error") + translationManager.emptyString;
|
||||||
informationPopup.text = currentWallet.errorString;
|
informationPopup.text = currentWallet.errorString;
|
||||||
|
|
|
@ -60,13 +60,13 @@ Rectangle {
|
||||||
MoneroComponents.Label {
|
MoneroComponents.Label {
|
||||||
id: soloTitleLabel
|
id: soloTitleLabel
|
||||||
fontSize: 24
|
fontSize: 24
|
||||||
text: qsTr("Prove Transaction") + translationManager.emptyString
|
text: qsTr("Prove Transaction / Reserve") + translationManager.emptyString
|
||||||
}
|
}
|
||||||
|
|
||||||
MoneroComponents.TextPlain {
|
MoneroComponents.TextPlain {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
text: qsTr("Generate a proof of your incoming/outgoing payment by supplying the transaction ID, the recipient address and an optional message. \n" +
|
text: qsTr("Generate a proof of your incoming/outgoing payment by supplying the transaction ID, the recipient address and an optional message. \n" +
|
||||||
"For the case of outgoing payments, you can get a 'Spend Proof' that proves the authorship of a transaction. In this case, you don't need to specify the recipient address.") + translationManager.emptyString
|
"For the case of outgoing payments, you can get a 'Spend Proof' that proves the authorship of a transaction. In this case, you don't need to specify the recipient address. \nFor reserve proofs you don't need to specify tx id or address.") + translationManager.emptyString
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
font.family: MoneroComponents.Style.fontRegular.name
|
font.family: MoneroComponents.Style.fontRegular.name
|
||||||
font.pixelSize: 14
|
font.pixelSize: 14
|
||||||
|
@ -83,6 +83,7 @@ Rectangle {
|
||||||
placeholderText: qsTr("Paste tx ID") + translationManager.emptyString
|
placeholderText: qsTr("Paste tx ID") + translationManager.emptyString
|
||||||
readOnly: false
|
readOnly: false
|
||||||
copyButton: true
|
copyButton: true
|
||||||
|
enabled: getReserveProofAmtLine.text.length === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
MoneroComponents.LineEdit {
|
MoneroComponents.LineEdit {
|
||||||
|
@ -95,6 +96,38 @@ Rectangle {
|
||||||
placeholderText: qsTr("Recipient's wallet address") + translationManager.emptyString;
|
placeholderText: qsTr("Recipient's wallet address") + translationManager.emptyString;
|
||||||
readOnly: false
|
readOnly: false
|
||||||
copyButton: true
|
copyButton: true
|
||||||
|
enabled: getReserveProofAmtLine.text.length === 0
|
||||||
|
}
|
||||||
|
|
||||||
|
MoneroComponents.LineEdit {
|
||||||
|
id: getReserveProofAmtLine
|
||||||
|
Layout.fillWidth: true
|
||||||
|
labelFontSize: 14
|
||||||
|
labelText: qsTr("Amount") + translationManager.emptyString
|
||||||
|
fontSize: 16
|
||||||
|
placeholderFontSize: 16
|
||||||
|
placeholderText: qsTr("Paste amount of XMR (reserve proof only)") + translationManager.emptyString
|
||||||
|
readOnly: false
|
||||||
|
copyButton: true
|
||||||
|
enabled: getProofAddressLine.text.length === 0 && getProofTxIdLine.text.length === 0
|
||||||
|
onTextChanged: {
|
||||||
|
text = text.trim().replace(",", ".");
|
||||||
|
const match = text.match(/^0+(\d.*)/);
|
||||||
|
if (match) {
|
||||||
|
const cursorPosition = cursorPosition;
|
||||||
|
text = match[1];
|
||||||
|
cursorPosition = Math.max(cursorPosition, 1) - 1;
|
||||||
|
} else if(text.indexOf('.') === 0) {
|
||||||
|
text = '0' + text;
|
||||||
|
if (text.length > 2) {
|
||||||
|
cursorPosition = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error = walletManager.amountFromString(text) > appWindow.getUnlockedBalance();
|
||||||
|
}
|
||||||
|
validator: RegExpValidator {
|
||||||
|
regExp: /^\s*(\d{1,8})?([\.,]\d{1,12})?\s*$/
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MoneroComponents.LineEdit {
|
MoneroComponents.LineEdit {
|
||||||
|
@ -113,10 +146,10 @@ Rectangle {
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 16
|
||||||
small: true
|
small: true
|
||||||
text: qsTr("Generate") + translationManager.emptyString
|
text: qsTr("Generate") + translationManager.emptyString
|
||||||
enabled: TxUtils.checkTxID(getProofTxIdLine.text) && (getProofAddressLine.text.length == 0 || TxUtils.checkAddress(getProofAddressLine.text, appWindow.persistentSettings.nettype))
|
enabled: TxUtils.checkTxID(getProofTxIdLine.text) && (getProofAddressLine.text.length == 0 || TxUtils.checkAddress(getProofAddressLine.text, appWindow.persistentSettings.nettype)) || getReserveProofAmtLine.text.length != 0 && walletManager.amountFromString(getReserveProofAmtLine.text) < appWindow.getUnlockedBalance() && walletManager.amountFromString(getReserveProofAmtLine.text) > 0
|
||||||
onClicked: {
|
onClicked: {
|
||||||
console.log("getProof: Generate clicked: txid " + getProofTxIdLine.text + ", address " + getProofAddressLine.text + ", message: " + getProofMessageLine.text);
|
console.log("getProof: Generate clicked: txid " + getProofTxIdLine.text + ", address " + getProofAddressLine.text + ", message: " + getProofMessageLine.text);
|
||||||
middlePanel.getProofClicked(getProofTxIdLine.text, getProofAddressLine.text, getProofMessageLine.text)
|
middlePanel.getProofClicked(getProofTxIdLine.text, getProofAddressLine.text, getProofMessageLine.text, getReserveProofAmtLine.text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,12 +166,12 @@ Rectangle {
|
||||||
MoneroComponents.Label {
|
MoneroComponents.Label {
|
||||||
id: soloTitleLabel2
|
id: soloTitleLabel2
|
||||||
fontSize: 24
|
fontSize: 24
|
||||||
text: qsTr("Check Transaction") + translationManager.emptyString
|
text: qsTr("Check Transaction / Reserve") + translationManager.emptyString
|
||||||
}
|
}
|
||||||
|
|
||||||
MoneroComponents.TextPlain {
|
MoneroComponents.TextPlain {
|
||||||
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.\n" +
|
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.\n" +
|
||||||
"For the case with Spend Proof, you don't need to specify the recipient address.") + translationManager.emptyString
|
"For the case with Spend Proof, you don't need to specify the recipient address.\nTransaction is not needed for reserve proof.") + translationManager.emptyString
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
font.family: MoneroComponents.Style.fontRegular.name
|
font.family: MoneroComponents.Style.fontRegular.name
|
||||||
|
@ -189,7 +222,7 @@ Rectangle {
|
||||||
labelFontSize: 14
|
labelFontSize: 14
|
||||||
labelText: qsTr("Signature") + translationManager.emptyString
|
labelText: qsTr("Signature") + translationManager.emptyString
|
||||||
placeholderFontSize: 16
|
placeholderFontSize: 16
|
||||||
placeholderText: qsTr("Paste tx proof") + translationManager.emptyString;
|
placeholderText: qsTr("Paste tx / reserve proof") + translationManager.emptyString;
|
||||||
readOnly: false
|
readOnly: false
|
||||||
copyButton: true
|
copyButton: true
|
||||||
}
|
}
|
||||||
|
@ -198,7 +231,7 @@ Rectangle {
|
||||||
Layout.topMargin: 16
|
Layout.topMargin: 16
|
||||||
small: true
|
small: true
|
||||||
text: qsTr("Check") + translationManager.emptyString
|
text: qsTr("Check") + translationManager.emptyString
|
||||||
enabled: TxUtils.checkTxID(checkProofTxIdLine.text) && TxUtils.checkSignature(checkProofSignatureLine.text) && ((checkProofSignatureLine.text.indexOf("SpendProofV") === 0 && checkProofAddressLine.text.length == 0) || (checkProofSignatureLine.text.indexOf("SpendProofV") !== 0 && TxUtils.checkAddress(checkProofAddressLine.text, appWindow.persistentSettings.nettype)))
|
enabled: (TxUtils.checkTxID(checkProofTxIdLine.text) && TxUtils.checkSignature(checkProofSignatureLine.text) && ((checkProofSignatureLine.text.indexOf("SpendProofV") === 0 && checkProofAddressLine.text.length == 0) || (checkProofSignatureLine.text.indexOf("SpendProofV") !== 0 && TxUtils.checkAddress(checkProofAddressLine.text, appWindow.persistentSettings.nettype)))) || (TxUtils.checkSignature(checkProofSignatureLine.text) && checkProofSignatureLine.text.indexOf("ReserveProofV") === 0 && TxUtils.checkAddress(checkProofAddressLine.text, appWindow.persistentSettings.nettype))
|
||||||
onClicked: {
|
onClicked: {
|
||||||
console.log("checkProof: Check clicked: txid " + checkProofTxIdLine.text + ", address " + checkProofAddressLine.text + ", message " + checkProofMessageLine.text + ", signature " + checkProofSignatureLine.text);
|
console.log("checkProof: Check clicked: txid " + checkProofTxIdLine.text + ", address " + checkProofAddressLine.text + ", message " + checkProofMessageLine.text + ", signature " + checkProofSignatureLine.text);
|
||||||
middlePanel.checkProofClicked(checkProofTxIdLine.text, checkProofAddressLine.text, checkProofMessageLine.text, checkProofSignatureLine.text)
|
middlePanel.checkProofClicked(checkProofTxIdLine.text, checkProofAddressLine.text, checkProofMessageLine.text, checkProofSignatureLine.text)
|
||||||
|
|
|
@ -844,6 +844,25 @@ Q_INVOKABLE QString Wallet::checkSpendProof(const QString &txid, const QString &
|
||||||
return QString::fromStdString(result);
|
return QString::fromStdString(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE QString Wallet::getReserveProof(bool all, quint32 account_index, quint64 amount, const QString &message) const
|
||||||
|
{
|
||||||
|
qDebug("Generating reserve proof");
|
||||||
|
std::string result = m_walletImpl->getReserveProof(all, account_index, amount, message.toStdString());
|
||||||
|
if (result.empty())
|
||||||
|
result = "error|" + m_walletImpl->errorString();
|
||||||
|
return QString::fromStdString(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE QString Wallet::checkReserveProof(const QString &address, const QString &message, const QString &signature) const
|
||||||
|
{
|
||||||
|
bool good;
|
||||||
|
u_int64_t total;
|
||||||
|
u_int64_t spent;
|
||||||
|
bool success = m_walletImpl->checkReserveProof(address.toStdString(), message.toStdString(), signature.toStdString(), good, total, spent);
|
||||||
|
std::string result = std::string(success ? "true" : "false") + "|" + std::string(good ? "true" : "false") + "|" + QString::number(total).toStdString() + "|" + QString::number(spent).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) {
|
||||||
|
|
|
@ -318,6 +318,8 @@ public:
|
||||||
Q_INVOKABLE QString getSpendProof(const QString &txid, const QString &message) const;
|
Q_INVOKABLE QString getSpendProof(const QString &txid, const QString &message) const;
|
||||||
Q_INVOKABLE void getSpendProofAsync(const QString &txid, const QString &message, const QJSValue &callback);
|
Q_INVOKABLE void getSpendProofAsync(const QString &txid, const QString &message, const QJSValue &callback);
|
||||||
Q_INVOKABLE QString checkSpendProof(const QString &txid, const QString &message, const QString &signature) const;
|
Q_INVOKABLE QString checkSpendProof(const QString &txid, const QString &message, const QString &signature) const;
|
||||||
|
Q_INVOKABLE QString getReserveProof(bool all, quint32 account_index, quint64 amount, const QString &message) const;
|
||||||
|
Q_INVOKABLE QString checkReserveProof(const QString &address, const QString &message, const QString &signature) const;
|
||||||
// Rescan spent outputs
|
// Rescan spent outputs
|
||||||
Q_INVOKABLE bool rescanSpent();
|
Q_INVOKABLE bool rescanSpent();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue