Offline transaction signing

This commit is contained in:
tobtoht
2020-10-19 01:39:23 +02:00
parent 44a2fc30dc
commit 216b2e0c5e
35 changed files with 1334 additions and 52 deletions
+89
View File
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020, The Monero Project.
#include "daemonrpc.h"
#include <utility>
DaemonRpc::DaemonRpc(QObject *parent, UtilsNetworking *network, QString daemonAddress)
: QObject(parent)
, m_network(network)
, m_daemonAddress(std::move(daemonAddress))
{
}
void DaemonRpc::sendRawTransaction(const QString &tx_as_hex, bool do_not_relay, bool do_sanity_checks) {
QJsonObject req;
req["tx_as_hex"] = tx_as_hex;
req["do_not_relay"] = do_not_relay;
req["do_sanity_checks"] = do_sanity_checks;
QString url = QString("%1/send_raw_transaction").arg(m_daemonAddress);
QNetworkReply *reply = m_network->postJson(url, req);
connect(reply, &QNetworkReply::finished, std::bind(&DaemonRpc::onResponse, this, reply, Endpoint::SEND_RAW_TRANSACTION));
}
void DaemonRpc::onResponse(QNetworkReply *reply, Endpoint endpoint) {
const auto ok = reply->error() == QNetworkReply::NoError;
const auto err = reply->errorString();
QByteArray data = reply->readAll();
QJsonObject obj;
if (!data.isEmpty() && Utils::validateJSON(data)) {
auto doc = QJsonDocument::fromJson(data);
obj = doc.object();
}
else if (!ok) {
emit ApiResponse(DaemonResponse(false, endpoint, err));
return;
}
else {
emit ApiResponse(DaemonResponse(false, endpoint, "Invalid response from daemon"));
return;
}
if (obj.value("status").toString() != "OK") {
QString failedMsg;
switch (endpoint) {
case SEND_RAW_TRANSACTION:
failedMsg = this->onSendRawTransactionFailed(obj);
break;
default:
failedMsg = obj.value("status").toString();
}
emit ApiResponse(DaemonResponse(false, endpoint, failedMsg, obj));
return;
}
reply->deleteLater();
emit ApiResponse(DaemonResponse(true, endpoint, "", obj));
}
QString DaemonRpc::onSendRawTransactionFailed(const QJsonObject &obj) {
QString message = [&obj]{
if (obj.value("double_spend").toBool())
return "Transaction is a double spend";
if (obj.value("fee_too_low").toBool())
return "Fee is too low";
if (obj.value("invalid_input").toBool())
return "Output is invalid";
if (obj.value("low_mixin").toBool())
return "Mixin count is too low";
if (obj.value("overspend").toBool())
return "Transaction uses more money than available";
if (obj.value("too_big").toBool())
return "Transaction size is too big";
return "Daemon returned an unknown error";
}();
return QString("Transaction failed: %1").arg(message);
}
void DaemonRpc::setDaemonAddress(const QString &daemonAddress) {
m_daemonAddress = daemonAddress;
}
void DaemonRpc::setNetwork(UtilsNetworking *network) {
m_network = network;
}
+49
View File
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020, The Monero Project.
#ifndef FEATHER_DAEMON_RPC_H
#define FEATHER_DAEMON_RPC_H
#include <QObject>
#include "utils/networking.h"
class DaemonRpc : public QObject {
Q_OBJECT
public:
enum Endpoint {
SEND_RAW_TRANSACTION = 0
};
struct DaemonResponse {
explicit DaemonResponse(bool ok, Endpoint endpoint, QString status, QJsonObject obj = {})
: ok(ok), endpoint(endpoint), status(std::move(status)), obj(std::move(obj)) {};
bool ok;
DaemonRpc::Endpoint endpoint;
QString status;
QJsonObject obj;
};
explicit DaemonRpc(QObject *parent, UtilsNetworking *network, QString daemonAddress);
void sendRawTransaction(const QString &tx_as_hex, bool do_not_relay = false, bool do_sanity_checks = true);
void setDaemonAddress(const QString &daemonAddress);
void setNetwork(UtilsNetworking *network);
signals:
void ApiResponse(DaemonResponse resp);
private slots:
void onResponse(QNetworkReply *reply, Endpoint endpoint);
QString onSendRawTransactionFailed(const QJsonObject &obj);
private:
UtilsNetworking *m_network;
QString m_daemonAddress;
};
#endif //FEATHER_DAEMON_RPC_H
+10 -1
View File
@@ -68,7 +68,7 @@ QByteArray Utils::fileOpenQRC(const QString &path) {
bool Utils::fileWrite(const QString &path, const QString &data) {
QFile file(path);
if(file.open(QIODevice::WriteOnly)){
QTextStream out(&file); out << data << endl;
QTextStream out(&file); out << data << Qt::endl;
file.close();
return true;
}
@@ -362,6 +362,15 @@ void Utils::copyToClipboard(const QString &string){
#endif
}
QString Utils::copyFromClipboard() {
QClipboard * clipboard = QApplication::clipboard();
if (!clipboard) {
qWarning() << "Unable to access clipboard";
return "";
}
return clipboard->text();
}
QString Utils::blockExplorerLink(const QString &blockExplorer, NetworkType::Type nettype, const QString &txid) {
if (blockExplorer == "exploremonero.com") {
if (nettype == NetworkType::MAINNET) {
+1
View File
@@ -85,6 +85,7 @@ public:
static QStandardItem *qStandardItem(const QString &text);
static QStandardItem *qStandardItem(const QString &text, QFont &font);
static void copyToClipboard(const QString &string);
static QString copyFromClipboard();
static QString blockExplorerLink(const QString &blockExplorer, NetworkType::Type nettype, const QString &txid);
static QString getUnixAccountName();
static QString xdgDesktopEntry();
+1 -1
View File
@@ -41,7 +41,6 @@ void XmrToApi::getOrderStatus(const QString &uuid) {
void XmrToApi::onResponse(QNetworkReply *reply, Endpoint endpoint) {
const auto ok = reply->error() == QNetworkReply::NoError;
const auto err = reply->errorString();
reply->deleteLater();
QByteArray data = reply->readAll();
QJsonObject obj;
@@ -64,6 +63,7 @@ void XmrToApi::onResponse(QNetworkReply *reply, Endpoint endpoint) {
return;
}
reply->deleteLater();
emit ApiResponse(XmrToResponse(true, endpoint, "", obj));
}