Offer Book updating half done. Need to fix refreshing gui to reflect new order.

This commit is contained in:
twiddle 2024-05-26 20:19:29 -04:00
parent e6aac4eb25
commit 0018b5a869
11 changed files with 493 additions and 193 deletions

View file

@ -26,7 +26,7 @@ option(DONATE_BEG "Prompt donation window every once in a while" OFF)
option(WITH_SCANNER "Enable webcam QR scanner" ON)
option(STACK_TRACE "Dump stack trace on crash (Linux only)" OFF)
# Plugins
#
option(WITH_PLUGIN_HOME "Include Home tab plugin" ON)
option(WITH_PLUGIN_TICKERS "Include Tickers Home plugin" ON)
option(WITH_PLUGIN_CROWDFUNDING "Include Crowdfunding Home plugin" ON)
@ -117,7 +117,7 @@ if(WITH_SCANNER)
endif()
# libzip
if(CHECK_UPDATES)
if(CHECK_UPDATES OR WITH_PLUGIN_ATOMIC)
set(ZLIB_USE_STATIC_LIBS "ON")
message("libzip inclueded")
find_package(ZLIB REQUIRED)

View file

@ -301,11 +301,11 @@ if (WITH_SCANNER)
)
endif()
if (WITH_PLUGIN_ATOMIC)
FIND_PACKAGE(LibArchive REQUIRED)
INCLUDE_DIRECTORIES(${LibArchive_INCLUDE_DIR})
target_link_libraries(feather PRIVATE ${LibArchive_LIBRARIES})
endif()
if(STATIC AND APPLE)
target_link_libraries(feather PRIVATE Qt6::QDarwinCameraPermissionPlugin)

View file

@ -6,6 +6,7 @@
#include "plugins/Plugin.h"
#include "AtomicWidget.h"
#include "Offer.h"
class AtomicPlugin : public Plugin {
Q_OBJECT
@ -26,7 +27,6 @@ public:
QDialog* configDialog(QWidget *parent) override;
void initialize(Wallet *wallet, QObject *parent) override;
static AtomicPlugin* create() { return new AtomicPlugin(); }
public slots:

View file

@ -5,8 +5,10 @@
#include "ui_AtomicWidget.h"
#include <QList>
#include <QProcess>
#include "AtomicConfigDialog.h"
#include "OfferModel.h"
#include "utils/AppData.h"
#include "utils/ColorScheme.h"
#include "utils/config.h"
@ -15,6 +17,8 @@
AtomicWidget::AtomicWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::AtomicWidget)
, o_model(new OfferModel(this))
, offerList(new QList<QSharedPointer<OfferEntry>>())
{
ui->setupUi(this);
@ -30,7 +34,15 @@ AtomicWidget::AtomicWidget(QWidget *parent)
QValidator *validator = new QRegularExpressionValidator(rx, this);
ui->lineFrom->setValidator(validator);
ui->lineTo->setValidator(validator);
ui->offerBookTable->setModel(o_model);
ui->offerBookTable->setSortingEnabled(true);
ui->offerBookTable->sortByColumn(0, Qt::SortOrder::AscendingOrder);
ui->offerBookTable->verticalHeader()->setVisible(false);
ui->offerBookTable->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->offerBookTable->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
ui->offerBookTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch);
//ui->offerBookTable->
connect(&appData()->prices, &Prices::fiatPricesUpdated, this, &AtomicWidget::onPricesReceived);
connect(&appData()->prices, &Prices::cryptoPricesUpdated, this, &AtomicWidget::onPricesReceived);
@ -42,6 +54,40 @@ AtomicWidget::AtomicWidget(QWidget *parent)
connect(ui->btn_configure, &QPushButton::clicked, this, &AtomicWidget::showAtomicConfigureDialog);
connect(ui->btn_refreshOffer, &QPushButton::clicked, this, [this]{
offerList->clear();
auto m_instance = Config::instance();
QStringList pointList = m_instance->get(Config::rendezVous).toStringList();
for(QString point :pointList)
AtomicWidget::list(point);
/*
QList<QFuture<void>> tempList;
for (int i=0; i<pointList.size();i++){
tempList.append(QtConcurrent::run([this, pointList, i]{
return AtomicWidget::list(pointList[i]);
}));
}
sleep(130);
for (int i=0; i<pointList.size();i++){
qDebug() << "Starting to read offers";
auto offers = tempList[i].result();
for (auto offer: offers){
offerList->append(offer);
}
}
qDebug() << "done";
/*for(auto offer: AtomicWidget::list(Config::instance()->get(Config::rendezVous).toStringList()[0])){
offerList->append(offer);
}
o_model->updateOffers(*offerList);
*/
});
QTimer::singleShot(1, [this]{
this->skinChanged();
});
@ -167,4 +213,57 @@ void AtomicWidget::updateStatus() {
}
}
void AtomicWidget::list(QString rendezvous) {
QStringList arguments;
QList<QSharedPointer<OfferEntry>> list;
auto m_instance = Config::instance();
arguments << "--data-base-dir";
arguments << Config::defaultConfigDir().absolutePath();
// Remove after testing
//arguments << "--testnet";
arguments << "-j";
arguments << "list-sellers";
arguments << "--tor-socks5-port";
arguments << m_instance->get(Config::socks5Port).toString();
arguments << "--rendezvous-point";
arguments << rendezvous;
auto *swap = new QProcess();
swap->setReadChannel(QProcess::StandardOutput);
//swap->start(m_instance->get(Config::swapPath).toString(), arguments);
connect(swap, &QProcess::finished, this, [this, swap]{
QJsonDocument parsedLine;
QJsonParseError parseError;
QList<QSharedPointer<OfferEntry>> 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(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")){
OfferEntry entry = {parsedLine["fields"]["price"].toDouble(),parsedLine["fields"]["min_quantity"].toDouble(),parsedLine["fields"]["max_quantity"].toDouble(), parsedLine["fields"]["address"].toString()};
offerList->append(QSharedPointer<OfferEntry>(&entry));
qDebug() << &entry;
}
}
qDebug() << "next line";
}
qDebug() << "exits fine";
swap->close();
return list;
});
swap->start("/home/dev/.config/feather/swap", arguments);
//swap->waitForFinished(120000);
}
AtomicWidget::~AtomicWidget() = default;

View file

@ -7,6 +7,10 @@
#include <QWidget>
#include <QComboBox>
#include <QTimer>
#include <QProcess>
#include <QMutex>
#include "OfferModel.h"
namespace Ui {
class AtomicWidget;
@ -19,6 +23,7 @@ Q_OBJECT
public:
explicit AtomicWidget(QWidget *parent = nullptr);
~AtomicWidget() override;
void list(QString rendezvous);
public slots:
void skinChanged();
@ -36,6 +41,8 @@ private:
QScopedPointer<Ui::AtomicWidget> ui;
bool m_comboBoxInit = false;
QTimer m_statusTimer;
OfferModel *o_model;
QList<QSharedPointer<OfferEntry>> *offerList;
};
#endif // FEATHER_ATOMICWIDGET_H

View file

@ -1,187 +1,191 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AtomicWidget</class>
<widget class="QWidget" name="AtomicWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>366</height>
</rect>
<class>AtomicWidget</class>
<widget class="QWidget" name="AtomicWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>366</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QFrame" name="frame_warning">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="icon_warning">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>MainWindow</string>
<property name="text">
<string>icon</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QFrame" name="frame_warning">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="icon_warning">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>icon</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Maximum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_warning">
<property name="text">
<string>Warning text</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_2">
<property name="horizontalSpacing">
<number>18</number>
</property>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="lineFrom">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frame">
<bool>true</bool>
</property>
<property name="placeholderText">
<string>From...</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboAtomicFrom"/>
</item>
</layout>
</item>
<item row="0" column="1">
<widget class="DoublePixmapLabel" name="imageExchange">
<property name="text">
<string>exchange image</string>
</property>
</widget>
</item>
<item row="0" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="lineTo">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
<property name="placeholderText">
<string>To...</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboAtomicTo"/>
</item>
</layout>
</item>
</layout>
</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_configure">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
</layout>
</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>
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Maximum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_warning">
<property name="text">
<string>Warning text</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>DoublePixmapLabel</class>
<extends>QLabel</extends>
<header>components.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
<slots>
<slot>fromChanged(QString)</slot>
<slot>toChanged(QString)</slot>
<slot>toComboChanged(QString)</slot>
</slots>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_2">
<property name="horizontalSpacing">
<number>18</number>
</property>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="lineFrom">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frame">
<bool>true</bool>
</property>
<property name="placeholderText">
<string>From...</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboAtomicFrom"/>
</item>
</layout>
</item>
<item row="0" column="1">
<widget class="DoublePixmapLabel" name="imageExchange">
<property name="text">
<string>exchange image</string>
</property>
</widget>
</item>
<item row="0" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="lineTo">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
<property name="placeholderText">
<string>To...</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboAtomicTo"/>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QTableView" name="offerBookTable"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="btn_refreshOffer">
<property name="text">
<string>Refresh Offer Book</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_addRendezvous">
<property name="text">
<string>Add Rendezvous Point</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>
<item>
<widget class="QPushButton" name="btn_configure">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>DoublePixmapLabel</class>
<extends>QLabel</extends>
<header>components.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
<slots>
<slot>fromChanged(QString)</slot>
<slot>toChanged(QString)</slot>
<slot>toComboChanged(QString)</slot>
</slots>
</ui>

View file

@ -0,0 +1,20 @@
//
// Created by dev on 5/23/24.
//
#ifndef FEATHER_OFFER_H
#define FEATHER_OFFER_H
#include <QString>
struct OfferEntry {
OfferEntry(double price, double min, double max, QString address )
: price(price), min(min), max(max), address(std::move(address)) {};
double price;
double min;
double max;
QString address;
};
#endif //FEATHER_OFFER_H

View file

@ -0,0 +1,124 @@
//
// Created by dev on 5/23/24.
//
#include "OfferModel.h"
OfferModel::OfferModel(QObject *parent)
: QAbstractTableModel(parent)
{
}
void OfferModel::clear() {
beginResetModel();
m_offers.clear();
endResetModel();
}
void OfferModel::updateOffers(const QList<QSharedPointer<OfferEntry>> &posts) {
beginResetModel();
qDebug() << "updating Offers";
m_offers.clear();
for (const auto& post : posts) {
m_offers.push_back(post);
}
endResetModel();
}
int OfferModel::rowCount(const QModelIndex &parent) const{
if (parent.isValid()) {
return 0;
}
return m_offers.count();
}
int OfferModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
return 0;
}
return OfferModel::ModelColumn::COUNT;
}
QVariant OfferModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0 || index.row() >= m_offers.count())
return {};
QSharedPointer<OfferEntry> post = m_offers.at(index.row());
if(role == Qt::DisplayRole || role == Qt::UserRole) {
switch(index.column()) {
case Price: {
if (role == Qt::UserRole) {
return post->price;
}
return QString::number(post->price);
}
case Address:
return post->address;
case Min_Amount:{
if (role == Qt::UserRole) {
return post->min;
}
return QString::number(post->min);
}
case Max_Amount: {
if (role == Qt::UserRole) {
return post->max;
}
return QString::number(post->max);
}
default:
return {};
}
}
else if (role == Qt::TextAlignmentRole) {
switch(index.column()) {
case Price:
case Min_Amount:
case Max_Amount:
return Qt::AlignRight;
default:
return {};
}
}
return {};
}
QVariant OfferModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole) {
return QVariant();
}
if (orientation == Qt::Horizontal)
{
switch(section) {
case Price:
return QString("Price (BTC) ");
case Min_Amount:
return QString("Min Amount (BTC)");
case Max_Amount:
return QString("Max Amount (BTC)");
case Address:
return QString("P2P Vendor Address");
default:
return QVariant();
}
}
return QVariant();
}
QSharedPointer<OfferEntry> OfferModel::post(int row) {
if (row < 0 || row >= m_offers.size()) {
qCritical("%s: no reddit post for index %d", __FUNCTION__, row);
return QSharedPointer<OfferEntry>();
}
return m_offers.at(row);
}

View file

@ -0,0 +1,42 @@
//
// Created by dev on 5/23/24.
//
#ifndef FEATHER_OFFERMODEL_H
#define FEATHER_OFFERMODEL_H
#include <QAbstractTableModel>
#include "Offer.h"
class OfferModel : public QAbstractTableModel
{
Q_OBJECT
public:
enum ModelColumn
{
Price = 0,
Min_Amount,
Max_Amount,
Address,
COUNT
};
explicit OfferModel(QObject *parent);
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
void clear();
void updateOffers(const QList<QSharedPointer<OfferEntry>>& posts);
QSharedPointer<OfferEntry> post(int row);
private:
QList<QSharedPointer<OfferEntry>> m_offers;
};
#endif //FEATHER_OFFERMODEL_H

View file

@ -4,8 +4,13 @@ Built in xmr-btc atomic swap
## Installation
## Hacking
sudo apt install
Install KArchive
sudo apt install libarchive-dev
Functions used to control swap (AtomicPlugin.cpp)
withdraw(btcaddress) - withdraw btc
list(rendezvous point) - list sellers at rendezvous
## Usage
Navigate to the Atomic tab after opening your wallet. Click the configure button.

View file

@ -142,11 +142,10 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
{Config::tickersShowFiatBalance, {QS("tickersShowFiatBalance"), true}},
// Atomic
{Config::rendezVous, {QS("rendezVous"), QStringList{"/dns4/xmr-btc-asb.coblox.tech/tcp/9939/p2p/12D3KooWCdMKjesXMJz1SiZ7HgotrxuqhQJbP5sgBm2BwP1cqThi",
"/dnsaddr/atomic.money/p2p/12D3KooWNfiwKyDpAbW7XSgw5xmoMJzMXsJxR91FbUJiuWU1vBGb",
"/dnsaddr/xmr.darkness.su/p2p/12D3KooWLWpQtmuPoQvMJxs6KRGMrc39ohkNbRNaXWJhW3DfQD6e",
"/dnsaddr/swapanarchy.cfd/p2p/12D3KooWMgGjeW7ErQxCQzaeHiXxJn42wegCPFepixEXfBJT1PNS",
"/onion3/spqfqxirmlrhq7gbiwn4jn35c77gu2kof26i6psoc6bbyduol3zty6qd:9841/p2p/12D3KooWM9ipr33nEtxyCBF7fdbHsMrRzHaSf1bEVYzV8XSBSMet"}}},
{Config::rendezVous, {QS("rendezVous"), QStringList{"/dns4/discover.unstoppableswap.net/tcp/8888/p2p/12D3KooWA6cnqJpVnreBVnoro8midDL9Lpzmg8oJPoAGi7YYaamE",
"/dns4/eratosthen.es/tcp/7798/p2p/12D3KooWAh7EXXa2ZyegzLGdjvj1W4G3EXrTGrf6trraoT1MEobs",
"/dnsaddr/rendezvous.coblox.tech/p2p/12D3KooWQUt9DkNZxEn2R5ymJzWj15MpG6mTW84kyd8vDaRZi46o",
"/dns4/swap.sethforprivacy.com/tcp/8888/p2p/12D3KooWCULyZKuV9YEkb6BX8FuwajdvktSzmMg4U5ZX2uYZjHeu"}}},
{Config::swapPath, {QS("swapPath"), ""}},
{Config::operatingSystem, {QS("operatingSystem"), OS}},
};