create migration plan
This commit is contained in:
@@ -29,13 +29,5 @@ int main(int argc, char *argv[])
|
|||||||
w.setWindowTitle("zec-qt-wallet v" + QString(APP_VERSION));
|
w.setWindowTitle("zec-qt-wallet v" + QString(APP_VERSION));
|
||||||
w.show();
|
w.show();
|
||||||
|
|
||||||
// Temp
|
|
||||||
Turnstile t;
|
|
||||||
double amt = 0.0003;
|
|
||||||
qDebug() << QString::number(amt, 'f', 8) << ":";
|
|
||||||
for (auto a : t.splitAmount(amt, 3)) {
|
|
||||||
qDebug() << QString::number(a, 'f', 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
return QApplication::exec();
|
return QApplication::exec();
|
||||||
}
|
}
|
||||||
|
|||||||
61
src/rpc.cpp
61
src/rpc.cpp
@@ -2,6 +2,7 @@
|
|||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "senttxstore.h"
|
#include "senttxstore.h"
|
||||||
|
#include "turnstile.h"
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
@@ -46,6 +47,7 @@ RPC::RPC(QNetworkAccessManager* client, MainWindow* main) {
|
|||||||
});
|
});
|
||||||
// Start at every 10s. When an operation is pending, this will change to every second
|
// Start at every 10s. When an operation is pending, this will change to every second
|
||||||
txTimer->start(Utils::updateSpeed);
|
txTimer->start(Utils::updateSpeed);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RPC::~RPC() {
|
RPC::~RPC() {
|
||||||
@@ -284,54 +286,6 @@ void RPC::handleTxError(const QString& error) {
|
|||||||
msg.exec();
|
msg.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void RPC::getBatchRPC(
|
|
||||||
const QList<QString>& payloads,
|
|
||||||
std::function<json(QString)> payloadGenerator,
|
|
||||||
std::function<void(QMap<QString, json>*)> cb)
|
|
||||||
{
|
|
||||||
auto responses = new QMap<QString, json>(); // zAddr -> list of responses for each call.
|
|
||||||
int totalSize = payloads.size();
|
|
||||||
|
|
||||||
for (auto item: payloads) {
|
|
||||||
json payload = payloadGenerator(item);
|
|
||||||
|
|
||||||
QNetworkReply *reply = restclient->post(request, QByteArray::fromStdString(payload.dump()));
|
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
|
||||||
reply->deleteLater();
|
|
||||||
|
|
||||||
auto all = reply->readAll();
|
|
||||||
auto parsed = json::parse(all.toStdString(), nullptr, false);
|
|
||||||
|
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
|
||||||
qDebug() << QString::fromStdString(parsed.dump());
|
|
||||||
qDebug() << reply->errorString();
|
|
||||||
|
|
||||||
(*responses)[item] = json::object(); // Empty object
|
|
||||||
} else {
|
|
||||||
if (parsed.is_discarded()) {
|
|
||||||
(*responses)[item] = json::object(); // Empty object
|
|
||||||
} else {
|
|
||||||
(*responses)[item] = parsed["result"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
auto waitTimer = new QTimer(main);
|
|
||||||
QObject::connect(waitTimer, &QTimer::timeout, [=]() {
|
|
||||||
if (responses->size() == totalSize) {
|
|
||||||
waitTimer->stop();
|
|
||||||
|
|
||||||
cb(responses);
|
|
||||||
|
|
||||||
waitTimer->deleteLater();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
waitTimer->start(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh received z txs by calling z_listreceivedbyaddress/gettransaction
|
// Refresh received z txs by calling z_listreceivedbyaddress/gettransaction
|
||||||
void RPC::refreshReceivedZTrans(QList<QString> zaddrs) {
|
void RPC::refreshReceivedZTrans(QList<QString> zaddrs) {
|
||||||
|
|
||||||
@@ -349,7 +303,7 @@ void RPC::refreshReceivedZTrans(QList<QString> zaddrs) {
|
|||||||
// and each z-Addr can have multiple received txs.
|
// and each z-Addr can have multiple received txs.
|
||||||
|
|
||||||
// 1. For each z-Addr, get list of received txs
|
// 1. For each z-Addr, get list of received txs
|
||||||
getBatchRPC(zaddrs,
|
getBatchRPC<QString>(zaddrs,
|
||||||
[=] (QString zaddr) {
|
[=] (QString zaddr) {
|
||||||
json payload = {
|
json payload = {
|
||||||
{"jsonrpc", "1.0"},
|
{"jsonrpc", "1.0"},
|
||||||
@@ -373,7 +327,7 @@ void RPC::refreshReceivedZTrans(QList<QString> zaddrs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. For all txids, go and get the details of that txid.
|
// 2. For all txids, go and get the details of that txid.
|
||||||
getBatchRPC(txids.toList(),
|
getBatchRPC<QString>(txids.toList(),
|
||||||
[=] (QString txid) {
|
[=] (QString txid) {
|
||||||
json payload = {
|
json payload = {
|
||||||
{"jsonrpc", "1.0"},
|
{"jsonrpc", "1.0"},
|
||||||
@@ -507,6 +461,11 @@ void RPC::refreshAddresses() {
|
|||||||
|
|
||||||
// Function to create the data model and update the views, used below.
|
// Function to create the data model and update the views, used below.
|
||||||
void RPC::updateUI(bool anyUnconfirmed) {
|
void RPC::updateUI(bool anyUnconfirmed) {
|
||||||
|
|
||||||
|
// Temp
|
||||||
|
Turnstile t(this);
|
||||||
|
t.planMigration(zaddresses->at(1), zaddresses->at(0));
|
||||||
|
|
||||||
ui->unconfirmedWarning->setVisible(anyUnconfirmed);
|
ui->unconfirmedWarning->setVisible(anyUnconfirmed);
|
||||||
|
|
||||||
// Update balances model data, which will update the table too
|
// Update balances model data, which will update the table too
|
||||||
@@ -624,7 +583,7 @@ void RPC::refreshSentZTrans() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Look up all the txids to get the confirmation count for them.
|
// Look up all the txids to get the confirmation count for them.
|
||||||
getBatchRPC(txids,
|
getBatchRPC<QString>(txids,
|
||||||
[=] (QString txid) {
|
[=] (QString txid) {
|
||||||
json payload = {
|
json payload = {
|
||||||
{"jsonrpc", "1.0"},
|
{"jsonrpc", "1.0"},
|
||||||
|
|||||||
54
src/rpc.h
54
src/rpc.h
@@ -46,6 +46,55 @@ public:
|
|||||||
void newZaddr (bool sapling, const std::function<void(json)>& cb);
|
void newZaddr (bool sapling, const std::function<void(json)>& cb);
|
||||||
void newTaddr (const std::function<void(json)>& cb);
|
void newTaddr (const std::function<void(json)>& cb);
|
||||||
|
|
||||||
|
// Batch method. Note: Because of the template, it has to be in the header file.
|
||||||
|
template<class T>
|
||||||
|
void getBatchRPC(const QList<T>& payloads,
|
||||||
|
std::function<json(T)> payloadGenerator,
|
||||||
|
std::function<void(QMap<T, json>*)> cb) {
|
||||||
|
auto responses = new QMap<T, json>(); // zAddr -> list of responses for each call.
|
||||||
|
int totalSize = payloads.size();
|
||||||
|
|
||||||
|
for (auto item: payloads) {
|
||||||
|
json payload = payloadGenerator(item);
|
||||||
|
|
||||||
|
QNetworkReply *reply = restclient->post(request, QByteArray::fromStdString(payload.dump()));
|
||||||
|
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, [=] {
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
auto all = reply->readAll();
|
||||||
|
auto parsed = json::parse(all.toStdString(), nullptr, false);
|
||||||
|
|
||||||
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
|
qDebug() << QString::fromStdString(parsed.dump());
|
||||||
|
qDebug() << reply->errorString();
|
||||||
|
|
||||||
|
(*responses)[item] = json::object(); // Empty object
|
||||||
|
} else {
|
||||||
|
if (parsed.is_discarded()) {
|
||||||
|
(*responses)[item] = json::object(); // Empty object
|
||||||
|
} else {
|
||||||
|
(*responses)[item] = parsed["result"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto waitTimer = new QTimer(main);
|
||||||
|
QObject::connect(waitTimer, &QTimer::timeout, [=]() {
|
||||||
|
if (responses->size() == totalSize) {
|
||||||
|
waitTimer->stop();
|
||||||
|
|
||||||
|
cb(responses);
|
||||||
|
|
||||||
|
waitTimer->deleteLater();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
waitTimer->start(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void doRPC (const json& payload, const std::function<void(json)>& cb);
|
void doRPC (const json& payload, const std::function<void(json)>& cb);
|
||||||
void doSendRPC (const json& payload, const std::function<void(json)>& cb);
|
void doSendRPC (const json& payload, const std::function<void(json)>& cb);
|
||||||
@@ -71,11 +120,6 @@ private:
|
|||||||
void handleConnectionError (const QString& error);
|
void handleConnectionError (const QString& error);
|
||||||
void handleTxError (const QString& error);
|
void handleTxError (const QString& error);
|
||||||
|
|
||||||
// Batch
|
|
||||||
void getBatchRPC(const QList<QString>& payloads,
|
|
||||||
std::function<json(QString)> payloadGenerator,
|
|
||||||
std::function<void(QMap<QString, json>*)> cb);
|
|
||||||
|
|
||||||
QNetworkAccessManager* restclient;
|
QNetworkAccessManager* restclient;
|
||||||
QNetworkRequest request;
|
QNetworkRequest request;
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,125 @@
|
|||||||
#include "turnstile.h"
|
#include "turnstile.h"
|
||||||
|
#include "rpc.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
Turnstile::Turnstile() {
|
#include "precompiled.h"
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
Turnstile::Turnstile(RPC* _rpc) {
|
||||||
|
this->rpc = _rpc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Turnstile::~Turnstile() {
|
Turnstile::~Turnstile() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Turnstile::writeableFile() {
|
||||||
|
auto filename = QStringLiteral("turnstilemigrationplan.dat");
|
||||||
|
|
||||||
|
auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
|
||||||
|
if (!dir.exists())
|
||||||
|
QDir().mkpath(dir.absolutePath());
|
||||||
|
|
||||||
|
if (Settings::getInstance()->isTestnet()) {
|
||||||
|
return dir.filePath("testnet-" % filename);
|
||||||
|
} else {
|
||||||
|
return dir.filePath(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data stream write/read methods for migration items
|
||||||
|
QDataStream &operator<<(QDataStream& ds, const TurnstileMigrationItem& item) {
|
||||||
|
return ds << "v1" << item.fromAddr << item.intTAddr
|
||||||
|
<< item.destAddr << item.amount << item.blockNumber << item.chaff;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream &operator>>(QDataStream& ds, TurnstileMigrationItem& item) {
|
||||||
|
QString version;
|
||||||
|
return ds >> version >> item.fromAddr >> item.intTAddr
|
||||||
|
>> item.destAddr >> item.amount >> item.blockNumber >> item.chaff;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Turnstile::writeMigrationPlan(QList<TurnstileMigrationItem> plan) {
|
||||||
|
QFile file(writeableFile());
|
||||||
|
file.open(QIODevice::WriteOnly);
|
||||||
|
QDataStream out(&file); // we will serialize the data into the file
|
||||||
|
out << plan;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<TurnstileMigrationItem> Turnstile::readMigrationPlan() {
|
||||||
|
QFile file(writeableFile());
|
||||||
|
|
||||||
|
QList<TurnstileMigrationItem> plan;
|
||||||
|
if (!file.exists()) return plan;
|
||||||
|
|
||||||
|
file.open(QIODevice::ReadOnly);
|
||||||
|
QDataStream in(&file); // read the data serialized from the file
|
||||||
|
in >> plan;
|
||||||
|
|
||||||
|
return plan;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Turnstile::planMigration(QString zaddr, QString destAddr) {
|
||||||
|
// First, get the balance and split up the amounts
|
||||||
|
auto bal = rpc->getAllBalances()->value(zaddr);
|
||||||
|
auto splits = splitAmount(bal+2354, 5);
|
||||||
|
|
||||||
|
// Then, generate an intermediate t-Address for each part using getBatchRPC
|
||||||
|
rpc->getBatchRPC<double>(splits,
|
||||||
|
[=] (double /*unused*/) {
|
||||||
|
json payload = {
|
||||||
|
{"jsonrpc", "1.0"},
|
||||||
|
{"id", "someid"},
|
||||||
|
{"method", "getnewaddress"},
|
||||||
|
};
|
||||||
|
return payload;
|
||||||
|
},
|
||||||
|
[=] (QMap<double, json>* newAddrs) {
|
||||||
|
// Get block numbers
|
||||||
|
auto curBlock = Settings::getInstance()->getBlockNumber();
|
||||||
|
auto blockNumbers = getBlockNumbers(curBlock, curBlock + 10, splits.size());
|
||||||
|
|
||||||
|
// Assign the amounts to the addresses.
|
||||||
|
QList<TurnstileMigrationItem> migItems;
|
||||||
|
|
||||||
|
for (int i=0; i < splits.size(); i++) {
|
||||||
|
auto tAddr = newAddrs->values()[i].get<json::string_t>();
|
||||||
|
auto item = TurnstileMigrationItem { zaddr, QString::fromStdString(tAddr), destAddr,
|
||||||
|
blockNumbers[i], splits[i], i == splits.size() -1 };
|
||||||
|
migItems.push_back(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(migItems.begin(), migItems.end(), [&] (auto a, auto b) {
|
||||||
|
return a.blockNumber < b.blockNumber;
|
||||||
|
});
|
||||||
|
|
||||||
|
// The first migration is shifted to the current block, so the user sees something
|
||||||
|
// happening immediately
|
||||||
|
migItems[0].blockNumber = curBlock;
|
||||||
|
|
||||||
|
writeMigrationPlan(migItems);
|
||||||
|
auto readPlan = readMigrationPlan();
|
||||||
|
|
||||||
|
for (auto item : readPlan) {
|
||||||
|
qDebug() << item.fromAddr << item.intTAddr
|
||||||
|
<< item.destAddr << item.amount << item.blockNumber << item.chaff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<int> Turnstile::getBlockNumbers(int start, int end, int count) {
|
||||||
|
QList<int> blocks;
|
||||||
|
// Generate 'count' numbers between [start, end]
|
||||||
|
for (int i=0; i < count; i++) {
|
||||||
|
auto blk = (std::rand() % (end - start)) + start;
|
||||||
|
blocks.push_back(blk);
|
||||||
|
}
|
||||||
|
|
||||||
|
return blocks;
|
||||||
|
}
|
||||||
|
|
||||||
QList<double> Turnstile::splitAmount(double amount, int parts) {
|
QList<double> Turnstile::splitAmount(double amount, int parts) {
|
||||||
QList<double> amounts;
|
QList<double> amounts;
|
||||||
// Need at least 0.0004 ZEC for this
|
// Need at least 0.0004 ZEC for this
|
||||||
|
|||||||
@@ -1,16 +1,37 @@
|
|||||||
#ifndef TURNSTILE_H
|
#ifndef TURNSTILE_H
|
||||||
#define TURNSTILE_H
|
#define TURNSTILE_H
|
||||||
|
|
||||||
|
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
|
|
||||||
|
class RPC;
|
||||||
|
|
||||||
|
struct TurnstileMigrationItem {
|
||||||
|
QString fromAddr;
|
||||||
|
QString intTAddr;
|
||||||
|
QString destAddr;
|
||||||
|
int blockNumber;
|
||||||
|
double amount;
|
||||||
|
bool chaff;
|
||||||
|
};
|
||||||
|
|
||||||
class Turnstile
|
class Turnstile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Turnstile();
|
Turnstile(RPC* _rpc);
|
||||||
~Turnstile();
|
~Turnstile();
|
||||||
|
|
||||||
|
void planMigration(QString zaddr, QString destAddr);
|
||||||
QList<double> splitAmount(double amount, int parts);
|
QList<double> splitAmount(double amount, int parts);
|
||||||
void fillAmounts(QList<double>& amounts, double amount, int count);
|
void fillAmounts(QList<double>& amounts, double amount, int count);
|
||||||
|
|
||||||
|
void writeMigrationPlan(QList<TurnstileMigrationItem> plan);
|
||||||
|
QList<TurnstileMigrationItem> readMigrationPlan();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<int> getBlockNumbers(int start, int end, int count);
|
||||||
|
QString writeableFile();
|
||||||
|
RPC* rpc;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
Reference in New Issue
Block a user