Skip to content

Commit

Permalink
Fusion GUI Wallet
Browse files Browse the repository at this point in the history
+ RPC consolidation fix;
+ added fusion optimize (it works only for tx with certain amounts);
+ disabled sending if not synced;
+ fixed shutdown (fast startup without index rebuild);
+ fixed zero fee issue in rare cases;
+ fixed log file creation;
+ do not create new wallet silently if previous wallet is absent;
+ added icon for cancelled tx;
  • Loading branch information
dynexcoin committed Dec 5, 2023
1 parent 202efa0 commit 0b174d1
Show file tree
Hide file tree
Showing 15 changed files with 189 additions and 56 deletions.
7 changes: 0 additions & 7 deletions src/DynexCNCore/Core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1141,16 +1141,9 @@ bool core::getTransactionsByAddress(const std::string address, std::vector<Trans
std::vector<Crypto::Hash> blockchainTransactionHashes;
m_blockchain.getTransactionIdsByAddress(address, blockchainTransactionHashes); // Blockchain.cpp

std::vector<Crypto::Hash> poolTransactionHashes;
m_mempool.getTransactionIdsByAddress(address, poolTransactionHashes); //TransactionPool.cpp

std::list<Transaction> txs;
std::list<Crypto::Hash> missed_txs;

if (!poolTransactionHashes.empty()) {
blockchainTransactionHashes.insert(blockchainTransactionHashes.end(), poolTransactionHashes.begin(), poolTransactionHashes.end());
}

if (blockchainTransactionHashes.empty()) {
return false;
}
Expand Down
10 changes: 8 additions & 2 deletions src/WalletGui/DynexCNWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ class InprocessNode : DynexCN::INodeObserver, public Node {
m_coreConfig(coreConfig),
m_logManager(logManager),
m_netNodeConfig(netNodeConfig),
m_core(currency, NULL, logManager, true),
m_core(currency, NULL, logManager, false),
m_protocolHandler(currency, m_dispatcher, m_core, nullptr, logManager),
m_nodeServer(m_dispatcher, m_protocolHandler, logManager),
m_node(m_core, m_protocolHandler) {
Expand Down Expand Up @@ -233,8 +233,14 @@ class InprocessNode : DynexCN::INodeObserver, public Node {
});

m_nodeServer.run();

//deinitialize components
LoggerAdapter::instance().log("Deinitializing core...");
m_core.deinit();
m_nodeServer.deinit();
m_node.shutdown();
m_core.set_cryptonote_protocol(NULL);
m_protocolHandler.set_p2p_endpoint(NULL);
m_node.shutdown();
}

void deinit() override {
Expand Down
3 changes: 1 addition & 2 deletions src/WalletGui/Settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,7 @@ QDir Settings::getDataDir() const {
}

QString Settings::getWalletFile() const {
return m_settings.contains("walletFile") ? m_settings.value("walletFile").toString() :
getDataDir().absoluteFilePath(QCoreApplication::applicationName() + ".wallet");
return m_settings.contains("walletFile") ? m_settings.value("walletFile").toString() : QString();
}

QStringList Settings::getRecentWalletsList() const {
Expand Down
80 changes: 63 additions & 17 deletions src/WalletGui/WalletAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,20 +131,21 @@ quint64 WalletAdapter::getPendingBalance() const {
void WalletAdapter::open(const QString& _password) {
Q_ASSERT(m_wallet == nullptr);
Settings::instance().setEncrypted(!_password.isEmpty());
QString msg = "Opening wallet " + Settings::instance().getWalletFile();
QString wallet = Settings::instance().getWalletFile();
QString msg = "Opening wallet " + wallet;
Q_EMIT walletStateChangedSignal(msg);

m_wallet = NodeAdapter::instance().createWallet();
m_wallet->addObserver(this);

if (QFile::exists(Settings::instance().getWalletFile())) {
if (QFile::exists(wallet)) {
if (Settings::instance().getWalletFile().endsWith(".keys")) {
if(!importLegacyWallet(_password)) {
return;
}
}

if (openFile(Settings::instance().getWalletFile(), true)) {
if (openFile(wallet, true)) {
try {
m_wallet->initAndLoad(m_file, _password.toStdString());
} catch (std::system_error&) {
Expand All @@ -154,19 +155,7 @@ void WalletAdapter::open(const QString& _password) {
}
}
} else {
Settings::instance().setEncrypted(false);
try {
#ifndef GENERATE_DETERMINISTIC
m_wallet->initAndGenerate("");
#else
m_wallet->initAndGenerateDeterministic("");
VerifyMnemonicSeedDialog dlg(nullptr);
dlg.exec();
#endif
} catch (std::system_error&) {
delete m_wallet;
m_wallet = nullptr;
}
QMessageBox::critical(nullptr, tr("Wallet file not found"), wallet, QMessageBox::Ok);
}
}

Expand Down Expand Up @@ -373,7 +362,10 @@ bool WalletAdapter::getAccountKeys(DynexCN::AccountKeys& _keys) {

void WalletAdapter::sendTransaction(const QVector<DynexCN::WalletLegacyTransfer>& _transfers, quint64 _fee, const QString& _paymentId, quint64 _mixin) {
Q_CHECK_PTR(m_wallet);

if (!m_isSynchronized) {
QMessageBox::warning(nullptr, tr("Warning"), tr("Wallet is not synchronized!"), QMessageBox::Ok);
return;
}
try {
lock();
std::vector<DynexCN::WalletLegacyTransfer> vec(_transfers.begin(), _transfers.end());
Expand Down Expand Up @@ -620,4 +612,58 @@ DynexCN::AccountKeys WalletAdapter::getKeysFromMnemonicSeed(QString& _seed) cons
return keys;
}

quint64 WalletAdapter::estimateFusion(quint64 _threshold) {
Q_CHECK_PTR(m_wallet);
try {
return m_wallet->estimateFusion(_threshold);
} catch (std::system_error&) {
}
return 0;
}

std::list<DynexCN::TransactionOutputInformation> WalletAdapter::getFusionTransfersToSend(quint64 _threshold, size_t _min_input_count, size_t _max_input_count) {
Q_CHECK_PTR(m_wallet);
try {
return m_wallet->selectFusionTransfersToSend(_threshold, _min_input_count, _max_input_count);
} catch (std::system_error&) {
}
return {};
}

void WalletAdapter::sendFusionTransaction(const std::list<DynexCN::TransactionOutputInformation>& _fusion_inputs, quint64 _mixin) {
Q_CHECK_PTR(m_wallet);
try {
lock();
Q_EMIT walletStateChangedSignal(tr("Optimizing wallet"));
m_wallet->sendFusionTransaction(_fusion_inputs, 0, "", _mixin, 0);
} catch (std::system_error&) {
unlock();
}
}

bool WalletAdapter::isFusionTransaction(const DynexCN::WalletLegacyTransaction& walletTx) const {
Q_CHECK_PTR(m_wallet);
return m_wallet->isFusionTransaction(walletTx);
}

void WalletAdapter::optimize(const quint64 mixin) {
if (!isOpen() || !m_isSynchronized) return;
uint64_t fusionThreshold = getActualBalance();
//size_t fusionReadyCount = estimateFusion(fusionThreshold);
const size_t MAX_FUSION_OUTPUT_COUNT = 4;
size_t estimatedFusionInputsCount = CurrencyAdapter::instance().getCurrency().getApproximateMaximumInputCount(CurrencyAdapter::instance().getCurrency().fusionTxMaxSize(), MAX_FUSION_OUTPUT_COUNT, mixin);
if (estimatedFusionInputsCount < CurrencyAdapter::instance().getCurrency().fusionTxMinInputCount()) {
QMessageBox::warning(nullptr, tr("Optimize"), tr("Anonimity level is too high!"), QMessageBox::Ok);
return;
}
std::list<DynexCN::TransactionOutputInformation> fusionInputs = getFusionTransfersToSend(fusionThreshold, CurrencyAdapter::instance().getCurrency().fusionTxMinInputCount(), estimatedFusionInputsCount);
if (fusionInputs.size() < CurrencyAdapter::instance().getCurrency().fusionTxMinInputCount()) {
QMessageBox::warning(nullptr, tr("Optimize"), tr("Nothing to do!"), QMessageBox::Ok);
return;
}
if (QMessageBox::warning(nullptr, tr("Optimize"), tr("Send fusion transaction?"), QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Ok) {
sendFusionTransaction(fusionInputs, mixin);
}
}

}
6 changes: 6 additions & 0 deletions src/WalletGui/WalletAdapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ class WalletAdapter : public QObject, public DynexCN::IWalletLegacyObserver {
QString getMnemonicSeed(QString _language) const;
DynexCN::AccountKeys getKeysFromMnemonicSeed(QString& _seed) const;

quint64 estimateFusion(quint64 _threshold);
std::list<DynexCN::TransactionOutputInformation> getFusionTransfersToSend(quint64 _threshold, size_t _min_input_count, size_t _max_input_count);
void sendFusionTransaction(const std::list<DynexCN::TransactionOutputInformation>& _fusion_inputs, quint64 _mixin);
bool isFusionTransaction(const DynexCN::WalletLegacyTransaction& walletTx) const;
void optimize(const quint64 mixin);

private:
std::fstream m_file;
DynexCN::IWalletLegacy* m_wallet;
Expand Down
2 changes: 1 addition & 1 deletion src/WalletGui/gui/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ void MainWindow::openRecent() {
WalletAdapter::instance().setWalletFile(filePath);
WalletAdapter::instance().open("");
} else {
QMessageBox::warning(this, tr("Recent wallet file not found"), tr("The recent wallet file is missing. Probably it was removed."), QMessageBox::Ok);
QMessageBox::warning(this, tr("Wallet file not found"), tr("The recent wallet file is missing. Probably it was removed."), QMessageBox::Ok);
}
}
}
Expand Down
33 changes: 28 additions & 5 deletions src/WalletGui/gui/SendFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,23 +55,41 @@ namespace WalletGui {
SendFrame::SendFrame(QWidget* _parent) : QFrame(_parent), m_ui(new Ui::SendFrame) {
m_ui->setupUi(this);
clearAllClicked();
m_ui->m_sendButton->setEnabled(false);
m_ui->m_optimizeButton->setEnabled(false);

connect(&WalletAdapter::instance(), &WalletAdapter::walletSendTransactionCompletedSignal, this, &SendFrame::sendTransactionCompleted,
Qt::QueuedConnection);
connect(&WalletAdapter::instance(), &WalletAdapter::walletActualBalanceUpdatedSignal, this, &SendFrame::walletActualBalanceUpdated,
Qt::QueuedConnection);

connect(&WalletAdapter::instance(), &WalletAdapter::walletSynchronizationProgressUpdatedSignal,
this, &SendFrame::walletSynchronizationInProgress, Qt::QueuedConnection);
connect(&WalletAdapter::instance(), &WalletAdapter::walletSynchronizationCompletedSignal, this, &SendFrame::walletSynchronized
, Qt::QueuedConnection);

m_ui->m_tickerLabel->setText(CurrencyAdapter::instance().getCurrencyTicker().toUpper());
m_ui->m_feeSpin->setSuffix(" " + CurrencyAdapter::instance().getCurrencyTicker().toUpper());
double fee = CurrencyAdapter::instance().formatAmount(NodeAdapter::instance().getMinimalFee()).toDouble();
m_ui->m_feeSpin->setValue(fee);
m_ui->m_feeSpin->setMinimum(fee);
m_ui->m_feeSpin->setMaximum(fee*1000);
//double fee = CurrencyAdapter::instance().formatAmount(NodeAdapter::instance().getMinimalFee()).toDouble();
//m_ui->m_feeSpin->setValue(fee);
//m_ui->m_feeSpin->setMinimum(fee);
//m_ui->m_feeSpin->setMaximum(fee*1000);
}

SendFrame::~SendFrame() {
}

void SendFrame::walletSynchronizationInProgress() {
m_ui->m_sendButton->setEnabled(false);
m_ui->m_optimizeButton->setEnabled(false);
}

void SendFrame::walletSynchronized(int _error, const QString& _error_text) {
bool enabled = (WalletAdapter::instance().getActualBalance() > 0 && _error == 0);
m_ui->m_sendButton->setEnabled(enabled);
m_ui->m_optimizeButton->setEnabled(enabled);
}

void SendFrame::addRecipientClicked() {
TransferFrame* newTransfer = new TransferFrame(m_ui->m_transfersScrollarea);
m_ui->m_send_frame_layout->insertWidget(m_transfers.size(), newTransfer);
Expand Down Expand Up @@ -103,10 +121,15 @@ void SendFrame::clearAllClicked() {
m_ui->m_feeSpin->setValue(CurrencyAdapter::instance().formatAmount(NodeAdapter::instance().getMinimalFee()).toDouble());
}

void SendFrame::optimizeClicked() {
WalletAdapter::instance().optimize(0);
}

void SendFrame::sendClicked() {

double minfee = CurrencyAdapter::instance().formatAmount(NodeAdapter::instance().getMinimalFee()).toDouble();
if (m_ui->m_feeSpin->value() > minfee*1000) {

if (m_ui->m_feeSpin->value() > 1.0) { // minfee*1000
QCoreApplication::postEvent(&MainWindow::instance(), new ShowMessageEvent(tr("Fee is too high"), QtCriticalMsg));
return;
}
Expand Down
3 changes: 3 additions & 0 deletions src/WalletGui/gui/SendFrame.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,12 @@ class SendFrame : public QFrame {
void sendTransactionCompleted(DynexCN::TransactionId _id, bool _error, const QString& _error_text);
void walletActualBalanceUpdated(quint64 _balance);
void insertPaymentId(QString _paymentId);
void walletSynchronizationInProgress();
void walletSynchronized(int _error, const QString& _error_text);

Q_SLOT void addRecipientClicked();
Q_SLOT void clearAllClicked();
Q_SLOT void optimizeClicked();
//Q_SLOT void mixinValueChanged(int _value);
Q_SLOT void sendClicked();
};
Expand Down
41 changes: 31 additions & 10 deletions src/WalletGui/gui/TransactionsModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include <QFont>
#include <QMetaEnum>
#include <QPixmap>
#include <QPixmapCache>

#include "CurrencyAdapter.h"
#include "NodeAdapter.h"
Expand All @@ -48,7 +49,9 @@

namespace WalletGui {

enum class TransactionType : quint8 {MINED, INPUT, OUTPUT, INOUT};
enum class TransactionType : quint8 {MINED, INPUT, OUTPUT, INOUT, FUSION};

enum class TransactionState : quint8 {ACTIVE, DELETED, SENDING, CANCELLED, FAILED};

namespace {

Expand All @@ -61,6 +64,7 @@ QPixmap getTransactionIcon(TransactionType _transactionType) {
case TransactionType::OUTPUT:
return QPixmap(":icons/tx-output");
case TransactionType::INOUT:
case TransactionType::FUSION:
return QPixmap(":icons/tx-inout");
default:
break;
Expand Down Expand Up @@ -217,7 +221,7 @@ QVariant TransactionsModel::getDisplayRole(const QModelIndex& _index) const {
case COLUMN_ADDRESS: {
TransactionType transactionType = static_cast<TransactionType>(_index.data(ROLE_TYPE).value<quint8>());
if (transactionType == TransactionType::INPUT || transactionType == TransactionType::MINED ||
transactionType == TransactionType::INOUT) {
transactionType == TransactionType::INOUT || transactionType == TransactionType::FUSION) {
return QString(tr("me (%1)").arg(WalletAdapter::instance().getAddress()));
}

Expand Down Expand Up @@ -261,21 +265,32 @@ QVariant TransactionsModel::getDisplayRole(const QModelIndex& _index) const {
QVariant TransactionsModel::getDecorationRole(const QModelIndex& _index) const {
if(_index.column() == COLUMN_STATE) {
quint64 numberOfConfirmations = _index.data(ROLE_NUMBER_OF_CONFIRMATIONS).value<quint64>();
if(numberOfConfirmations == 0) {
return QPixmap(":icons/unconfirmed");
TransactionState transactionState = static_cast<TransactionState>(_index.data(ROLE_STATE).value<quint8>());
QString file;
if (transactionState != TransactionState::ACTIVE && transactionState != TransactionState::SENDING) {
file = QString(":icons/cancelled");
} else if (numberOfConfirmations == 0) {
file = QString(":icons/unconfirmed");
} else if(numberOfConfirmations < 2) {
return QPixmap(":icons/clock1");
file = QString(":icons/clock1");
} else if(numberOfConfirmations < 4) {
return QPixmap(":icons/clock2");
file = QString(":icons/clock2");
} else if(numberOfConfirmations < 6) {
return QPixmap(":icons/clock3");
file = QString(":icons/clock3");
} else if(numberOfConfirmations < 8) {
return QPixmap(":icons/clock4");
file = QString(":icons/clock4");
} else if(numberOfConfirmations < 10) {
return QPixmap(":icons/clock5");
file = QString(":icons/clock5");
} else {
return QPixmap(":icons/transaction");
file = QString(":icons/transaction");
}
QPixmap pixmap;
if (!QPixmapCache::find(file, &pixmap)) {
pixmap.load(file);
QPixmapCache::insert(file, pixmap);
}
return pixmap;

} else if (_index.column() == COLUMN_ADDRESS) {
return _index.data(ROLE_ICON).value<QPixmap>().scaled(20, 20, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
Expand All @@ -290,13 +305,19 @@ QVariant TransactionsModel::getAlignmentRole(const QModelIndex& _index) const {
QVariant TransactionsModel::getUserRole(const QModelIndex& _index, int _role, DynexCN::TransactionId _transactionId,
DynexCN::WalletLegacyTransaction& _transaction, DynexCN::TransferId _transferId, DynexCN::WalletLegacyTransfer& _transfer) const {
switch(_role) {

case ROLE_STATE:
return static_cast<quint8>(_transaction.state);

case ROLE_DATE:
return (_transaction.timestamp > 0 ? QDateTime::fromTime_t(_transaction.timestamp) : QDateTime());

case ROLE_TYPE: {
QString transactionAddress = _index.data(ROLE_ADDRESS).toString();
if(_transaction.isCoinbase) {
return static_cast<quint8>(TransactionType::MINED);
} else if (WalletAdapter::instance().isFusionTransaction(_transaction)) {
return static_cast<quint8>(TransactionType::FUSION);
} else if (!transactionAddress.compare(WalletAdapter::instance().getAddress())) {
return static_cast<quint8>(TransactionType::INOUT);
} else if(_transaction.totalAmount < 0) {
Expand Down
2 changes: 1 addition & 1 deletion src/WalletGui/gui/TransactionsModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class TransactionsModel : public QAbstractItemModel {

enum Roles{
ROLE_DATE = Qt::UserRole, ROLE_TYPE, ROLE_HASH, ROLE_ADDRESS, ROLE_AMOUNT, ROLE_PAYMENT_ID, ROLE_ICON,
ROLE_TRANSACTION_ID, ROLE_HEIGHT, ROLE_FEE, ROLE_NUMBER_OF_CONFIRMATIONS, ROLE_COLUMN, ROLE_ROW
ROLE_TRANSACTION_ID, ROLE_HEIGHT, ROLE_FEE, ROLE_NUMBER_OF_CONFIRMATIONS, ROLE_COLUMN, ROLE_ROW, ROLE_STATE
};

static TransactionsModel& instance();
Expand Down
Loading

0 comments on commit 0b174d1

Please sign in to comment.