From f39722eb69f9cec76f58ab6c46faf158876e1c35 Mon Sep 17 00:00:00 2001 From: Aditya Kulkarni Date: Thu, 25 Oct 2018 11:52:02 -0700 Subject: [PATCH] create migration plan --- src/main.cpp | 8 ---- src/rpc.cpp | 61 ++++-------------------- src/rpc.h | 54 +++++++++++++++++++-- src/turnstile.cpp | 117 ++++++++++++++++++++++++++++++++++++++++++++-- src/turnstile.h | 23 ++++++++- 5 files changed, 195 insertions(+), 68 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 96581a4..c86c2af 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,14 +28,6 @@ int main(int argc, char *argv[]) MainWindow w; w.setWindowTitle("zec-qt-wallet v" + QString(APP_VERSION)); 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(); } diff --git a/src/rpc.cpp b/src/rpc.cpp index 5dce998..c70a6f1 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -2,6 +2,7 @@ #include "utils.h" #include "settings.h" #include "senttxstore.h" +#include "turnstile.h" 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 txTimer->start(Utils::updateSpeed); + } RPC::~RPC() { @@ -284,54 +286,6 @@ void RPC::handleTxError(const QString& error) { msg.exec(); } - -void RPC::getBatchRPC( - const QList& payloads, - std::function payloadGenerator, - std::function*)> cb) -{ - auto responses = new QMap(); // 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 void RPC::refreshReceivedZTrans(QList zaddrs) { @@ -349,7 +303,7 @@ void RPC::refreshReceivedZTrans(QList zaddrs) { // and each z-Addr can have multiple received txs. // 1. For each z-Addr, get list of received txs - getBatchRPC(zaddrs, + getBatchRPC(zaddrs, [=] (QString zaddr) { json payload = { {"jsonrpc", "1.0"}, @@ -373,7 +327,7 @@ void RPC::refreshReceivedZTrans(QList zaddrs) { } // 2. For all txids, go and get the details of that txid. - getBatchRPC(txids.toList(), + getBatchRPC(txids.toList(), [=] (QString txid) { json payload = { {"jsonrpc", "1.0"}, @@ -507,6 +461,11 @@ void RPC::refreshAddresses() { // Function to create the data model and update the views, used below. void RPC::updateUI(bool anyUnconfirmed) { + + // Temp + Turnstile t(this); + t.planMigration(zaddresses->at(1), zaddresses->at(0)); + ui->unconfirmedWarning->setVisible(anyUnconfirmed); // 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. - getBatchRPC(txids, + getBatchRPC(txids, [=] (QString txid) { json payload = { {"jsonrpc", "1.0"}, diff --git a/src/rpc.h b/src/rpc.h index bdb903b..1bbd6fd 100644 --- a/src/rpc.h +++ b/src/rpc.h @@ -46,6 +46,55 @@ public: void newZaddr (bool sapling, const std::function& cb); void newTaddr (const std::function& cb); + // Batch method. Note: Because of the template, it has to be in the header file. + template + void getBatchRPC(const QList& payloads, + std::function payloadGenerator, + std::function*)> cb) { + auto responses = new QMap(); // 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: void doRPC (const json& payload, const std::function& cb); void doSendRPC (const json& payload, const std::function& cb); @@ -71,11 +120,6 @@ private: void handleConnectionError (const QString& error); void handleTxError (const QString& error); - // Batch - void getBatchRPC(const QList& payloads, - std::function payloadGenerator, - std::function*)> cb); - QNetworkAccessManager* restclient; QNetworkRequest request; diff --git a/src/turnstile.cpp b/src/turnstile.cpp index a4f8f94..9385ba8 100644 --- a/src/turnstile.cpp +++ b/src/turnstile.cpp @@ -1,14 +1,125 @@ #include "turnstile.h" - +#include "rpc.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() { } +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 plan) { + QFile file(writeableFile()); + file.open(QIODevice::WriteOnly); + QDataStream out(&file); // we will serialize the data into the file + out << plan; +} + +QList Turnstile::readMigrationPlan() { + QFile file(writeableFile()); + + QList 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(splits, + [=] (double /*unused*/) { + json payload = { + {"jsonrpc", "1.0"}, + {"id", "someid"}, + {"method", "getnewaddress"}, + }; + return payload; + }, + [=] (QMap* newAddrs) { + // Get block numbers + auto curBlock = Settings::getInstance()->getBlockNumber(); + auto blockNumbers = getBlockNumbers(curBlock, curBlock + 10, splits.size()); + + // Assign the amounts to the addresses. + QList migItems; + + for (int i=0; i < splits.size(); i++) { + auto tAddr = newAddrs->values()[i].get(); + 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 Turnstile::getBlockNumbers(int start, int end, int count) { + QList 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 Turnstile::splitAmount(double amount, int parts) { QList amounts; // Need at least 0.0004 ZEC for this diff --git a/src/turnstile.h b/src/turnstile.h index 7a4bac7..427634b 100644 --- a/src/turnstile.h +++ b/src/turnstile.h @@ -1,16 +1,37 @@ #ifndef TURNSTILE_H #define TURNSTILE_H + #include "precompiled.h" +class RPC; + +struct TurnstileMigrationItem { + QString fromAddr; + QString intTAddr; + QString destAddr; + int blockNumber; + double amount; + bool chaff; +}; + class Turnstile { public: - Turnstile(); + Turnstile(RPC* _rpc); ~Turnstile(); + void planMigration(QString zaddr, QString destAddr); QList splitAmount(double amount, int parts); void fillAmounts(QList& amounts, double amount, int count); + + void writeMigrationPlan(QList plan); + QList readMigrationPlan(); + +private: + QList getBlockNumbers(int start, int end, int count); + QString writeableFile(); + RPC* rpc; }; #endif \ No newline at end of file