From 12bbc76e26276bb4624990ca687ec58323632a77 Mon Sep 17 00:00:00 2001 From: twiddle Date: Sat, 10 Aug 2024 21:03:23 -0400 Subject: [PATCH] Version 0.9 DONE: 1. Connect signals to make status of swap reflected in AtomicSwap dialog 2. Add informational tabs to AtomicSwap dialog 3. Add cancel and refund functionality to AtomicSwap when things go wrong 4. Add recovery to atomic widget 5. Refactor AtomicWidget so AtomicSwap handles parsing of swap binary output. TODO: Test for bugs, make custom readme, build for other systems --- src/plugins/atomic/AtomicConfigDialog.cpp | 9 ++- src/plugins/atomic/AtomicRecoverDialog.cpp | 17 ++++- src/plugins/atomic/AtomicSwap.cpp | 51 +++++++------ src/plugins/atomic/AtomicSwap.h | 1 - src/plugins/atomic/AtomicWidget.cpp | 84 ++++++++++------------ src/plugins/atomic/OfferModel.cpp | 1 - src/utils/config.cpp | 1 + src/utils/config.h | 1 + 8 files changed, 91 insertions(+), 74 deletions(-) diff --git a/src/plugins/atomic/AtomicConfigDialog.cpp b/src/plugins/atomic/AtomicConfigDialog.cpp index c9dbfe5..b627074 100644 --- a/src/plugins/atomic/AtomicConfigDialog.cpp +++ b/src/plugins/atomic/AtomicConfigDialog.cpp @@ -58,13 +58,14 @@ void AtomicConfigDialog::downloadBinary() { tempFile = download->fileName(); QString url; auto operatingSystem = Config::instance()->get(Config::operatingSystem).toString().toStdString(); + QString firstPart = "https://github.com/comit-network/xmr-btc-swap/releases/download/" + conf()->get(Config::swapVersion).toString(); if(strcmp("WIN",operatingSystem.c_str()) == 0) { // HARD CODED DOWNload URL CHANGE IF PROBLEMS - url = QString("https://github.com/comit-network/xmr-btc-swap/releases/download/0.13.1/swap_0.13.1_Windows_x86_64.zip"); + url = QString(firstPart+"/swap_"+conf()->get(Config::swapVersion).toString()+"_Windows_x86_64.zip"); } else if (strcmp("LINUX",operatingSystem.c_str())==0){ - url = QString("https://github.com/comit-network/xmr-btc-swap/releases/download/0.13.1/swap_0.13.1_Linux_x86_64.tar"); + url = QString(firstPart+"/swap_"+conf()->get(Config::swapVersion).toString()+"_Linux_x86_64.tar"); } else { - url = QString("https://github.com/comit-network/xmr-btc-swap/releases/download/0.13.1/swap_0.13.1_Linux_x86_64.tar"); + url = QString(firstPart + "/swap_" + conf()->get(Config::swapVersion).toString() + "_Darwin_x86_64.tar"); } archive = network->get(this, url); @@ -72,7 +73,6 @@ void AtomicConfigDialog::downloadBinary() { QStringList answer; connect(archive,&QNetworkReply::readyRead, this, [this]{ QByteArray data= archive->readAll(); - qDebug() << "received data of size: " << data.size(); download->write(data.constData(), data.size()); }); connect(archive, &QNetworkReply::finished, @@ -82,7 +82,6 @@ void AtomicConfigDialog::downloadBinary() { void AtomicConfigDialog::extract() { ui->downloadLabel->setText("Download Successful, extracting binary to final destination"); - qDebug() << "extracting"; download->close(); archive->deleteLater(); diff --git a/src/plugins/atomic/AtomicRecoverDialog.cpp b/src/plugins/atomic/AtomicRecoverDialog.cpp index fa00416..e6cd086 100644 --- a/src/plugins/atomic/AtomicRecoverDialog.cpp +++ b/src/plugins/atomic/AtomicRecoverDialog.cpp @@ -9,6 +9,7 @@ #include "History.h" #include "config.h" #include "AtomicSwap.h" +#include "Utils.h" #include AtomicRecoverDialog::AtomicRecoverDialog(QWidget *parent) : @@ -57,8 +58,20 @@ AtomicRecoverDialog::AtomicRecoverDialog(QWidget *parent) : } 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(); + arguments << "--monero-daemon-address"; + 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(); + } + } + arguments << nodes.value("0").toObject()["ws"].toArray()[0].toString(); + if(conf()->get(Config::proxy).toInt() != Config::Proxy::None) { + arguments << "--tor-socks5-port"; + arguments << conf()->get(Config::socks5Port).toString(); + } swapDialog->runSwap(arguments); }); } diff --git a/src/plugins/atomic/AtomicSwap.cpp b/src/plugins/atomic/AtomicSwap.cpp index 7fa80d9..74604dc 100644 --- a/src/plugins/atomic/AtomicSwap.cpp +++ b/src/plugins/atomic/AtomicSwap.cpp @@ -6,8 +6,8 @@ #include "AtomicSwap.h" -#include #include +#include #include "ui_AtomicSwap.h" #include "AtomicWidget.h" @@ -15,7 +15,6 @@ AtomicSwap::AtomicSwap(QWidget *parent) : WindowModalDialog(parent), ui(new Ui::AtomicSwap), fundDialog( new AtomicFundDialog(this)), procList(new QList>()) { 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; @@ -33,7 +32,6 @@ void AtomicSwap::runSwap(QStringList arguments){ swap->setProcessChannelMode(QProcess::MergedChannels); swap->setReadChannel(QProcess::StandardOutput); connect(swap, &QProcess::readyRead,this, [this, swap] { - //Refactor and move this to a slot in atomicswap, move fund dialog to be part of atomic swap while(swap->canReadLine()){ QJsonParseError err; const QByteArray& rawline = swap->readLine(); @@ -51,14 +49,18 @@ void AtomicSwap::runSwap(QStringList arguments){ fundDialog->show(); } else if (line["fields"]["message"].toString().startsWith("Received Bitcoin")){ this->updateStatus(line["fields"]["new_balance"].toString().split(" ")[0] + " BTC received, starting swap"); - this->setSwap(line["span"]["swap_id"].toString()); + QVariant var; + var.setValue(HistoryEntry {QDateTime::currentDateTime(),line["span"]["swap_id"].toString()}); + QVariantList past = conf()->get(Config::pendingSwap).toList(); + past.append(var); + conf()->set(Config::pendingSwap,past); fundDialog->close(); qDebug() << "Spawn atomic swap progress dialog"; this->show(); } else if ( QString confs = line["fields"]["seen_confirmations"].toString(); !confs.isEmpty()){ qDebug() << "Updating xmrconfs " + confs; this->updateXMRConf(confs.toInt()); - } else if (QString message = line["fields"]["message"].toString(); !QString::compare(message, "Bitcoin transaction status changed")){ + } else if (QString message = line["fields"]["message"].toString(); QString::compare(message, "Bitcoin transaction status changed")==0){ qDebug() << "Updating btconfs " + line["fields"]["new_status"].toString().split(" ")[2]; QString status = line["fields"]["new_status"].toString(); bool ok; @@ -68,9 +70,31 @@ void AtomicSwap::runSwap(QStringList arguments){ } else { this->updateStatus("Found txid " + line["fields"]["txid"].toString() + " in mempool"); } - + } else if (QString message = line["fields"]["message"].toString(); message.startsWith("Swap completed")){ + QVariantList past = conf()->get(Config::pendingSwap).toList(); + past.removeLast(); + conf()->set(Config::pendingSwap, past); + this->updateStatus("Swap has successfully completed you can close this window now"); + } else if (QString message = line["fields"]["message"].toString(); QString::compare(message,"Advancing state")==0){ + this->updateStatus("State of swap has advanced to " + line["fields"]["state"].toString()); + } else if (QString refund = line["fields"]["kind"].toString(); QString::compare(refund,"refund")==0){ + QString txid = line["fields"]["txid"].toString(); + QString id = line["span"]["swap_id"].toString(); + QVariantList past = conf()->get(Config::pendingSwap).toList(); + for(int i=0;i().id,id)==0) { + past.remove(i); + break; + } + } + conf()->set(Config::pendingSwap, past); + QMessageBox::information(this,"Cancel and Refund","Swap refunded succesfully with txid " + txid); + } else if (QString message = line["fields"]["message"].toString(); QString::compare(message, "API call resulted in an error")==0){ + QString err = line["fields"]["err"].toString().split("\n")[0].split(":")[1]; + QMessageBox::warning(this, "Cancel and Refund", "Time lock hasn't expired yet so cancel failed. Try again in " + err + "blocks"); + } else if (QString message = line["fields"]["latest_version"].toString(); !message.isEmpty()){ + conf()->set(Config::swapVersion,message); } - //Insert line conditionals here } }); @@ -82,18 +106,8 @@ AtomicSwap::~AtomicSwap() { for (const auto& proc : *procList){ proc->kill(); } - 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"}); - (new QProcess)->start("kill", QStringList{"-f", Config::defaultConfigDir().absolutePath() + - "/testnet/monero/monero-wallet-rpc"}); - } - } void AtomicSwap::logLine(QString line){ - //ui->debug_log->setText(ui->debug_log->toPlainText().append(QTime::currentTime().toString() + ":" + line)); - this->update(); } void AtomicSwap::updateStatus(QString status){ @@ -117,9 +131,6 @@ void AtomicSwap::setTitle(QString title) { this->update(); } -void AtomicSwap::setSwap(QString swapId){ - id = std::move(swapId); -} void AtomicSwap::cancel(){ diff --git a/src/plugins/atomic/AtomicSwap.h b/src/plugins/atomic/AtomicSwap.h index 3cb7d56..f4e2f10 100644 --- a/src/plugins/atomic/AtomicSwap.h +++ b/src/plugins/atomic/AtomicSwap.h @@ -27,7 +27,6 @@ public: void updateBTCConf(int confs); void updateXMRConf(int confs); void setTitle(QString title); - void setSwap(QString swapId); public slots: void runSwap(QStringList swap); signals: diff --git a/src/plugins/atomic/AtomicWidget.cpp b/src/plugins/atomic/AtomicWidget.cpp index a31d5fc..416b296 100644 --- a/src/plugins/atomic/AtomicWidget.cpp +++ b/src/plugins/atomic/AtomicWidget.cpp @@ -61,19 +61,45 @@ AtomicWidget::AtomicWidget(QWidget *parent) connect(ui->btn_swap, &QPushButton::clicked, this, [this]{ auto rows = ui->offerBookTable->selectionModel()->selectedRows(); 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"; + 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(); //Add proper error checking on ui input after rest of swap is implemented QString btcChange = ui->change_address->text(); + QRegularExpression btcMain("^(bc1)[a-zA-HJ-NP-Z0-9]{39}$"); + QRegularExpression btcTest("^(tb1)[a-zA-HJ-NP-Z0-9]{39}$"); QString xmrReceive = ui->xmr_address->text(); + if(xmrReceive.isEmpty()) { + QMessageBox::warning(this, "Warning", "XMR receive address is required to start swap"); + return; + } + QRegularExpression xmrMain("^[48][0-9AB][1-9A-HJ-NP-Za-km-z]{93}"); + QRegularExpression xmrStage("^[57][0-9AB][1-9A-HJ-NP-Za-km-z]{93}"); + if (constants::networkType==NetworkType::STAGENET){ + if(!btcChange.isEmpty() && !btcTest.match(btcChange).hasMatch()){ + QMessageBox::warning(this, "Warning","BTC change address is wrong, not a bech32 segwit address, or on wrong network"); + return; + } + if(!xmrStage.match(xmrReceive).hasMatch()){ + QMessageBox::warning(this, "Warning","XMR receive address is improperly formated or on wrong network"); + return; + } + } else { + if(!btcChange.isEmpty() && !btcMain.match(btcChange).hasMatch()){ + QMessageBox::warning(this, "Warning","BTC change address is wrong, not a bech32 segwit address,or on wrong network"); + return; + } + if(!xmrMain.match(xmrReceive).hasMatch()){ + QMessageBox::warning(this, "Warning","XMR receive address is improperly formated or on wrong network"); + return; + } + } + sleep(1); runSwap(seller,btcChange, xmrReceive); - //} + } }); connect(ui->btn_addRendezvous, &QPushButton::clicked, this, [this]{ @@ -89,11 +115,6 @@ AtomicWidget::AtomicWidget(QWidget *parent) } }); - - //Remove after testing - 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(); @@ -124,18 +145,10 @@ void AtomicWidget::runSwap(const QString& seller, const QString& btcChange, cons arguments << "-j"; arguments << "buy-xmr"; arguments << "--change-address"; - arguments << "tb1qzndh6u8qgl2ee4k4gl9erg947g67hyx03vvgen"; - //arguments << btcChange; + arguments << btcChange; arguments << "--receive-address"; - arguments << "78YnzFTp3UUMgtKuAJCP2STcbxRZPDPveJ5YGgfg5doiPahS9suWF1r3JhKqjM1McYBJvu8nhkXExGfXVkU6n5S6AXrg4KP"; - //arguments << xmrReceive; - arguments << "--seller"; - arguments << "/ip4/127.0.0.1/tcp/9939/p2p/12D3KooW9yDFYojXnZRdqS9UXcfP2amgwoYdSjujwWdRw4LTSdWw"; - // Remove after testing - arguments << "--electrum-rpc"; - arguments << "tcp://127.0.0.1:50001"; - arguments << "--bitcoin-target-block"; - arguments << "1"; + arguments << xmrReceive; + auto nodes = conf()->get(Config::nodes).toJsonObject(); if (nodes.isEmpty()) { auto jsonData = conf()->get(Config::nodes).toByteArray(); @@ -144,12 +157,10 @@ void AtomicWidget::runSwap(const QString& seller, const QString& btcChange, cons nodes = doc.object(); } } - qDebug() << nodes.value("0").toObject()["ws"].toArray()[0]; arguments << "--monero-daemon-address"; - //arguments << "node.monerodevs.org:38089"; arguments << nodes.value("0").toObject()["ws"].toArray()[0].toString(); - // Uncomment after testing - //arguments << seller; + arguments << "--seller"; + arguments << seller; if(conf()->get(Config::proxy).toInt() != Config::Proxy::None) { arguments << "--tor-socks5-port"; arguments << conf()->get(Config::socks5Port).toString(); @@ -176,23 +187,16 @@ void AtomicWidget::list(const QString& rendezvous) { auto *swap = new QProcess(); procList->append(QSharedPointer(swap)); swap->setReadChannel(QProcess::StandardError); - //swap->start(conf()->get(Config::swapPath).toString(), arguments); connect(swap, &QProcess::finished, this, [this, swap]{ QJsonDocument parsedLine; QJsonParseError parseError; QList> list; - qDebug() << "Subprocess has finished"; auto output = QString::fromLocal8Bit(swap->readAllStandardError()); - qDebug() << "Crashes before splitting"; auto lines = output.split(QRegularExpression("[\r\n]"),Qt::SkipEmptyParts); - qDebug() << lines.size(); - qDebug() << "parsing Output"; for(const auto& line : lines){ - qDebug() << line; if(line.contains("status")){ - qDebug() << "status contained"; parsedLine = QJsonDocument::fromJson(line.toLocal8Bit(), &parseError ); if (parsedLine["fields"]["status"].toString().contains("Online")){ bool skip = false; @@ -205,17 +209,14 @@ void AtomicWidget::list(const QString& rendezvous) { ui->meta_label->setText("Updated offer book"); offerList->append(QSharedPointer(entry)); } - qDebug() << entry; } } } - qDebug() << "exits fine"; swap->close(); o_model->updateOffers(*offerList); return list; }); swap->start(conf()->get(Config::swapPath).toString(), arguments); - //swap->waitForFinished(120000); @@ -234,13 +235,6 @@ void AtomicWidget::clean() { for (const auto& proc : *procList){ proc->kill(); } - 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"}); - (new QProcess)->start("kill", QStringList{"-f", Config::defaultConfigDir().absolutePath() + - "/testnet/monero/monero-wallet-rpc"}); - } } diff --git a/src/plugins/atomic/OfferModel.cpp b/src/plugins/atomic/OfferModel.cpp index e4092c8..013a080 100644 --- a/src/plugins/atomic/OfferModel.cpp +++ b/src/plugins/atomic/OfferModel.cpp @@ -21,7 +21,6 @@ void OfferModel::clear() { void OfferModel::updateOffers(const QList> &posts) { beginResetModel(); - qDebug() << "updating Offers"; m_offers.clear(); for (const auto& post : posts) { m_offers.push_back(post); diff --git a/src/utils/config.cpp b/src/utils/config.cpp index 47e7b99..7eef841 100644 --- a/src/utils/config.cpp +++ b/src/utils/config.cpp @@ -150,6 +150,7 @@ static const QHash configStrings = { {Config::swapPath, {QS("swapPath"), ""}}, {Config::operatingSystem, {QS("operatingSystem"), OS}}, {Config::pendingSwap, {QS("pendingSwap"), QVariantList{}}}, + {Config::swapVersion, {QS("swapVersion"), "0.13.4"}}, }; diff --git a/src/utils/config.h b/src/utils/config.h index 0aab639..d7c91ee 100644 --- a/src/utils/config.h +++ b/src/utils/config.h @@ -156,6 +156,7 @@ public: swapPath, operatingSystem, pendingSwap, + swapVersion, }; enum PrivacyLevel {