Jonathan "Duke" Leto
5 years ago
10 changed files with 22 additions and 1286 deletions
@ -1,372 +0,0 @@ |
|||
#include "turnstile.h" |
|||
#include "mainwindow.h" |
|||
#include "balancestablemodel.h" |
|||
#include "rpc.h" |
|||
#include "settings.h" |
|||
|
|||
using json = nlohmann::json; |
|||
|
|||
Turnstile::Turnstile(RPC* _rpc, MainWindow* mainwindow) { |
|||
this->rpc = _rpc; |
|||
this->mainwindow = mainwindow; |
|||
} |
|||
|
|||
|
|||
void printPlan(QList<TurnstileMigrationItem> plan) { |
|||
for (auto item : plan) { |
|||
//qDebug() << item.fromAddr << item.intTAddr
|
|||
// << item.destAddr << item.amount << item.blockNumber << item.status;
|
|||
} |
|||
} |
|||
|
|||
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); |
|||
} |
|||
} |
|||
|
|||
void Turnstile::removeFile() { |
|||
QFile(writeableFile()).remove(); |
|||
} |
|||
|
|||
// Data stream write/read methods for migration items
|
|||
QDataStream &operator<<(QDataStream& ds, const TurnstileMigrationItem& item) { |
|||
return ds << QString("v1") << item.fromAddr << item.intTAddr |
|||
<< item.destAddr << item.amount << item.blockNumber << item.status; |
|||
} |
|||
|
|||
QDataStream &operator>>(QDataStream& ds, TurnstileMigrationItem& item) { |
|||
QString version; |
|||
return ds >> version >> item.fromAddr >> item.intTAddr |
|||
>> item.destAddr >> item.amount >> item.blockNumber >> item.status; |
|||
} |
|||
|
|||
void Turnstile::writeMigrationPlan(QList<TurnstileMigrationItem> plan) { |
|||
//qDebug() << QString("Writing plan");
|
|||
printPlan(plan); |
|||
|
|||
QFile file(writeableFile()); |
|||
file.open(QIODevice::ReadWrite | QIODevice::Truncate); |
|||
QDataStream out(&file); // we will serialize the data into the file
|
|||
out << plan; |
|||
file.close(); |
|||
} |
|||
|
|||
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; |
|||
|
|||
file.close(); |
|||
|
|||
// Sort to see when the next step is.
|
|||
std::sort(plan.begin(), plan.end(), [&] (auto a, auto b) { |
|||
return a.blockNumber < b.blockNumber; |
|||
}); |
|||
|
|||
return plan; |
|||
} |
|||
|
|||
void Turnstile::planMigration(QString zaddr, QString destAddr, int numsplits, int numBlocks) { |
|||
// First, get the balance and split up the amounts
|
|||
auto bal = rpc->getAllBalances()->value(zaddr); |
|||
auto splits = splitAmount(bal, numsplits); |
|||
|
|||
// Then, generate an intermediate t-address for each part using getBatchRPC
|
|||
rpc->getConnection()->doBatchRPC<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 + numBlocks, 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], |
|||
TurnstileMigrationItemStatus::NotStarted }; |
|||
migItems.push_back(item); |
|||
} |
|||
|
|||
// The first migration is shifted to the current block, so the user sees something
|
|||
// happening immediately
|
|||
if (migItems.empty()) { |
|||
// Show error and abort
|
|||
QMessageBox::warning(mainwindow, |
|||
QObject::tr("Locked funds"), |
|||
QObject::tr("Could not initiate migration.\nYou either have unconfirmed funds or the balance is too low for an automatic migration.")); |
|||
return; |
|||
} |
|||
|
|||
migItems[0].blockNumber = curBlock; |
|||
|
|||
std::sort(migItems.begin(), migItems.end(), [&] (auto a, auto b) { |
|||
return a.blockNumber < b.blockNumber; |
|||
}); |
|||
|
|||
writeMigrationPlan(migItems); |
|||
rpc->refresh(true); // Force refresh, to start the migration immediately
|
|||
} |
|||
); |
|||
} |
|||
|
|||
|
|||
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; |
|||
} |
|||
|
|||
// Need at least 0.0005 ZEC for this
|
|||
double Turnstile::minMigrationAmount = 0.0005; |
|||
|
|||
QList<double> Turnstile::splitAmount(double amount, int parts) { |
|||
QList<double> amounts; |
|||
|
|||
if (amount < minMigrationAmount) |
|||
return amounts; |
|||
|
|||
fillAmounts(amounts, amount, parts); |
|||
//qDebug() << amounts;
|
|||
|
|||
// Ensure they all add up!
|
|||
double sumofparts = 0; |
|||
for (auto a : amounts) { |
|||
sumofparts += a; |
|||
} |
|||
|
|||
// Add the Tx fees
|
|||
sumofparts += amounts.size() * Settings::getMinerFee(); |
|||
|
|||
return amounts; |
|||
} |
|||
|
|||
void Turnstile::fillAmounts(QList<double>& amounts, double amount, int count) { |
|||
if (count == 1 || amount < 0.01) { |
|||
// Also account for the fees needed to send all these transactions
|
|||
auto actual = amount - (Settings::getMinerFee() * (amounts.size() + 1)); |
|||
|
|||
amounts.push_back(actual); |
|||
return; |
|||
} |
|||
|
|||
// Get a random amount off the total amount and call recursively.
|
|||
// Multiply by hundred, because we'll operate on 0.01 ZEC minimum. We'll divide by 100 later on
|
|||
// in this function.
|
|||
double curAmount = std::rand() % (int)std::floor(amount * 100); |
|||
|
|||
// Try to round it off
|
|||
auto places = (int)std::floor(std::log10(curAmount)); |
|||
if (places > 0) { |
|||
auto a = std::pow(10, places); |
|||
curAmount = std::floor(curAmount / a) * a; |
|||
} |
|||
|
|||
// And divide by 100
|
|||
curAmount = curAmount / 100; |
|||
|
|||
if (curAmount > 0) |
|||
amounts.push_back(curAmount); |
|||
|
|||
fillAmounts(amounts, amount - curAmount, count - 1); |
|||
} |
|||
|
|||
QList<TurnstileMigrationItem>::Iterator |
|||
Turnstile::getNextStep(QList<TurnstileMigrationItem>& plan) { |
|||
// Get to the next unexecuted step
|
|||
auto fnIsEligibleItem = [&] (auto item) { |
|||
return item.status == TurnstileMigrationItemStatus::NotStarted || |
|||
item.status == TurnstileMigrationItemStatus::SentToT; |
|||
}; |
|||
|
|||
// Find the next step
|
|||
auto nextStep = std::find_if(plan.begin(), plan.end(), fnIsEligibleItem); |
|||
return nextStep; |
|||
} |
|||
|
|||
bool Turnstile::isMigrationPresent() { |
|||
auto plan = readMigrationPlan(); |
|||
return !plan.isEmpty(); |
|||
} |
|||
|
|||
ProgressReport Turnstile::getPlanProgress() { |
|||
auto plan = readMigrationPlan(); |
|||
|
|||
auto nextStep = getNextStep(plan); |
|||
|
|||
auto step = std::distance(plan.begin(), nextStep) * 2; // 2 steps per item
|
|||
if (nextStep != plan.end() && |
|||
nextStep->status == TurnstileMigrationItemStatus::SentToT) |
|||
step++; |
|||
|
|||
auto total = plan.size(); |
|||
|
|||
auto nextBlock = nextStep == plan.end() ? 0 : nextStep->blockNumber; |
|||
|
|||
bool hasErrors = std::find_if(plan.begin(), plan.end(), [=] (auto i) { |
|||
return i.status == TurnstileMigrationItemStatus::NotEnoughBalance || |
|||
i.status == TurnstileMigrationItemStatus::UnknownError; |
|||
}) != plan.end(); |
|||
|
|||
auto stepData = (nextStep == plan.end() ? std::prev(nextStep) : nextStep); |
|||
|
|||
return ProgressReport{(int)step, total*2, nextBlock, hasErrors, stepData->fromAddr, stepData->destAddr, stepData->intTAddr}; |
|||
} |
|||
|
|||
void Turnstile::executeMigrationStep() { |
|||
// Do a step only if not syncing, else wait for the blockchain to catch up
|
|||
if (Settings::getInstance()->isSyncing()) |
|||
return; |
|||
|
|||
auto plan = readMigrationPlan(); |
|||
|
|||
//qDebug() << QString("Executing step");
|
|||
printPlan(plan); |
|||
|
|||
// Get to the next unexecuted step
|
|||
auto fnIsEligibleItem = [&] (auto item) { |
|||
return item.status == TurnstileMigrationItemStatus::NotStarted || |
|||
item.status == TurnstileMigrationItemStatus::SentToT; |
|||
}; |
|||
|
|||
// Fn to find if there are any unconfirmed funds for this address.
|
|||
auto fnHasUnconfirmed = [=] (QString addr) { |
|||
auto utxoset = rpc->getUTXOs(); |
|||
return std::find_if(utxoset->begin(), utxoset->end(), [=] (auto utxo) { |
|||
return utxo.address == addr && utxo.confirmations == 0 && utxo.spendable; |
|||
}) != utxoset->end(); |
|||
}; |
|||
|
|||
// Find the next step
|
|||
auto nextStep = std::find_if(plan.begin(), plan.end(), fnIsEligibleItem); |
|||
|
|||
if (nextStep == plan.end()) |
|||
return; // Nothing to do
|
|||
|
|||
if (nextStep->blockNumber > Settings::getInstance()->getBlockNumber()) |
|||
return; |
|||
|
|||
// Is this the last step for this address?
|
|||
auto lastStep = std::find_if(std::next(nextStep), plan.end(), fnIsEligibleItem) == plan.end(); |
|||
|
|||
// Execute this step
|
|||
if (nextStep->status == TurnstileMigrationItemStatus::NotStarted) { |
|||
// Does this z addr have enough balance?
|
|||
if (fnHasUnconfirmed(nextStep->fromAddr)) { |
|||
//qDebug() << QString("unconfirmed, waiting");
|
|||
return; |
|||
} |
|||
|
|||
auto balance = rpc->getAllBalances()->value(nextStep->fromAddr); |
|||
if (nextStep->amount > balance) { |
|||
qDebug() << "Not enough balance!"; |
|||
nextStep->status = TurnstileMigrationItemStatus::NotEnoughBalance; |
|||
writeMigrationPlan(plan); |
|||
return; |
|||
} |
|||
|
|||
auto to = ToFields{ nextStep->intTAddr, nextStep->amount, "", "" }; |
|||
|
|||
// If this is the last step, then send the remaining amount instead of the actual amount.
|
|||
if (lastStep) { |
|||
auto remainingAmount = balance - Settings::getMinerFee(); |
|||
if (remainingAmount > 0) { |
|||
to.amount = remainingAmount; |
|||
} |
|||
} |
|||
|
|||
// Create the Tx
|
|||
auto tx = Tx{ nextStep->fromAddr, { to }, Settings::getMinerFee() }; |
|||
|
|||
// And send it
|
|||
doSendTx(tx, [=] () { |
|||
// Update status and write plan to disk
|
|||
nextStep->status = TurnstileMigrationItemStatus::SentToT; |
|||
writeMigrationPlan(plan); |
|||
}); |
|||
} else if (nextStep->status == TurnstileMigrationItemStatus::SentToT) { |
|||
// First thing to do is check to see if the funds are confirmed.
|
|||
// We'll check both the original sprout address and the intermediate t-addr for safety.
|
|||
if (fnHasUnconfirmed(nextStep->intTAddr) || fnHasUnconfirmed(nextStep->fromAddr)) { |
|||
//qDebug() << QString("unconfirmed, waiting");
|
|||
return; |
|||
} |
|||
|
|||
// Sometimes, we check too quickly, and the unspent UTXO is not updated yet, so we'll
|
|||
// double check to see if there is enough balance.
|
|||
if (!rpc->getAllBalances()->keys().contains(nextStep->intTAddr)) { |
|||
//qDebug() << QString("The intermediate t-address doesn't have balance, even though it seems to be confirmed");
|
|||
return; |
|||
} |
|||
|
|||
// Send it to the final destination address.
|
|||
auto bal = rpc->getAllBalances()->value(nextStep->intTAddr); |
|||
auto sendAmt = bal - Settings::getMinerFee(); |
|||
|
|||
if (sendAmt < 0) { |
|||
qDebug() << "Not enough balance!." << bal << ":" << sendAmt; |
|||
nextStep->status = TurnstileMigrationItemStatus::NotEnoughBalance; |
|||
writeMigrationPlan(plan); |
|||
return; |
|||
} |
|||
|
|||
QList<ToFields> to = { ToFields{ nextStep->destAddr, sendAmt, "", "" } }; |
|||
|
|||
// Create the Tx
|
|||
auto tx = Tx{ nextStep->intTAddr, to, Settings::getMinerFee()}; |
|||
|
|||
// And send it
|
|||
doSendTx(tx, [=] () { |
|||
// Update status and write plan to disk
|
|||
nextStep->status = TurnstileMigrationItemStatus::SentToZS; |
|||
writeMigrationPlan(plan); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
void Turnstile::doSendTx(Tx tx, std::function<void(void)> cb) { |
|||
rpc->executeTransaction(tx, [=] (QString opid) { |
|||
mainwindow->ui->statusBar->showMessage(QObject::tr("Computing Tx: ") % opid); |
|||
}, |
|||
[=] (QString /*opid*/, QString txid) { |
|||
mainwindow->ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid); |
|||
cb(); |
|||
}, |
|||
[=] (QString opid, QString errStr) { |
|||
mainwindow->ui->statusBar->showMessage(QObject::tr(" Tx ") % opid % QObject::tr(" failed"), 15 * 1000); |
|||
|
|||
if (!opid.isEmpty()) |
|||
errStr = QObject::tr("The transaction with id ") % opid % QObject::tr(" failed. The error was") + ":\n\n" + errStr; |
|||
|
|||
QMessageBox::critical(mainwindow, QObject::tr("Transaction Error"), errStr, QMessageBox::Ok); |
|||
}); |
|||
|
|||
} |
@ -1,69 +0,0 @@ |
|||
#ifndef TURNSTILE_H |
|||
#define TURNSTILE_H |
|||
|
|||
#include "precompiled.h" |
|||
|
|||
class RPC; |
|||
class MainWindow; |
|||
struct Tx; |
|||
|
|||
|
|||
struct TurnstileMigrationItem { |
|||
QString fromAddr; |
|||
QString intTAddr; |
|||
QString destAddr; |
|||
int blockNumber; |
|||
double amount; |
|||
int status; |
|||
}; |
|||
|
|||
enum TurnstileMigrationItemStatus { |
|||
NotStarted = 0, |
|||
SentToT, |
|||
SentToZS, |
|||
NotEnoughBalance, |
|||
UnknownError |
|||
}; |
|||
|
|||
struct ProgressReport { |
|||
int step; |
|||
int totalSteps; |
|||
int nextBlock; |
|||
bool hasErrors; |
|||
QString from; |
|||
QString to; |
|||
QString via; |
|||
}; |
|||
|
|||
class Turnstile |
|||
{ |
|||
public: |
|||
Turnstile(RPC* _rpc, MainWindow* mainwindow); |
|||
|
|||
void planMigration(QString zaddr, QString destAddr, int splits, int numBlocks); |
|||
QList<double> splitAmount(double amount, int parts); |
|||
void fillAmounts(QList<double>& amounts, double amount, int count); |
|||
|
|||
QList<TurnstileMigrationItem> readMigrationPlan(); |
|||
void writeMigrationPlan(QList<TurnstileMigrationItem> plan); |
|||
void removeFile(); |
|||
|
|||
void executeMigrationStep(); |
|||
ProgressReport getPlanProgress(); |
|||
bool isMigrationPresent(); |
|||
|
|||
static double minMigrationAmount; |
|||
private: |
|||
QList<int> getBlockNumbers(int start, int end, int count); |
|||
QString writeableFile(); |
|||
|
|||
void doSendTx(Tx tx, std::function<void(void)> cb); |
|||
|
|||
|
|||
QList<TurnstileMigrationItem>::Iterator getNextStep(QList<TurnstileMigrationItem>& plan); |
|||
|
|||
RPC* rpc; |
|||
MainWindow* mainwindow; |
|||
}; |
|||
|
|||
#endif |
@ -1,235 +0,0 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<ui version="4.0"> |
|||
<class>Turnstile</class> |
|||
<widget class="QDialog" name="Turnstile"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>0</x> |
|||
<y>0</y> |
|||
<width>565</width> |
|||
<height>416</height> |
|||
</rect> |
|||
</property> |
|||
<property name="windowTitle"> |
|||
<string>Turnstile Migration</string> |
|||
</property> |
|||
<layout class="QVBoxLayout" name="verticalLayout"> |
|||
<item> |
|||
<widget class="QGroupBox" name="groupBox"> |
|||
<property name="title"> |
|||
<string>Turnstile Migration</string> |
|||
</property> |
|||
<layout class="QVBoxLayout" name="verticalLayout_2"> |
|||
<item> |
|||
<layout class="QGridLayout" name="gridLayout"> |
|||
<item row="0" column="0"> |
|||
<widget class="QLabel" name="msgIcon"> |
|||
<property name="text"> |
|||
<string/> |
|||
</property> |
|||
<property name="alignment"> |
|||
<set>Qt::AlignCenter</set> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="5" column="0"> |
|||
<widget class="QLabel" name="label_2"> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred"> |
|||
<horstretch>0</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="text"> |
|||
<string>Migrate over</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="2" column="0"> |
|||
<widget class="QLabel" name="label"> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred"> |
|||
<horstretch>0</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="text"> |
|||
<string>From</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="2" column="1" colspan="2"> |
|||
<widget class="AddressCombo" name="migrateZaddList"> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed"> |
|||
<horstretch>0</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="editable"> |
|||
<bool>false</bool> |
|||
</property> |
|||
<property name="currentText"> |
|||
<string/> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="5" column="1" colspan="2"> |
|||
<widget class="QComboBox" name="privLevel"> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed"> |
|||
<horstretch>0</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="0" column="1" colspan="2"> |
|||
<widget class="QLabel" name="label_8"> |
|||
<property name="text"> |
|||
<string><html><head/><body><p>Funds from Sprout z-Addresses (which start with &quot;zc&quot;) need to be moved to the upgraded Sapling z-Addresses (which start with &quot;zs&quot;). The funds cannot be moved directly, but need to be sent through intermediate &quot;transparent&quot; addresses in privacy-preserving way.</p><p>This migration can be done automatically for you.</p></body></html></string> |
|||
</property> |
|||
<property name="wordWrap"> |
|||
<bool>true</bool> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="4" column="0"> |
|||
<widget class="QLabel" name="label_9"> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred"> |
|||
<horstretch>0</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="text"> |
|||
<string>To</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="4" column="1" colspan="2"> |
|||
<widget class="AddressCombo" name="migrateTo"/> |
|||
</item> |
|||
<item row="1" column="0" colspan="3"> |
|||
<widget class="Line" name="line"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Horizontal</enum> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="3" column="1"> |
|||
<widget class="QLabel" name="fromBalance"> |
|||
<property name="text"> |
|||
<string>Balance</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="7" column="0" rowspan="2" colspan="3"> |
|||
<spacer name="verticalSpacer"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Vertical</enum> |
|||
</property> |
|||
<property name="sizeHint" stdset="0"> |
|||
<size> |
|||
<width>20</width> |
|||
<height>40</height> |
|||
</size> |
|||
</property> |
|||
</spacer> |
|||
</item> |
|||
<item row="6" column="0"> |
|||
<widget class="QLabel" name="label_5"> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred"> |
|||
<horstretch>0</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="text"> |
|||
<string>Miner Fees</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="6" column="1" colspan="2"> |
|||
<widget class="QLabel" name="minerFee"> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred"> |
|||
<horstretch>0</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="text"> |
|||
<string notr="true">0.0004 ZEC $0.04</string> |
|||
</property> |
|||
<property name="alignment"> |
|||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="3" column="0"> |
|||
<widget class="QLabel" name="label_3"> |
|||
<property name="text"> |
|||
<string>Total Balance</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
</layout> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QDialogButtonBox" name="buttonBox"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Horizontal</enum> |
|||
</property> |
|||
<property name="standardButtons"> |
|||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
<customwidgets> |
|||
<customwidget> |
|||
<class>AddressCombo</class> |
|||
<extends>QComboBox</extends> |
|||
<header>addresscombo.h</header> |
|||
</customwidget> |
|||
</customwidgets> |
|||
<resources/> |
|||
<connections> |
|||
<connection> |
|||
<sender>buttonBox</sender> |
|||
<signal>accepted()</signal> |
|||
<receiver>Turnstile</receiver> |
|||
<slot>accept()</slot> |
|||
<hints> |
|||
<hint type="sourcelabel"> |
|||
<x>248</x> |
|||
<y>254</y> |
|||
</hint> |
|||
<hint type="destinationlabel"> |
|||
<x>157</x> |
|||
<y>274</y> |
|||
</hint> |
|||
</hints> |
|||
</connection> |
|||
<connection> |
|||
<sender>buttonBox</sender> |
|||
<signal>rejected()</signal> |
|||
<receiver>Turnstile</receiver> |
|||
<slot>reject()</slot> |
|||
<hints> |
|||
<hint type="sourcelabel"> |
|||
<x>316</x> |
|||
<y>260</y> |
|||
</hint> |
|||
<hint type="destinationlabel"> |
|||
<x>286</x> |
|||
<y>274</y> |
|||
</hint> |
|||
</hints> |
|||
</connection> |
|||
</connections> |
|||
</ui> |
@ -1,192 +0,0 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<ui version="4.0"> |
|||
<class>TurnstileProgress</class> |
|||
<widget class="QDialog" name="TurnstileProgress"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>0</x> |
|||
<y>0</y> |
|||
<width>400</width> |
|||
<height>300</height> |
|||
</rect> |
|||
</property> |
|||
<property name="windowTitle"> |
|||
<string>Turnstile Migration Progress</string> |
|||
</property> |
|||
<layout class="QGridLayout" name="gridLayout"> |
|||
<item row="0" column="0"> |
|||
<widget class="QLabel" name="label"> |
|||
<property name="text"> |
|||
<string>From</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="2" column="0"> |
|||
<widget class="QLabel" name="label_3"> |
|||
<property name="text"> |
|||
<string>To</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="1" column="0" colspan="3"> |
|||
<widget class="QLabel" name="fromAddr"> |
|||
<property name="text"> |
|||
<string notr="true">From Address</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="6" column="2"> |
|||
<widget class="QLabel" name="progressTxt"> |
|||
<property name="text"> |
|||
<string notr="true">4 / 12</string> |
|||
</property> |
|||
<property name="alignment"> |
|||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="12" column="1" colspan="2"> |
|||
<widget class="QLabel" name="label_4"> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred"> |
|||
<horstretch>0</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="text"> |
|||
<string>Please ensure you have your wallet.dat backed up!</string> |
|||
</property> |
|||
<property name="wordWrap"> |
|||
<bool>true</bool> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="9" column="0" colspan="3"> |
|||
<widget class="QLabel" name="nextTx"> |
|||
<property name="text"> |
|||
<string>Next Transaction in 4 hours</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="7" column="0" colspan="3"> |
|||
<widget class="QProgressBar" name="progressBar"> |
|||
<property name="value"> |
|||
<number>33</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="13" column="0" colspan="3"> |
|||
<widget class="Line" name="line_2"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Horizontal</enum> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="6" column="0" colspan="2"> |
|||
<widget class="QLabel" name="label_2"> |
|||
<property name="text"> |
|||
<string>Migration Progress</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="14" column="0" colspan="3"> |
|||
<widget class="QDialogButtonBox" name="buttonBox"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Horizontal</enum> |
|||
</property> |
|||
<property name="standardButtons"> |
|||
<set>QDialogButtonBox::Close|QDialogButtonBox::Discard</set> |
|||
</property> |
|||
<property name="centerButtons"> |
|||
<bool>false</bool> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="12" column="0"> |
|||
<widget class="QLabel" name="msgIcon"> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred"> |
|||
<horstretch>0</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="text"> |
|||
<string notr="true">TextLabel</string> |
|||
</property> |
|||
<property name="alignment"> |
|||
<set>Qt::AlignCenter</set> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="11" column="0" colspan="3"> |
|||
<spacer name="verticalSpacer"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Vertical</enum> |
|||
</property> |
|||
<property name="sizeHint" stdset="0"> |
|||
<size> |
|||
<width>20</width> |
|||
<height>40</height> |
|||
</size> |
|||
</property> |
|||
</spacer> |
|||
</item> |
|||
<item row="5" column="0" colspan="3"> |
|||
<widget class="Line" name="line_3"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Horizontal</enum> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="3" column="0" colspan="3"> |
|||
<widget class="QLabel" name="toAddr"> |
|||
<property name="text"> |
|||
<string notr="true">To Address</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="10" column="0" colspan="3"> |
|||
<widget class="Line" name="line"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Horizontal</enum> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
<resources/> |
|||
<connections> |
|||
<connection> |
|||
<sender>buttonBox</sender> |
|||
<signal>accepted()</signal> |
|||
<receiver>TurnstileProgress</receiver> |
|||
<slot>accept()</slot> |
|||
<hints> |
|||
<hint type="sourcelabel"> |
|||
<x>248</x> |
|||
<y>254</y> |
|||
</hint> |
|||
<hint type="destinationlabel"> |
|||
<x>157</x> |
|||
<y>274</y> |
|||
</hint> |
|||
</hints> |
|||
</connection> |
|||
<connection> |
|||
<sender>buttonBox</sender> |
|||
<signal>rejected()</signal> |
|||
<receiver>TurnstileProgress</receiver> |
|||
<slot>reject()</slot> |
|||
<hints> |
|||
<hint type="sourcelabel"> |
|||
<x>316</x> |
|||
<y>260</y> |
|||
</hint> |
|||
<hint type="destinationlabel"> |
|||
<x>286</x> |
|||
<y>274</y> |
|||
</hint> |
|||
</hints> |
|||
</connection> |
|||
</connections> |
|||
</ui> |
Loading…
Reference in new issue