Minor Refactoring and addition of behind the scene changes to swap settings based on feather configuration.

Atomic will no longer appear if feather is launched in testnet mode.
DONE:
     1. Connect signals to make status of swap reflected in AtomicSwap dialog
     2. Add informational tabs to AtomicSwap dialog
     4. Add recovery to atomic widget
     5. Refactor AtomicWidget so AtomicSwap handles parsing of swap binary output.
TODO:
     3. Add cancel and refund functionality to AtomicSwap when things go wrong
This commit is contained in:
twiddle 2024-08-08 23:49:37 -04:00
parent c67fedf9bc
commit 41f78b5f89
10 changed files with 165 additions and 56 deletions

View file

@ -69,6 +69,7 @@ MainWindow::MainWindow(WindowManager *windowManager, Wallet *wallet, QWidget *pa
this->restoreGeo();
this->initStatusBar();
this->initPlugins();
this->initWidgets();
this->initMenu();

View file

@ -6,6 +6,7 @@
#include "Plugin.h"
#include "utils/config.h"
#include "constants.h"
class PluginRegistry {
public:
@ -35,7 +36,7 @@ public:
}
bool isPluginEnabled(const QString &id) {
if (!pluginMap.contains(id)) {
if (!pluginMap.contains(id) or (QString::compare(id,"atomic")==0 && constants::networkType==NetworkType::TESTNET)) {
return false;
}

View file

@ -90,7 +90,7 @@ void AtomicConfigDialog::extract() {
swapPath.append("/swapTool");
QFile binaryFile(swapPath);
binaryFile.open(QIODevice::WriteOnly);
auto operatingSystem = Config::instance()->get(Config::operatingSystem).toString().toStdString();
auto operatingSystem = conf()->get(Config::operatingSystem).toString().toStdString();
if(strcmp("WIN",operatingSystem.c_str()) == 0) {
// UNZIP
zip *z = zip_open(tempFile.toStdString().c_str(), 0, 0);
@ -114,7 +114,7 @@ void AtomicConfigDialog::extract() {
zip_fclose(f);
//And close the archive
zip_close(z);
Config::instance()->set(Config::swapPath,swapPath);
conf()->set(Config::swapPath,swapPath);
} else {
struct archive *a;
@ -145,7 +145,7 @@ void AtomicConfigDialog::extract() {
archive_write_close(ext);
archive_write_free(ext);
Config::instance()->set(Config::swapPath, QString(savePath.c_str()));
conf()->set(Config::swapPath, QString(savePath.c_str()));
}
qDebug() << "Finished";
binaryFile.close();

View file

@ -5,6 +5,7 @@
#include "AtomicConfigDialog.h"
#include "plugins/PluginRegistry.h"
#include "constants.h"
AtomicPlugin::AtomicPlugin()
{
@ -64,7 +65,11 @@ void AtomicPlugin::skinChanged() {
}
const bool AtomicPlugin::registered = [] {
if(constants::networkType!=NetworkType::TESTNET) {
PluginRegistry::registerPlugin(AtomicPlugin::create());
PluginRegistry::getInstance().registerPluginCreator(&AtomicPlugin::create);
return true;
}
return false;
}();

View file

@ -8,20 +8,27 @@
#include "ui_AtomicRecoverDialog.h"
#include "History.h"
#include "config.h"
#include "AtomicSwap.h"
#include <QStandardItemModel>
AtomicRecoverDialog::AtomicRecoverDialog(QWidget *parent) :
WindowModalDialog(parent), ui(new Ui::AtomicRecoverDialog) {
WindowModalDialog(parent), ui(new Ui::AtomicRecoverDialog), swapDialog(new AtomicSwap(this)) {
ui->setupUi(this);
auto model = new QStandardItemModel();
ui->swap_history->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
ui->swap_history->setModel(model);
ui->btn_refund_resume->setVisible(false);
ui->swap_history->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->swap_history->verticalHeader()->setVisible(false);
// Makes it easy to see if button is in refund or resume mode
ui->btn_refund_resume->setProperty("Refund",0);
this->setWindowFlag(Qt::WindowStaysOnTopHint);
model->setHorizontalHeaderItem(0, new QStandardItem("Swap-Id"));
model->setHorizontalHeaderItem(1, new QStandardItem("Timestamp swap started"));
model->setHorizontalHeaderItem(2, new QStandardItem("Status"));
QList<QStandardItem*> rowData;
auto data = Config::instance()->get(Config::pendingSwap).value<QVariantList>();
auto data = conf()->get(Config::pendingSwap).value<QVariantList>();
for(int i=0; i< data.size(); i++){
auto entry = data[i].value<HistoryEntry>();
qint64 difference = entry.timestamp.secsTo(QDateTime::currentDateTime());
@ -39,20 +46,47 @@ AtomicRecoverDialog::AtomicRecoverDialog(QWidget *parent) :
data.remove(i);
}
}
Config::instance()->set(Config::pendingSwap,data);
conf()->set(Config::pendingSwap,data);
connect(ui->swap_history, &QAbstractItemView::clicked, this, &AtomicRecoverDialog::updateBtn);
connect(ui->btn_refund_resume, &QPushButton::clicked, this, [this]{
QStringList arguments;
if (ui->btn_refund_resume->property("Refund").toBool()){
arguments << "cancel-and-refund";
} else {
arguments << "resume";
}
arguments << "--swap-id";
arguments << ui->swap_history->selectionModel()->selectedRows().at(0).sibling(0,1).data().toString();
arguments << "--tor-socks5-port";
arguments << conf()->get(Config::socks5Port).toString();
swapDialog->runSwap(arguments);
});
}
void AtomicRecoverDialog::updateBtn(const QModelIndex &index){
auto row = index.sibling(index.row(),2).data().toString();
if (row.startsWith("Ref")){
ui->btn_refund_resume->setText("Cancel And Refund");
ui->btn_refund_resume->setProperty("Refund",1);
} else {
ui->btn_refund_resume->setText("Attempt to Resume");
ui->btn_refund_resume->setProperty("Refund",0);
}
ui->btn_refund_resume->setVisible(true);
}
bool AtomicRecoverDialog::historyEmpty(){
return Config::instance()->get(Config::pendingSwap).value<QVariantList>().isEmpty();
return conf()->get(Config::pendingSwap).value<QVariantList>().isEmpty();
}
void AtomicRecoverDialog::appendHistory(HistoryEntry entry){
auto current = Config::instance()->get(Config::pendingSwap).value<QVariantList>();
auto current = conf()->get(Config::pendingSwap).value<QVariantList>();
auto var = QVariant();
var.setValue(entry);
current.append(var);
Config::instance()->set(Config::pendingSwap, current);
conf()->set(Config::pendingSwap, current);
}
AtomicRecoverDialog::~AtomicRecoverDialog() {
delete ui;

View file

@ -8,6 +8,7 @@
#include <QDialog>
#include "components.h"
#include "History.h"
#include "AtomicSwap.h"
QT_BEGIN_NAMESPACE
namespace Ui { class AtomicRecoverDialog; }
@ -22,8 +23,12 @@ public:
void appendHistory(HistoryEntry entry);
~AtomicRecoverDialog() override;
private slots:
void updateBtn(const QModelIndex &index);
private:
Ui::AtomicRecoverDialog *ui;
AtomicSwap *swapDialog;
};

View file

@ -6,22 +6,71 @@
<rect>
<x>0</x>
<y>0</y>
<width>903</width>
<height>248</height>
<width>936</width>
<height>256</height>
</rect>
</property>
<property name="windowTitle">
<string>AtomicRecoverDialog</string>
</property>
<widget class="QTableView" name="swap_history">
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>20</y>
<width>881</width>
<height>179</height>
<y>10</y>
<width>911</width>
<height>211</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>There are one or more Atomic Swaps still pending, take action to prevent losing funds!</string>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="swap_history"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer_2">
<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_refund_resume">
<property name="text">
<string>Resume</string>
</property>
</widget>
</item>
<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>
</layout>
</item>
</layout>
</widget>
</widget>
<resources/>

View file

@ -60,13 +60,21 @@ void AtomicSwap::runSwap(QStringList arguments){
this->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];
this->updateBTCConf(line["fields"]["new_status"].toString().split(" ")[2].toInt());
QString status = line["fields"]["new_status"].toString();
bool ok;
auto confirmations = status.split(" ")[2].toInt(&ok, 10);
if(ok) {
this->updateBTCConf(confirmations);
} else {
this->updateStatus("Found txid " + line["fields"]["txid"].toString() + " in mempool");
}
}
//Insert line conditionals here
}
});
swap->start(Config::instance()->get(Config::swapPath).toString(),arguments);
swap->start(conf()->get(Config::swapPath).toString(),arguments);
qDebug() << "process started";
}
AtomicSwap::~AtomicSwap() {
@ -74,7 +82,7 @@ AtomicSwap::~AtomicSwap() {
for (const auto& proc : *procList){
proc->kill();
}
if(QString::compare("WINDOWS",Config::instance()->get(Config::operatingSystem).toString()) != 0) {
if(QString::compare("WINDOWS",conf()->get(Config::operatingSystem).toString()) != 0) {
qDebug() << "Closing monero-wallet-rpc";
(new QProcess)->start("kill", QStringList{"-f", Config::defaultConfigDir().absolutePath() +
"/mainnet/monero/monero-wallet-rpc"});

View file

@ -11,6 +11,7 @@
#include "AtomicConfigDialog.h"
#include "OfferModel.h"
#include "utils/AppData.h"
#include "utils/nodes.h"
#include "utils/ColorScheme.h"
#include "utils/WebsocketNotifier.h"
#include "AtomicFundDialog.h"
@ -18,7 +19,6 @@
AtomicWidget::AtomicWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::AtomicWidget)
, m_instance(Config::instance())
, o_model(new OfferModel(this))
, offerList(new QList<QSharedPointer<OfferEntry>>())
, swapDialog(new AtomicSwap(this))
@ -43,7 +43,7 @@ AtomicWidget::AtomicWidget(QWidget *parent)
ui->offerBookTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch);
ui->btn_configure->setEnabled(true);
if (!m_instance->get(Config::swapPath).toString().isEmpty())
if (!conf()->get(Config::swapPath).toString().isEmpty())
ui->meta_label->setText("Refresh offer book before swapping to prevent errors");
connect(ui->btn_configure, &QPushButton::clicked, this, &AtomicWidget::showAtomicConfigureDialog);
@ -53,7 +53,7 @@ AtomicWidget::AtomicWidget(QWidget *parent)
ui->meta_label->setText("Updating offer book this may take a bit, if no offers appear after a while try refreshing again");
QStringList pointList = m_instance->get(Config::rendezVous).toStringList();
QStringList pointList = conf()->get(Config::rendezVous).toStringList();
for(const QString& point :pointList)
AtomicWidget::list(point);
});
@ -83,22 +83,21 @@ AtomicWidget::AtomicWidget(QWidget *parent)
tr("p2p multi address of rendezvous point"), QLineEdit::Normal,
"", &ok);
if (ok && !text.isEmpty()) {
QStringList copy = m_instance->get(Config::rendezVous).toStringList();
QStringList copy = conf()->get(Config::rendezVous).toStringList();
copy.append(text);
m_instance->set(Config::rendezVous,copy);
conf()->set(Config::rendezVous,copy);
}
});
//Remove after testing
//QVariant var;
//var.setValue(HistoryEntry {QDateTime::currentDateTime(),"test-id"});
//m_instance->set(Config::pendingSwap, QVariantList{var});
//auto recd = new AtomicRecoverDialog();
//if (!recd->historyEmpty()){
// recd->show();
//}
this->updateStatus();
QVariant var;
var.setValue(HistoryEntry {QDateTime::currentDateTime(),"test-id"});
conf()->set(Config::pendingSwap, QVariantList{var});
auto recd = new AtomicRecoverDialog(this);
if (!recd->historyEmpty()){
recd->show();
}
}
@ -111,13 +110,6 @@ void AtomicWidget::showAtomicConfigureDialog() {
dialog.exec();
}
void AtomicWidget::showAtomicSwapDialog() {
swapDialog->show();
}
void AtomicWidget::updateStatus() {
}
void AtomicWidget::runSwap(const QString& seller, const QString& btcChange, const QString& xmrReceive) {
qDebug() << "starting swap";
@ -125,7 +117,9 @@ void AtomicWidget::runSwap(const QString& seller, const QString& btcChange, cons
arguments << "--data-base-dir";
arguments << Config::defaultConfigDir().absolutePath();
// Remove after testing
if (constants::networkType==NetworkType::STAGENET){
arguments << "--testnet";
}
arguments << "--debug";
arguments << "-j";
arguments << "buy-xmr";
@ -142,12 +136,24 @@ void AtomicWidget::runSwap(const QString& seller, const QString& btcChange, cons
arguments << "tcp://127.0.0.1:50001";
arguments << "--bitcoin-target-block";
arguments << "1";
auto nodes = conf()->get(Config::nodes).toJsonObject();
if (nodes.isEmpty()) {
auto jsonData = conf()->get(Config::nodes).toByteArray();
if (Utils::validateJSON(jsonData)) {
auto doc = QJsonDocument::fromJson(jsonData);
nodes = doc.object();
}
}
qDebug() << nodes.value("0").toObject()["ws"].toArray()[0];
arguments << "--monero-daemon-address";
arguments << "node.monerodevs.org:38089";
//arguments << "node.monerodevs.org:38089";
arguments << nodes.value("0").toObject()["ws"].toArray()[0].toString();
// Uncomment after testing
//arguments << seller;
if(conf()->get(Config::proxy).toInt() != Config::Proxy::None) {
arguments << "--tor-socks5-port";
arguments << m_instance->get(Config::socks5Port).toString();
arguments << conf()->get(Config::socks5Port).toString();
}
swapDialog->runSwap(arguments);
}
@ -156,16 +162,21 @@ void AtomicWidget::list(const QString& rendezvous) {
QStringList arguments;
arguments << "--data-base-dir";
arguments << Config::defaultConfigDir().absolutePath();
if (constants::networkType==NetworkType::STAGENET){
arguments << "--testnet";
}
arguments << "-j";
arguments << "list-sellers";
if(conf()->get(Config::proxy).toInt() != Config::Proxy::None) {
arguments << "--tor-socks5-port";
arguments << m_instance->get(Config::socks5Port).toString();
arguments << conf()->get(Config::socks5Port).toString();
}
arguments << "--rendezvous-point";
arguments << rendezvous;
auto *swap = new QProcess();
procList->append(QSharedPointer<QProcess>(swap));
swap->setReadChannel(QProcess::StandardError);
//swap->start(m_instance->get(Config::swapPath).toString(), arguments);
//swap->start(conf()->get(Config::swapPath).toString(), arguments);
connect(swap, &QProcess::finished, this, [this, swap]{
QJsonDocument parsedLine;
QJsonParseError parseError;
@ -203,7 +214,7 @@ void AtomicWidget::list(const QString& rendezvous) {
o_model->updateOffers(*offerList);
return list;
});
swap->start(m_instance->get(Config::swapPath).toString(), arguments);
swap->start(conf()->get(Config::swapPath).toString(), arguments);
//swap->waitForFinished(120000);
@ -216,7 +227,6 @@ AtomicWidget::~AtomicWidget() {
delete o_model;
delete offerList;
clean();
delete m_instance;
delete procList;
}
@ -224,7 +234,7 @@ void AtomicWidget::clean() {
for (const auto& proc : *procList){
proc->kill();
}
if(QString::compare("WINDOWS",m_instance->get(Config::operatingSystem).toString()) != 0) {
if(QString::compare("WINDOWS",conf()->get(Config::operatingSystem).toString()) != 0) {
qDebug() << "Closing monero-wallet-rpc";
(new QProcess)->start("kill", QStringList{"-f", Config::defaultConfigDir().absolutePath() +
"/mainnet/monero/monero-wallet-rpc"});

View file

@ -38,8 +38,6 @@ private slots:
void runSwap(const QString& seller, const QString& btcChange, const QString& xmrReceive);
private:
void updateStatus();
QScopedPointer<Ui::AtomicWidget> ui;
bool m_comboBoxInit = false;
QTimer m_statusTimer;
@ -48,10 +46,8 @@ private:
AtomicSwap *swapDialog;
AtomicFundDialog *fundDialog;
AtomicRecoverDialog *recoverDialog;
void showAtomicSwapDialog();
QList<QSharedPointer<QProcess>> *procList;
Config *m_instance;
};
#endif // FEATHER_ATOMICWIDGET_H