mirror of
https://git.wownero.com/wowlet/wowlet.git
synced 2024-08-15 01:03:14 +00:00
8968a8cbce
``` ./wowlet --daemon 127.0.0.1:1234 --daemon-password "sekrit" ``` The wallet will start in the background and expose a websocket port that you can connect to using a websocket client. This way, you will be able to control the wallet via websockets. The commands are defined in wsserver.cpp, in the `processBinaryMessage()` function. - `openWallet` - opens a wallet by path/password. - `closeWallet` - close current wallet. - `addressList` - Returns a list of receive addresses. - `sendTransaction` - Creates and sends a transaction. - `createWallet` - Create a wallet by path/password. - `transactionHistory` - Returns the complete list of transactions - `addressBook` - Returns the complete list of address book entries. Messages sent back and forth between the server and client are JSON. There is a Python example client available over at https://git.wownero.com/wownero/wowlet-ws-client
275 lines
No EOL
9.2 KiB
C++
275 lines
No EOL
9.2 KiB
C++
// SPDX-License-Identifier: BSD-3-Clause
|
|
// Copyright (c) 2014-2021, The Monero Project.
|
|
|
|
#include "TransactionHistory.h"
|
|
#include "TransactionInfo.h"
|
|
#include "utils/utils.h"
|
|
#include "appcontext.h"
|
|
|
|
|
|
bool TransactionHistory::transaction(int index, std::function<void (TransactionInfo &)> callback)
|
|
{
|
|
QReadLocker locker(&m_lock);
|
|
|
|
if (index < 0 || index >= m_tinfo.size()) {
|
|
qCritical("%s: no transaction info for index %d", __FUNCTION__, index);
|
|
qCritical("%s: there's %d transactions in backend", __FUNCTION__, m_pimpl->count());
|
|
return false;
|
|
}
|
|
|
|
callback(*m_tinfo.value(index));
|
|
return true;
|
|
}
|
|
|
|
TransactionInfo* TransactionHistory::transaction(const QString &id)
|
|
{
|
|
QReadLocker locker(&m_lock);
|
|
|
|
auto itr = std::find_if(m_tinfo.begin(), m_tinfo.end(),
|
|
[&](const TransactionInfo * ti) {
|
|
return ti->hash() == id;
|
|
});
|
|
return itr != m_tinfo.end() ? *itr : nullptr;
|
|
}
|
|
|
|
void TransactionHistory::refresh(quint32 accountIndex)
|
|
{
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
|
QDateTime firstDateTime = QDate(2014, 4, 18).startOfDay();
|
|
#else
|
|
QDateTime firstDateTime = QDateTime(QDate(2014, 4, 18)); // the genesis block
|
|
#endif
|
|
QDateTime lastDateTime = QDateTime::currentDateTime().addDays(1); // tomorrow (guard against jitter and timezones)
|
|
|
|
emit refreshStarted();
|
|
|
|
{
|
|
QWriteLocker locker(&m_lock);
|
|
|
|
qDeleteAll(m_tinfo);
|
|
m_tinfo.clear();
|
|
|
|
quint64 lastTxHeight = 0;
|
|
m_locked = false;
|
|
m_minutesToUnlock = 0;
|
|
|
|
m_pimpl->refresh();
|
|
for (const auto i : m_pimpl->getAll()) {
|
|
if (i->subaddrAccount() != accountIndex) {
|
|
continue;
|
|
}
|
|
|
|
m_tinfo.append(new TransactionInfo(i, this));
|
|
|
|
const TransactionInfo *ti = m_tinfo.back();
|
|
// looking for transactions timestamp scope
|
|
if (ti->timestamp() >= lastDateTime) {
|
|
lastDateTime = ti->timestamp();
|
|
}
|
|
if (ti->timestamp() <= firstDateTime) {
|
|
firstDateTime = ti->timestamp();
|
|
}
|
|
quint64 requiredConfirmations = (ti->blockHeight() < ti->unlockTime()) ? ti->unlockTime() - ti->blockHeight() : 10;
|
|
// store last tx height
|
|
if (ti->confirmations() < requiredConfirmations && ti->blockHeight() >= lastTxHeight) {
|
|
lastTxHeight = ti->blockHeight();
|
|
// TODO: Fetch block time and confirmations needed from wallet2?
|
|
m_minutesToUnlock = (requiredConfirmations - ti->confirmations()) * 2;
|
|
m_locked = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
emit refreshFinished();
|
|
|
|
if (m_firstDateTime != firstDateTime) {
|
|
m_firstDateTime = firstDateTime;
|
|
emit firstDateTimeChanged();
|
|
}
|
|
if (m_lastDateTime != lastDateTime) {
|
|
m_lastDateTime = lastDateTime;
|
|
emit lastDateTimeChanged();
|
|
}
|
|
}
|
|
|
|
void TransactionHistory::setTxNote(const QString &txid, const QString ¬e)
|
|
{
|
|
m_pimpl->setTxNote(txid.toStdString(), note.toStdString());
|
|
this->refresh(0); // todo: get actual account index
|
|
emit txNoteChanged();
|
|
}
|
|
|
|
quint64 TransactionHistory::count() const
|
|
{
|
|
QReadLocker locker(&m_lock);
|
|
|
|
return m_tinfo.count();
|
|
}
|
|
|
|
QDateTime TransactionHistory::firstDateTime() const
|
|
{
|
|
return m_firstDateTime;
|
|
}
|
|
|
|
QDateTime TransactionHistory::lastDateTime() const
|
|
{
|
|
return m_lastDateTime;
|
|
}
|
|
|
|
quint64 TransactionHistory::minutesToUnlock() const
|
|
{
|
|
return m_minutesToUnlock;
|
|
}
|
|
|
|
bool TransactionHistory::TransactionHistory::locked() const
|
|
{
|
|
return m_locked;
|
|
}
|
|
|
|
|
|
TransactionHistory::TransactionHistory(Monero::TransactionHistory *pimpl, QObject *parent)
|
|
: QObject(parent), m_pimpl(pimpl), m_minutesToUnlock(0), m_locked(false)
|
|
{
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
|
m_firstDateTime = QDate(2014, 4, 18).startOfDay();
|
|
#else
|
|
m_firstDateTime = QDateTime(QDate(2014, 4, 18)); // the genesis block
|
|
#endif
|
|
m_lastDateTime = QDateTime::currentDateTime().addDays(1); // tomorrow (guard against jitter and timezones)
|
|
}
|
|
|
|
bool TransactionHistory::writeCSV(const QString &path) {
|
|
auto data = QString("");
|
|
QReadLocker locker(&m_lock);
|
|
for (const auto &tx : m_pimpl->getAll()) {
|
|
if (tx->subaddrAccount() != 0) { // only account 0
|
|
continue;
|
|
}
|
|
|
|
TransactionInfo info(tx, this);
|
|
|
|
// collect column data
|
|
QDateTime timeStamp = info.timestamp();
|
|
double amount = info.amount();
|
|
|
|
// calc historical fiat price
|
|
QString fiatAmount;
|
|
QString preferredFiatSymbol = config()->get(Config::preferredFiatCurrency).toString();
|
|
const double usd_price = AppContext::txFiatHistory->get(timeStamp.toString("yyyyMMdd"));
|
|
double fiat_price = usd_price * amount;
|
|
|
|
if(preferredFiatSymbol != "USD")
|
|
fiat_price = AppContext::prices->convert("USD", preferredFiatSymbol, fiat_price);
|
|
double fiat_rounded = ceil(Utils::roundSignificant(fiat_price, 3) * 100.0) / 100.0;
|
|
if(fiat_price != 0)
|
|
fiatAmount = QString("%1 %2").arg(QString::number(fiat_rounded)).arg(preferredFiatSymbol);
|
|
|
|
// collect some more column data
|
|
quint64 atomicAmount = info.atomicAmount();
|
|
quint32 subaddrAccount = info.subaddrAccount();
|
|
QString fee = info.fee();
|
|
QString direction = QString("");
|
|
TransactionInfo::Direction _direction = info.direction();
|
|
if(_direction == TransactionInfo::Direction_In)
|
|
direction = QString("in");
|
|
else if(_direction == TransactionInfo::Direction_Out)
|
|
direction = QString("out");
|
|
else
|
|
continue; // skip TransactionInfo::Direction_Both
|
|
|
|
QString label = info.label();
|
|
label.remove(QChar('"')); // reserved
|
|
quint64 blockHeight = info.blockHeight();
|
|
QString date = info.date() + " " + info.time();
|
|
uint epoch = timeStamp.toTime_t();
|
|
QString displayAmount = info.displayAmount();
|
|
QString paymentId = info.paymentId();
|
|
if(paymentId == "0000000000000000")
|
|
paymentId = "";
|
|
|
|
// format and write
|
|
QString line = QString("%1,%2,%3,%4,%5,\"%6\",%7,%8,%9,\"%10\",%11,%12\n")
|
|
.arg(QString::number(blockHeight), QString::number(epoch), date)
|
|
.arg(direction, QString::number(amount), fiatAmount, QString::number(atomicAmount))
|
|
.arg(info.fee(), info.hash(), label, QString::number(subaddrAccount))
|
|
.arg(paymentId);
|
|
data += line;
|
|
}
|
|
|
|
if(data.isEmpty())
|
|
return false;
|
|
|
|
data = QString("blockHeight,epoch,date,direction,amount,fiat,atomicAmount,fee,txid,label,subaddrAccount,paymentId\n%1").arg(data);
|
|
return Utils::fileWrite(path, data);
|
|
}
|
|
|
|
QJsonArray TransactionHistory::toJsonArray(){
|
|
QJsonArray return_array;
|
|
|
|
for (const auto &tx : m_pimpl->getAll()) {
|
|
if (tx->subaddrAccount() != 0) { // only account 0
|
|
continue;
|
|
}
|
|
|
|
TransactionInfo info(tx, this);
|
|
|
|
// collect column data
|
|
QDateTime timeStamp = info.timestamp();
|
|
double amount = info.amount();
|
|
|
|
// calc historical fiat price
|
|
QString fiatAmount;
|
|
QString preferredCur = config()->get(Config::preferredFiatCurrency).toString();
|
|
const double usd_price = AppContext::txFiatHistory->get(timeStamp.toString("yyyyMMdd"));
|
|
double fiat_price = usd_price * amount;
|
|
|
|
if(preferredCur != "USD")
|
|
fiat_price = AppContext::prices->convert("USD", preferredCur, fiat_price);
|
|
double fiat_rounded = ceil(Utils::roundSignificant(fiat_price, 3) * 100.0) / 100.0;
|
|
if(fiat_price != 0)
|
|
fiatAmount = QString("%1 %2").arg(QString::number(fiat_rounded)).arg(preferredCur);
|
|
|
|
// collect some more column data
|
|
quint64 atomicAmount = info.atomicAmount();
|
|
quint32 subaddrAccount = info.subaddrAccount();
|
|
QString fee = info.fee();
|
|
QString direction = QString("");
|
|
TransactionInfo::Direction _direction = info.direction();
|
|
if(_direction == TransactionInfo::Direction_In)
|
|
direction = QString("in");
|
|
else if(_direction == TransactionInfo::Direction_Out)
|
|
direction = QString("out");
|
|
else
|
|
continue; // skip TransactionInfo::Direction_Both
|
|
|
|
QString label = info.label();
|
|
quint64 blockHeight = info.blockHeight();
|
|
QString date = info.date() + " " + info.time();
|
|
uint epoch = timeStamp.toTime_t();
|
|
QString displayAmount = info.displayAmount();
|
|
QString paymentId = info.paymentId();
|
|
if(paymentId == "0000000000000000")
|
|
paymentId = "";
|
|
|
|
QJsonObject tx_item;
|
|
tx_item["timestamp"] = (int) epoch;
|
|
tx_item["date"] = date;
|
|
tx_item["preferred_currency"] = preferredCur;
|
|
tx_item["direction"] = direction;
|
|
tx_item["blockheight"] = (int) blockHeight;
|
|
tx_item["description"] = label;
|
|
tx_item["subaddress_account"] = (int) subaddrAccount;
|
|
tx_item["payment_id"] = paymentId;
|
|
|
|
tx_item["amount"] = amount;
|
|
tx_item["amount_display"] = displayAmount;
|
|
tx_item["amount_fiat"] = fiatAmount;
|
|
tx_item["fiat_rounded"] = fiat_rounded;
|
|
tx_item["fiat_price"] = fiat_price;
|
|
tx_item["fee"] = fee;
|
|
|
|
return_array.append(tx_item);
|
|
}
|
|
|
|
return return_array;
|
|
} |