1. Connect signals to make status of swap reflected in AtomicSwap dialog
     2. Add informational tabs to AtomicSwap dialog
TODO:
     3. Add cancel and refund functionality to AtomicSwap when things go wrong, possibly add automatic cancel functionality
     4. Add History and recovery to atomic window
This commit is contained in:
twiddle 2024-07-23 16:21:00 -04:00
parent 8744138b42
commit 08328d96f4
9 changed files with 180 additions and 154 deletions

View file

@ -41,6 +41,7 @@
<file>assets/images/file_manager_32px.png</file>
<file>assets/images/gnome-calc.png</file>
<file>assets/images/atomic-icon.png</file>
<file>assets/images/hint-icon.png</file>
<file>assets/images/hd_32px.png</file>
<file>assets/images/history.png</file>
<file>assets/images/i2p.png</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View file

@ -8,20 +8,21 @@
#include <QFileDialog>
#include <QMessageBox>
AtomicFundDialog::AtomicFundDialog(QWidget *parent, QrCode *qrCode, const QString &title, const QString &btc_address)
AtomicFundDialog::AtomicFundDialog(QWidget *parent, const QString &title, const QString &btc_address)
: WindowModalDialog(parent)
, ui(new Ui::AtomicFundDialog)
, address(btc_address)
, qrCode(address, QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::HIGH)
{
ui->setupUi(this);
this->setWindowTitle(title);
ui->qrWidget->setQrCode(qrCode);
m_pixmap = qrCode->toPixmap(1).scaled(500, 500, Qt::KeepAspectRatio);
ui->qrWidget->setQrCode(&qrCode);
m_pixmap = qrCode.toPixmap(1).scaled(500, 500, Qt::KeepAspectRatio);
connect(ui->btn_CopyAddress, &QPushButton::clicked, this, &AtomicFundDialog::copyAddress);
connect(ui->btn_CopyImage, &QPushButton::clicked, this, &AtomicFundDialog::copyImage);
connect(ui->btn_Save, &QPushButton::clicked, this, &AtomicFundDialog::saveImage);
connect(ui->btn_Close, &QPushButton::clicked, [this](){
emit cleanProcs();
accept();
});

View file

@ -20,7 +20,7 @@ class AtomicFundDialog : public WindowModalDialog {
Q_OBJECT
public:
explicit AtomicFundDialog(QWidget *parent, QrCode *qrCode, const QString &title = "Qr Code", const QString &btc_address = "Error Restart swap");
explicit AtomicFundDialog(QWidget *parent, const QString &title = "Qr Code", const QString &btc_address = "Error Restart swap");
~AtomicFundDialog() override;
signals:
void cleanProcs();
@ -31,6 +31,7 @@ private:
QScopedPointer<Ui::AtomicFundDialog> ui;
QPixmap m_pixmap;
QString address;
QrCode qrCode;
};

View file

@ -1,111 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AtomicFundDialog</class>
<widget class="QDialog" name="AtomicFundDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>520</width>
<height>446</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QrCodeWidget" name="qrWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>150</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<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_CopyAddress">
<property name="text">
<string>Copy Address</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_CopyImage">
<property name="text">
<string>Copy Image</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_Save">
<property name="text">
<string>Save</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_Close">
<property name="text">
<string>Cancel</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
<class>AtomicFundDialog</class>
<widget class="QDialog" name="AtomicFundDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>520</width>
<height>446</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QrCodeWidget" name="qrWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>150</height>
</size>
</property>
</widget>
<customwidgets>
<customwidget>
<class>QrCodeWidget</class>
<extends>QWidget</extends>
<header>widgets/QrCodeWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::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_CopyAddress">
<property name="text">
<string>Copy Address</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_CopyImage">
<property name="text">
<string>Copy Image</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_Save">
<property name="text">
<string>Save</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_Close">
<property name="text">
<string>Close</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QrCodeWidget</class>
<extends>QWidget</extends>
<header>widgets/QrCodeWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View file

@ -14,6 +14,11 @@ AtomicSwap::AtomicSwap(QWidget *parent) :
ui->setupUi(this);
//ui->debug_log->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
ui->label_status->setTextInteractionFlags(Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse);
QPixmap pixmapTarget = QPixmap(":/assets/images/hint-icon.png");
int size=20;
pixmapTarget = pixmapTarget.scaled(size-5, size-5, Qt::KeepAspectRatio, Qt::SmoothTransformation);
ui->btc_hint->setPixmap(pixmapTarget);
ui->btc_hint->setToolTip("Alice is expected to send monero lock after one btc confirmation,\nswap is cancelable after 72 btc confirmations,\nyou will lose your funds if you don't refund before 144 confirmations");
this->setContentsMargins(3,3,3,3);
this->adjustSize();
}

View file

@ -95,6 +95,16 @@
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="btc_hint">
<property name="pixmap">
<pixmap>hint-icon.png</pixmap>
</property>
<property name="textFormat">
<enum>Qt::TextFormat::PlainText</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_xmr">
<property name="text">

View file

@ -23,6 +23,7 @@ AtomicWidget::AtomicWidget(QWidget *parent)
, offerList(new QList<QSharedPointer<OfferEntry>>())
, swapDialog(new AtomicSwap(this))
, procList(new QList<QSharedPointer<QProcess>>())
, fundDialog(new AtomicFundDialog(this))
{
ui->setupUi(this);
@ -58,16 +59,20 @@ AtomicWidget::AtomicWidget(QWidget *parent)
connect(ui->btn_swap, &QPushButton::clicked, this, [this]{
auto rows = ui->offerBookTable->selectionModel()->selectedRows();
if (rows.size() < 1){
ui->meta_label->setText("You must select an offer to use for swap, refresh if there aren't any");
} else {
QModelIndex index = rows.at(0);
QString seller = index.sibling(index.row(), 3).data().toString();
clean();
// UNCOMENT after testing
//if (rows.size() < 1){
// ui->meta_label->setText("You must select an offer to use for swap, refresh if there aren't any");
//} else {
//QModelIndex index = rows.at(0);
//QString seller = index.sibling(index.row(), 3).data().toString();
QString seller = "test";
//Add proper error checking on ui input after rest of swap is implemented
QString btcChange = ui->change_address->text();
QString xmrReceive = ui->xmr_address->text();
sleep(1);
runSwap(seller,btcChange, xmrReceive);
}
//}
});
connect(ui->btn_addRendezvous, &QPushButton::clicked, this, [this]{
@ -84,7 +89,6 @@ AtomicWidget::AtomicWidget(QWidget *parent)
});
connect(swapDialog,&AtomicSwap::cleanProcs, this, [this]{clean();});
this->updateStatus();
}
@ -95,10 +99,7 @@ void AtomicWidget::skinChanged() {
void AtomicWidget::showAtomicConfigureDialog() {
AtomicConfigDialog dialog{this};
if (dialog.exec() == QDialog::Accepted) {
}
dialog.show();
}
void AtomicWidget::showAtomicSwapDialog() {
@ -115,57 +116,62 @@ void AtomicWidget::runSwap(const QString& seller, const QString& btcChange, cons
arguments << "--data-base-dir";
arguments << Config::defaultConfigDir().absolutePath();
// Remove after testing
//arguments << "--testnet";
arguments << "--testnet";
arguments << "--debug";
arguments << "-j";
arguments << "buy-xmr";
arguments << "--change-address";
//arguments << "tb1qzndh6u8qgl2ee4k4gl9erg947g67hyx03vvgen";
arguments << btcChange;
arguments << "tb1qzndh6u8qgl2ee4k4gl9erg947g67hyx03vvgen";
//arguments << btcChange;
arguments << "--receive-address";
//arguments << "78YnzFTp3UUMgtKuAJCP2STcbxRZPDPveJ5YGgfg5doiPahS9suWF1r3JhKqjM1McYBJvu8nhkXExGfXVkU6n5S6AXrg4KP";
arguments << xmrReceive;
arguments << "78YnzFTp3UUMgtKuAJCP2STcbxRZPDPveJ5YGgfg5doiPahS9suWF1r3JhKqjM1McYBJvu8nhkXExGfXVkU6n5S6AXrg4KP";
//arguments << xmrReceive;
arguments << "--seller";
//arguments << "/ip4/127.0.0.1/tcp/9939/p2p/12D3KooWQA4fXDYLNXgxPsVZmnR8kh2wwHUQnkH9e1Wjc8KyJ7p8";
arguments << "/ip4/127.0.0.1/tcp/9939/p2p/12D3KooWQA4fXDYLNXgxPsVZmnR8kh2wwHUQnkH9e1Wjc8KyJ7p8";
// Remove after testing
//arguments << "--electrum-rpc";
//arguments << "tcp://127.0.0.1:50001";
//arguments << "--bitcoin-target-block";
//arguments << "8";
//arguments << "--monero-daemon-address";
//arguments << "http://127.0.0.1:38083";
arguments << "--electrum-rpc";
arguments << "tcp://127.0.0.1:50001";
arguments << "--bitcoin-target-block";
arguments << "1";
arguments << "--monero-daemon-address";
arguments << "node.monerodevs.org:38089";
// Uncomment after testing
arguments << seller;
//arguments << seller;
arguments << "--tor-socks5-port";
arguments << m_instance->get(Config::socks5Port).toString();
auto *swap = new QProcess();
procList->append(QSharedPointer<QProcess>(swap));
swap->setReadChannel(QProcess::StandardError);
connect(swap, &QProcess::readyReadStandardError,this, [this, swap] {
swap->setProcessChannelMode(QProcess::MergedChannels);
swap->setReadChannel(QProcess::StandardOutput);
connect(swap, &QProcess::readyRead,this, [this, swap] {
while(swap->canReadLine()){
QJsonParseError err;
const QByteArray& rawline = swap->readLine();
QJsonDocument line = QJsonDocument::fromJson(rawline, &err);
qDebug() << rawline;
bool check;
if (line["fields"]["message"].toString().contains("Connected to Alice")){
qDebug() << "Successfully connected";
swapDialog->logLine(line["fields"].toString());
} else if (!line["fields"]["deposit_address"].toString().isEmpty()){
qDebug() << "Deposit to btc to segwit address";
QString address = line["fields"]["deposit_address"].toString();
QrCode qrc(address, QrCode::Version::AUTO, QrCode::ErrorCorrectionLevel::HIGH);
AtomicFundDialog dialog(qobject_cast<QWidget*>(parent()), &qrc, "Deposit BTC to this address", address);
connect(&dialog,&AtomicFundDialog::cleanProcs, this, [this]{clean();});
connect(this, &AtomicWidget::receivedBTC,&dialog, [ &dialog]{
disconnect(&dialog, SIGNAL(cleanProcs()), nullptr, nullptr);
dialog.close();});
dialog.exec();
fundDialog = new AtomicFundDialog(this, "Deposit BTC to this address", address);
//dialog->setModal(true);
fundDialog->show();
} else if (line["fields"]["message"].toString().startsWith("Received Bitcoin")){
emit receivedBTC(line["fields"]["new_balance"].toString().split(" ")[0].toFloat());
swapDialog->updateStatus(line["fields"]["new_balance"].toString().split(" ")[0] + " BTC received, starting swap");
fundDialog->close();
qDebug() << "Spawn atomic swap progress dialog";
showAtomicSwapDialog();
} else if ( QString confs = line["fields"]["seen_confirmations"].toString(); !confs.isEmpty()){
qDebug() << "Updating xmrconfs " + confs;
swapDialog->updateXMRConf(confs.toInt());
} else if (QString message = line["fields"]["message"].toString(); !QString::compare(message, "Bitcoin transaction status changed")){
qDebug() << "Updating btconfs " + line["fields"]["new_status"].toString().split(" ")[2];
swapDialog->updateBTCConf(line["fields"]["new_status"].toString().split(" ")[2].toInt());
}
//Insert line conditionals here
}
@ -245,15 +251,16 @@ AtomicWidget::~AtomicWidget() {
void AtomicWidget::clean() {
for (const auto& proc : *procList){
if(!proc->atEnd())
proc->terminate();
proc->kill();
}
if(QString::compare("WINDOWS",m_instance->get(Config::operatingSystem).toString()) != 0) {
qDebug() << "Closing monero-wallet-rpc";
(new QProcess)->start("pkill", QStringList{"-f", Config::defaultConfigDir().absolutePath() +
(new QProcess)->start("kill", QStringList{"-f", Config::defaultConfigDir().absolutePath() +
"/mainnet/monero/monero-wallet-rpc"});
(new QProcess)->start("pkill", QStringList{"-f", Config::defaultConfigDir().absolutePath() +
(new QProcess)->start("kill", QStringList{"-f", Config::defaultConfigDir().absolutePath() +
"/testnet/monero/monero-wallet-rpc"});
}
}

View file

@ -13,6 +13,7 @@
#include "OfferModel.h"
#include "AtomicSwap.h"
#include "config.h"
#include "AtomicFundDialog.h"
namespace Ui {
class AtomicWidget;
@ -34,8 +35,7 @@ public slots:
private slots:
void showAtomicConfigureDialog();
void runSwap(const QString& seller, const QString& btcChange, const QString& xmrReceive);
signals:
void receivedBTC(float new_amount);
private:
void updateStatus();
@ -45,6 +45,7 @@ private:
OfferModel *o_model;
QList<QSharedPointer<OfferEntry>> *offerList;
AtomicSwap *swapDialog;
AtomicFundDialog *fundDialog;
void showAtomicSwapDialog();