forked from feather/feather
parent
44a2fc30dc
commit
216b2e0c5e
@ -1 +1 @@
|
||||
Subproject commit f6587d7943a19c55a5b78af1a89b22c130513b73
|
||||
Subproject commit c2b7b50fdea2e66a593f9c9109ebd742f69ad9d1
|
@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020, The Monero Project.
|
||||
|
||||
#include "broadcasttxdialog.h"
|
||||
#include "ui_broadcasttxdialog.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
BroadcastTxDialog::BroadcastTxDialog(QWidget *parent, AppContext *ctx)
|
||||
: QDialog(parent)
|
||||
, m_ctx(ctx)
|
||||
, ui(new Ui::BroadcastTxDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
m_network = new UtilsNetworking(m_ctx->network, this);
|
||||
|
||||
auto node = ctx->nodes->connection();
|
||||
m_rpc = new DaemonRpc(this, m_network, node.full);
|
||||
|
||||
connect(ui->btn_Broadcast, &QPushButton::clicked, this, &BroadcastTxDialog::broadcastTx);
|
||||
connect(ui->btn_Close, &QPushButton::clicked, this, &BroadcastTxDialog::reject);
|
||||
|
||||
connect(m_rpc, &DaemonRpc::ApiResponse, this, &BroadcastTxDialog::onApiResponse);
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
void BroadcastTxDialog::broadcastTx() {
|
||||
QString tx = ui->transaction->toPlainText();
|
||||
|
||||
QString node = [this]{
|
||||
QString node;
|
||||
if (ui->radio_useCustom->isChecked())
|
||||
node = ui->customNode->text();
|
||||
else
|
||||
node = m_ctx->nodes->connection().full;
|
||||
|
||||
if (!node.startsWith("http://"))
|
||||
node = QString("http://%1").arg(node);
|
||||
return node;
|
||||
}();
|
||||
|
||||
m_rpc->setDaemonAddress(node);
|
||||
m_rpc->sendRawTransaction(tx);
|
||||
}
|
||||
|
||||
void BroadcastTxDialog::onApiResponse(const DaemonRpc::DaemonResponse &resp) {
|
||||
if (!resp.ok) {
|
||||
QMessageBox::warning(this, "Transaction broadcast", resp.status);
|
||||
return;
|
||||
}
|
||||
|
||||
if (resp.endpoint == DaemonRpc::Endpoint::SEND_RAW_TRANSACTION) {
|
||||
QMessageBox::information(this, "Transaction broadcast", "Transaction submitted successfully.\n\n"
|
||||
"If the transaction belongs to this wallet it may take several minutes before it shows up in the history tab.");
|
||||
}
|
||||
}
|
||||
|
||||
BroadcastTxDialog::~BroadcastTxDialog() {
|
||||
delete ui;
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020, The Monero Project.
|
||||
|
||||
#ifndef FEATHER_BROADCASTTXDIALOG_H
|
||||
#define FEATHER_BROADCASTTXDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "appcontext.h"
|
||||
#include "utils/daemonrpc.h"
|
||||
|
||||
namespace Ui {
|
||||
class BroadcastTxDialog;
|
||||
}
|
||||
|
||||
class BroadcastTxDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit BroadcastTxDialog(QWidget *parent, AppContext *ctx);
|
||||
~BroadcastTxDialog() override;
|
||||
|
||||
private slots:
|
||||
void broadcastTx();
|
||||
void onApiResponse(const DaemonRpc::DaemonResponse &resp);
|
||||
|
||||
private:
|
||||
Ui::BroadcastTxDialog *ui;
|
||||
UtilsNetworking *m_network;
|
||||
AppContext *m_ctx;
|
||||
DaemonRpc *m_rpc;
|
||||
};
|
||||
|
||||
|
||||
#endif //FEATHER_BROADCASTTXDIALOG_H
|
@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>BroadcastTxDialog</class>
|
||||
<widget class="QDialog" name="BroadcastTxDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>797</width>
|
||||
<height>575</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Broadcast transaction</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Transaction:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="transaction">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>500</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Paste hex encoded signed tx string here</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Node</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radio_useDefault">
|
||||
<property name="text">
|
||||
<string>Use currently connected node</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radio_useCustom">
|
||||
<property name="text">
|
||||
<string>Use custom node</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="customNode"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>All transactions are broadcast over Tor</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_Broadcast">
|
||||
<property name="text">
|
||||
<string>Broadcast</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_Close">
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -0,0 +1,186 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020, The Monero Project.
|
||||
|
||||
#include "txconfadvdialog.h"
|
||||
#include "ui_txconfadvdialog.h"
|
||||
#include "libwalletqt/WalletManager.h"
|
||||
#include "qrcode/QrCode.h"
|
||||
#include "dialog/qrcodedialog.h"
|
||||
#include "utils/utils.h"
|
||||
#include "libwalletqt/PendingTransactionInfo.h"
|
||||
#include "libwalletqt/Transfer.h"
|
||||
#include "libwalletqt/Input.h"
|
||||
#include "model/ModelUtils.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
TxConfAdvDialog::TxConfAdvDialog(AppContext *ctx, const QString &description, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::TxConfAdvDialog)
|
||||
, m_ctx(ctx)
|
||||
, m_exportUnsignedMenu(new QMenu(this))
|
||||
, m_exportSignedMenu(new QMenu(this))
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
m_exportUnsignedMenu->addAction("Copy to clipboard", this, &TxConfAdvDialog::unsignedCopy);
|
||||
m_exportUnsignedMenu->addAction("Show as QR code", this, &TxConfAdvDialog::unsignedQrCode);
|
||||
m_exportUnsignedMenu->addAction("Save to file", this, &TxConfAdvDialog::unsignedSaveFile);
|
||||
ui->btn_exportUnsigned->setMenu(m_exportUnsignedMenu);
|
||||
|
||||
m_exportSignedMenu->addAction("Copy to clipboard", this, &TxConfAdvDialog::signedCopy);
|
||||
m_exportSignedMenu->addAction("Save to file", this, &TxConfAdvDialog::signedSaveFile);
|
||||
ui->btn_exportSigned->setMenu(m_exportSignedMenu);
|
||||
|
||||
if (m_ctx->currentWallet->viewOnly()) {
|
||||
ui->btn_exportSigned->hide();
|
||||
ui->btn_send->hide();
|
||||
}
|
||||
|
||||
ui->label_description->setText(QString("Description: %1").arg(description));
|
||||
|
||||
connect(ui->btn_sign, &QPushButton::clicked, this, &TxConfAdvDialog::signTransaction);
|
||||
connect(ui->btn_send, &QPushButton::clicked, this, &TxConfAdvDialog::broadcastTransaction);
|
||||
connect(ui->btn_close, &QPushButton::clicked, this, &TxConfAdvDialog::closeDialog);
|
||||
|
||||
ui->inputs->setFont(ModelUtils::getMonospaceFont());
|
||||
ui->outputs->setFont(ModelUtils::getMonospaceFont());
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::setTransaction(PendingTransaction *tx) {
|
||||
ui->btn_sign->hide();
|
||||
|
||||
m_tx = tx;
|
||||
m_tx->refresh();
|
||||
PendingTransactionInfo *ptx = m_tx->transaction(0);
|
||||
|
||||
ui->txid->setText(tx->txid().first());
|
||||
|
||||
ui->amount->setText(WalletManager::displayAmount(tx->amount()));
|
||||
ui->fee->setText(WalletManager::displayAmount(ptx->fee()));
|
||||
ui->total->setText(WalletManager::displayAmount(tx->amount() + ptx->fee()));
|
||||
|
||||
auto size_str = [this]{
|
||||
if (m_ctx->currentWallet->viewOnly()) {
|
||||
return QString("Size: %1 bytes (unsigned)").arg(QString::number(m_tx->unsignedTxToBin().size()));
|
||||
} else {
|
||||
auto size = m_tx->signedTxToHex(0).size() / 2;
|
||||
return QString("Size: %1 bytes (%2 bytes unsigned)").arg(QString::number(size), QString::number(m_tx->unsignedTxToBin().size()));
|
||||
}
|
||||
}();
|
||||
ui->label_size->setText(size_str);
|
||||
|
||||
this->setupConstructionData(ptx);
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::setUnsignedTransaction(UnsignedTransaction *utx) {
|
||||
m_utx = utx;
|
||||
m_utx->refresh();
|
||||
|
||||
ui->btn_exportUnsigned->hide();
|
||||
ui->btn_exportSigned->hide();
|
||||
ui->btn_sign->show();
|
||||
ui->btn_send->hide();
|
||||
|
||||
ui->txid->setText("n/a");
|
||||
ui->label_size->setText("Size: n/a");
|
||||
|
||||
ui->amount->setText(WalletManager::displayAmount(utx->amount(0)));
|
||||
ui->fee->setText(WalletManager::displayAmount(utx->fee(0)));
|
||||
ui->total->setText(WalletManager::displayAmount(utx->amount(0) + utx->fee(0)));
|
||||
|
||||
ConstructionInfo *ci = m_utx->constructionInfo(0);
|
||||
this->setupConstructionData(ci);
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::setupConstructionData(ConstructionInfo *ci) {
|
||||
QString inputs_str;
|
||||
auto inputs = ci->inputs();
|
||||
for (const auto& i: inputs) {
|
||||
inputs_str += QString("%1 %2\n").arg(i->pubKey(), WalletManager::displayAmount(i->amount()));
|
||||
}
|
||||
ui->inputs->setText(inputs_str);
|
||||
ui->label_inputs->setText(QString("Inputs (%1)").arg(QString::number(inputs.size())));
|
||||
|
||||
QString outputs_str;
|
||||
auto outputs = ci->outputs();
|
||||
for (const auto& o: outputs) {
|
||||
outputs_str += QString("%1 %2\n").arg(o->address(), WalletManager::displayAmount(o->amount()));
|
||||
}
|
||||
ui->outputs->setText(outputs_str);
|
||||
ui->label_outputs->setText(QString("Outputs (%1)").arg(QString::number(outputs.size())));
|
||||
|
||||
ui->label_ringSize->setText(QString("Ring size: %1").arg(QString::number(ci->minMixinCount() + 1)));
|
||||
ui->label_unlockTime->setText(QString("Unlock time: %1 (height)").arg(QString::number(ci->unlockTime())));
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::signTransaction() {
|
||||
QString defaultName = QString("%1_signed_monero_tx").arg(QString::number(QDateTime::currentSecsSinceEpoch()));
|
||||
QString fn = QFileDialog::getSaveFileName(this, "Save signed transaction to file", QDir::home().filePath(defaultName), "Transaction (*signed_monero_tx)");
|
||||
if(fn.isEmpty()) return;
|
||||
|
||||
m_utx->sign(fn) ? QMessageBox::information(this, "Sign transaction", "Transaction saved successfully")
|
||||
: QMessageBox::warning(this, "Sign transaction", "Failes to save transaction to file.");
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::unsignedSaveFile() {
|
||||
QString defaultName = QString("%1_unsigned_monero_tx").arg(QString::number(QDateTime::currentSecsSinceEpoch()));
|
||||
QString fn = QFileDialog::getSaveFileName(this, "Save transaction to file", QDir::home().filePath(defaultName), "Transaction (*unsigned_monero_tx)");
|
||||
if(fn.isEmpty()) return;
|
||||
|
||||
m_tx->saveToFile(fn) ? QMessageBox::information(this, "Transaction saved to file", "Transaction saved successfully")
|
||||
: QMessageBox::warning(this, "Save transaction", "Failed to save transaction to file.");
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::signedSaveFile() {
|
||||
QString defaultName = QString("%1_signed_monero_tx").arg(QString::number(QDateTime::currentSecsSinceEpoch()));
|
||||
QString fn = QFileDialog::getSaveFileName(this, "Save transaction to file", QDir::home().filePath(defaultName), "Transaction (*signed_monero_tx)");
|
||||
if(fn.isEmpty()) return;
|
||||
|
||||
m_tx->saveToFile(fn) ? QMessageBox::information(this, "Transaction saved to file", "Transaction saved successfully")
|
||||
: QMessageBox::warning(this, "Save transaction", "Failed to save transaction to file.");
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::unsignedQrCode() {
|
||||
if (m_tx->unsignedTxToBin().size() > 2953) {
|
||||
QMessageBox::warning(this, "Unable to show QR code", "Transaction size exceeds the maximum size for QR codes (2953 bytes).");
|
||||
return;
|
||||
}
|
||||
|
||||
QrCode qr(m_tx->unsignedTxToBin(), QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::LOW);
|
||||
auto *dialog = new QrCodeDialog(this, qr, "Unsigned Transaction");
|
||||
dialog->exec();
|
||||
dialog->deleteLater();
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::unsignedCopy() {
|
||||
Utils::copyToClipboard(m_tx->unsignedTxToBase64());
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::signedCopy() {
|
||||
Utils::copyToClipboard(m_tx->signedTxToHex(0));
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::signedQrCode() {
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::broadcastTransaction() {
|
||||
if (m_tx == nullptr) return;
|
||||
m_ctx->currentWallet->commitTransactionAsync(m_tx);
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void TxConfAdvDialog::closeDialog() {
|
||||
if (m_tx != nullptr)
|
||||
m_ctx->currentWallet->disposeTransaction(m_tx);
|
||||
if (m_utx != nullptr)
|
||||
m_ctx->currentWallet->disposeTransaction(m_utx);
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
TxConfAdvDialog::~TxConfAdvDialog() {
|
||||
delete ui;
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2020, The Monero Project.
|
||||
|
||||
#ifndef FEATHER_TXCONFADVDIALOG_H
|
||||
#define FEATHER_TXCONFADVDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QStandardItemModel>
|
||||
#include <QAbstractButton>
|
||||
#include <QMenu>
|
||||
|
||||
#include "libwalletqt/PendingTransaction.h"
|
||||
#include "appcontext.h"
|
||||
|
||||
namespace Ui {
|
||||
class TxConfAdvDialog;
|
||||
}
|
||||
|
||||
class TxConfAdvDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TxConfAdvDialog(AppContext *ctx, const QString &description, QWidget *parent = nullptr);
|
||||
~TxConfAdvDialog() override;
|
||||
|
||||
void setTransaction(PendingTransaction *tx);
|
||||
void setUnsignedTransaction(UnsignedTransaction *utx);
|
||||
|
||||
private:
|
||||
void setupConstructionData(ConstructionInfo *ci);
|
||||
void signTransaction();
|
||||
void broadcastTransaction();
|
||||
void closeDialog();
|
||||
|
||||
void unsignedCopy();
|
||||
void unsignedQrCode();
|
||||
void unsignedSaveFile();
|
||||
|
||||
void signedCopy();
|
||||
void signedQrCode();
|
||||
void signedSaveFile();
|
||||
|
||||
Ui::TxConfAdvDialog *ui;
|
||||
AppContext *m_ctx;
|
||||
PendingTransaction *m_tx = nullptr;
|
||||
UnsignedTransaction *m_utx = nullptr;
|
||||
QMenu *m_exportUnsignedMenu;
|
||||
QMenu *m_exportSignedMenu;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //FEATHER_TXCONFADVDIALOG_H
|
@ -0,0 +1,279 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TxConfAdvDialog</class>
|
||||
<widget class="QDialog" name="TxConfAdvDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>542</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>800</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Transaction</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Transaction ID:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="txid">
|
||||
<property name="text">
|
||||
<string>txid</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_amount">
|
||||
<property name="text">
|
||||
<string>Amount: </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="amount">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_fee">
|
||||
<property name="text">
|
||||
<string>Fee: </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="fee">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Total:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="total">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_description">
|
||||
<property name="text">
|
||||
<string>Description:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_size">
|
||||
<property name="text">
|
||||
<string>Size: </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_unlockTime">
|
||||
<property name="text">
|
||||
<string>Unlock time: </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_ringSize">
|
||||
<property name="text">
|
||||
<string>Ringsize:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Minimum</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_inputs">
|
||||
<property name="text">
|
||||
<string>Inputs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="inputs">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_outputs">
|
||||
<property name="text">
|
||||
<string>Outputs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="outputs">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QToolButton" name="btn_exportUnsigned">
|
||||
<property name="text">
|
||||
<string>Export unsigned</string>
|
||||
</property>
|
||||
<property name="popupMode">
|
||||
<enum>QToolButton::InstantPopup</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="btn_exportSigned">
|
||||
<property name="text">
|
||||
<string>Export signed</string>
|
||||
</property>
|
||||
<property name="popupMode">
|
||||
<enum>QToolButton::InstantPopup</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_sign">
|
||||
<property name="text">
|
||||
<string>Sign</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_send">
|
||||
<property name="text">
|
||||
<string>Send</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_close">
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -0,0 +1,53 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2014-2020, The Monero Project.
|
||||
|
||||
#include "ConstructionInfo.h"
|
||||
|
||||
#include "Input.h"
|
||||
#include "Transfer.h"
|
||||
|
||||
quint64 ConstructionInfo::unlockTime() const {
|
||||
return m_unlockTime;
|
||||
}
|
||||
|
||||
QSet<quint32> ConstructionInfo::subaddressIndices() const {
|
||||
return m_subaddressIndices;
|
||||
}
|
||||
|
||||
QVector<QString> ConstructionInfo::subaddresses() const {
|
||||
return m_subaddresses;
|
||||
}
|
||||
|
||||
quint64 ConstructionInfo::minMixinCount() const {
|
||||
return m_minMixinCount;
|
||||
}
|
||||
|
||||
QList<Input *> ConstructionInfo::inputs() const {
|
||||
return m_inputs;
|
||||
}
|
||||
|
||||
QList<Transfer *> ConstructionInfo::outputs() const {
|
||||
return m_outputs;
|
||||
}
|
||||
|
||||
ConstructionInfo::ConstructionInfo(const Monero::TransactionConstructionInfo *pimpl, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_unlockTime(pimpl->unlockTime())
|
||||
, m_minMixinCount(pimpl->minMixinCount())
|
||||
{
|
||||
for (auto const &i : pimpl->inputs())
|
||||
{
|
||||
Input *input = new Input(i.amount, QString::fromStdString(i.pubkey), this);
|
||||
m_inputs.append(input);
|
||||
}
|
||||
|
||||
for (auto const &o : pimpl->outputs())
|
||||
{
|
||||
Transfer *output = new Transfer(o.amount, QString::fromStdString(o.address), this);
|
||||
m_outputs.append(output);
|
||||
}
|
||||
for (uint32_t i : pimpl->subaddressIndices())
|
||||
{
|
||||
m_subaddressIndices.insert(i);
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2014-2020, The Monero Project.
|
||||
|
||||
#ifndef FEATHER_CONSTRUCTIONINFO_H
|
||||
#define FEATHER_CONSTRUCTIONINFO_H
|
||||
|
||||
#include <wallet/api/wallet2_api.h>
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
|
||||
class Input;
|
||||
class Transfer;
|
||||
|
||||
class ConstructionInfo : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(quint64 unlockTime READ unlockTime)
|
||||
Q_PROPERTY(QSet<quint32> subaddressIndices READ subaddressIndices)
|
||||
Q_PROPERTY(QVector<QString> subaddresses READ subaddresses)
|
||||
Q_PROPERTY(quint64 minMixinCount READ minMixinCount)
|
||||
Q_PROPERTY(QList<Input*> inputs READ inputs)
|
||||
Q_PROPERTY(QList<Transfer*> outputs READ outputs)
|
||||
|
||||
public:
|
||||
quint64 unlockTime() const;
|
||||
QSet<quint32> subaddressIndices() const;
|
||||
QVector<QString> subaddresses() const;
|
||||
quint64 minMixinCount() const;
|
||||
QList<Input*> inputs() const;
|
||||
QList<Transfer*> outputs() const;
|
||||
|
||||
private:
|
||||
explicit ConstructionInfo(const Monero::TransactionConstructionInfo *pimpl, QObject *parent = nullptr);
|
||||
|
||||
friend class PendingTransactionInfo;
|
||||
friend class UnsignedTransaction;
|
||||
quint64 m_unlockTime;
|
||||
QSet<quint32> m_subaddressIndices;
|
||||
QVector<QString> m_subaddresses;
|
||||
quint64 m_minMixinCount;
|
||||
mutable QList<Input*> m_inputs;
|
||||
mutable QList<Transfer*> m_outputs;
|
||||
};
|
||||
|
||||
|
||||
#endif //FEATHER_CONSTRUCTIONINFO_H
|
@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2014-2020, The Monero Project.
|
||||
|
||||
#ifndef FEATHER_INPUT_H
|
||||
#define FEATHER_INPUT_H
|
||||
|
||||
#include <wallet/api/wallet2_api.h>
|
||||
#include <QObject>
|
||||
#include <utility>
|
||||
|
||||
class Input : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(quint64 amount READ amount)
|
||||
Q_PROPERTY(QString pubKey READ pubKey)
|
||||
|
||||
private:
|
||||
explicit Input(uint64_t _amount, QString _address, QObject *parent = nullptr): QObject(parent), m_amount(_amount), m_pubkey(std::move(_address)) {};
|
||||
|
||||
friend class ConstructionInfo;
|
||||
quint64 m_amount;
|
||||
QString m_pubkey;
|
||||
public:
|
||||
quint64 amount() const { return m_amount; }
|
||||
QString pubKey() const { return m_pubkey; }
|
||||
|
||||
};
|
||||
|
||||
#endif //FEATHER_INPUT_H
|
@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2014-2020, The Monero Project.
|
||||
|
||||
#include "PendingTransactionInfo.h"
|
||||
#include "Input.h"
|
||||
#include "Transfer.h"
|
||||
|
||||
quint64 PendingTransactionInfo::fee() const {
|
||||
return m_fee;
|
||||
}
|
||||
|
||||
quint64 PendingTransactionInfo::dust() const {
|
||||
return m_dust;
|
||||
}
|
||||
|
||||
bool PendingTransactionInfo::dustAddedToFee() const {
|
||||
return m_dustAddedToFee;
|
||||
}
|
||||
|
||||
QString PendingTransactionInfo::txKey() const {
|
||||
return m_txKey;
|
||||
}
|
||||
|
||||
PendingTransactionInfo::PendingTransactionInfo(const Monero::PendingTransactionInfo *pimpl, QObject *parent)
|
||||
: ConstructionInfo(pimpl->constructionData(), parent)
|
||||
, m_fee(pimpl->fee())
|
||||
, m_dust(pimpl->dust())
|
||||
, m_dustAddedToFee(pimpl->dustAddedToFee())
|
||||
, m_txKey(QString::fromStdString(pimpl->txKey()))
|
||||
{
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
// Copyright (c) 2014-2020, The Monero Project.
|
||||
|
||||
#ifndef FEATHER_PENDINGTRANSACTIONINFO_H
|
||||
#define FEATHER_PENDINGTRANSACTIONINFO_H
|
||||
|
||||
#include <wallet/api/wallet2_api.h>
|
||||
#include "ConstructionInfo.h"
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
|
||||
class Input;
|
||||
class Transfer;
|
||||
|
||||
class PendingTransactionInfo : public ConstructionInfo
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(quint64 fee READ fee)
|
||||
Q_PROPERTY(quint64 dust READ dust)
|
||||
Q_PROPERTY(bool dustAddedToFee READ dustAddedToFee)
|
||||
Q_PROPERTY(QString txKey READ txKey)
|
||||
Q_PROPERTY(quint64 unlockTime READ unlockTime)
|
||||
Q_PROPERTY(QSet<quint32> subaddressIndices READ subaddressIndices)
|
||||
Q_PROPERTY(QVector<QString> subaddresses READ subaddresses)
|
||||
Q_PROPERTY(quint64 minMixinCount READ minMixinCount)
|
||||
Q_PROPERTY(QList<Input*> inputs READ inputs)
|
||||
Q_PROPERTY(QList<Transfer*> outputs READ outputs)
|
||||
|
||||
public:
|
||||
quint64 fee() const;
|
||||
quint64 dust() const;
|
||||
bool dustAddedToFee() const;
|
||||
QString txKey() const;
|
||||
|
||||
private:
|
||||
explicit PendingTransactionInfo(const Monero::PendingTransactionInfo *pimpl, QObject *parent = nullptr);
|
||||
|
||||
friend class PendingTransaction;
|
||||
quint64 m_fee;
|
||||
quint64 m_dust;
|
||||
bool m_dustAddedToFee;
|
||||
QString m_txKey;
|
||||
};
|
||||
|
||||
|
||||
#endif //FEATHER_PENDINGTRANSACTIONINFO_H
|
@ -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;
|
||||
}
|
@ -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
|
Loading…
Reference in new issue