Browse Source

Merge branch 'master' of github.com:zcashfoundation/zecwallet

import_zecw
Aditya Kulkarni 5 years ago
parent
commit
12f9c7b8b5
  1. 65
      src/mainwindow.cpp
  2. 8
      src/mainwindow.ui
  3. 28
      src/rpc.cpp
  4. 2
      src/rpc.h
  5. 14
      src/sendtab.cpp
  6. 22
      src/settings.cpp
  7. 4
      src/settings.h
  8. 26
      src/txtablemodel.cpp
  9. 9
      src/txtablemodel.h

65
src/mainwindow.cpp

@ -82,8 +82,9 @@ MainWindow::MainWindow(QWidget *parent) :
// Export transactions
QObject::connect(ui->actionExport_transactions, &QAction::triggered, this, &MainWindow::exportTransactions);
// Z-board seems to have been abandoned
// z-Board.net
QObject::connect(ui->actionz_board_net, &QAction::triggered, this, &MainWindow::postToZBoard);
// QObject::connect(ui->actionz_board_net, &QAction::triggered, this, &MainWindow::postToZBoard);
// Validate Address
QObject::connect(ui->actionValidate_Address, &QAction::triggered, this, &MainWindow::validateAddress);
@ -177,6 +178,11 @@ void MainWindow::restoreSavedStates() {
ui->balancesTable->horizontalHeader()->restoreState(s.value("baltablegeometry").toByteArray());
ui->transactionsTable->horizontalHeader()->restoreState(s.value("tratablegeometry").toByteArray());
// Explicitly set the tx table resize headers, since some previous values may have made them
// non-expandable.
ui->transactionsTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Interactive);
ui->transactionsTable->horizontalHeader()->setSectionResizeMode(4, QHeaderView::Interactive);
}
void MainWindow::doClose() {
@ -637,8 +643,7 @@ void MainWindow::donate() {
// Set up a donation to me :)
clearSendForm();
ui->Address1->setText(Settings::getDonationAddr(
Settings::getInstance()->isSaplingAddress(ui->inputsCombo->currentText())));
ui->Address1->setText(Settings::getDonationAddr());
ui->Address1->setCursorPosition(0);
ui->Amount1->setText("0.01");
ui->MemoTxt1->setText(tr("Thanks for supporting ZecWallet!"));
@ -710,12 +715,12 @@ void MainWindow::postToZBoard() {
QMap<QString, QString> topics;
// Insert the main topic automatically
topics.insert("#Main_Area", Settings::getInstance()->isTestnet() ? Settings::getDonationAddr(true) : Settings::getZboardAddr());
topics.insert("#Main_Area", Settings::getInstance()->isTestnet() ? Settings::getDonationAddr() : Settings::getZboardAddr());
zb.topicsList->addItem(topics.firstKey());
// Then call the API to get topics, and if it returns successfully, then add the rest of the topics
rpc->getZboardTopics([&](QMap<QString, QString> topicsMap) {
for (auto t : topicsMap.keys()) {
topics.insert(t, Settings::getInstance()->isTestnet() ? Settings::getDonationAddr(true) : topicsMap[t]);
topics.insert(t, Settings::getInstance()->isTestnet() ? Settings::getDonationAddr() : topicsMap[t]);
zb.topicsList->addItem(t);
}
});
@ -791,20 +796,7 @@ void MainWindow::postToZBoard() {
tx.fee = Settings::getMinerFee();
// And send the Tx
rpc->executeTransaction(tx, [=] (QString opid) {
ui->statusBar->showMessage(tr("Computing Tx: ") % opid);
},
[=] (QString /*opid*/, QString txid) {
ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);
},
[=] (QString opid, QString errStr) {
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(this, QObject::tr("Transaction Error"), errStr, QMessageBox::Ok);
});
rpc->executeStandardUITransaction(tx);
}
}
@ -825,7 +817,7 @@ void MainWindow::doImport(QList<QString>* keys) {
keys->pop_front();
bool rescan = keys->isEmpty();
if (key.startsWith("S") ||
if (key.startsWith("SK") ||
key.startsWith("secret")) { // Z key
rpc->importZPrivKey(key, rescan, [=] (auto) { this->doImport(keys); });
} else {
@ -949,6 +941,16 @@ void MainWindow::importPrivKey() {
return key.trimmed().split(" ")[0];
});
// Special case.
// Sometimes, when importing from a paperwallet or such, the key is split by newlines, and might have
// been pasted like that. So check to see if the whole thing is one big private key
if (Settings::getInstance()->isValidSaplingPrivateKey(keys->join(""))) {
auto multiline = keys;
keys = new QList<QString>();
keys->append(multiline->join(""));
delete multiline;
}
// Start the import. The function takes ownership of keys
QTimer::singleShot(1, [=]() {doImport(keys);});
@ -1304,6 +1306,9 @@ std::function<void(bool)> MainWindow::addZAddrsToComboList(bool sapling) {
return [=] (bool checked) {
if (checked && this->rpc->getAllZAddresses() != nullptr) {
auto addrs = this->rpc->getAllZAddresses();
// Save the current address, so we can update it later
auto zaddr = ui->listReceiveAddresses->currentText();
ui->listReceiveAddresses->clear();
std::for_each(addrs->begin(), addrs->end(), [=] (auto addr) {
@ -1315,6 +1320,10 @@ std::function<void(bool)> MainWindow::addZAddrsToComboList(bool sapling) {
}
}
});
if (!zaddr.isEmpty() && Settings::isZAddress(zaddr)) {
ui->listReceiveAddresses->setCurrentText(zaddr);
}
// If z-addrs are empty, then create a new one.
if (addrs->isEmpty()) {
@ -1508,6 +1517,10 @@ void MainWindow::setupReceiveTab() {
void MainWindow::updateTAddrCombo(bool checked) {
if (checked) {
auto utxos = this->rpc->getUTXOs();
// Save the current address so we can restore it later
auto currentTaddr = ui->listReceiveAddresses->currentText();
ui->listReceiveAddresses->clear();
// Maintain a set of addresses so we don't duplicate any, because we'll be adding
@ -1550,7 +1563,17 @@ void MainWindow::updateTAddrCombo(bool checked) {
}
}
// 4. Add a last, disabled item if there are remaining items
// 4. Add the previously selected t-address
if (!currentTaddr.isEmpty() && Settings::isTAddress(currentTaddr)) {
// Make sure the current taddr is in the list
if (!addrs.contains(currentTaddr)) {
auto bal = rpc->getAllBalances()->value(currentTaddr);
ui->listReceiveAddresses->addItem(currentTaddr, bal);
}
ui->listReceiveAddresses->setCurrentText(currentTaddr);
}
// 5. Add a last, disabled item if there are remaining items
if (allTaddrs->size() > addrs.size()) {
auto num = QString::number(allTaddrs->size() - addrs.size());
ui->listReceiveAddresses->addItem("-- " + num + " more --", 0);

8
src/mainwindow.ui

@ -1149,14 +1149,6 @@
<string>&amp;Export all private keys</string>
</property>
</action>
<action name="actionz_board_net">
<property name="text">
<string>&amp;z-board.net</string>
</property>
<property name="shortcut">
<string>Ctrl+A, Ctrl+Z</string>
</property>
</action>
<action name="action_Address_Book">
<property name="text">
<string>Address &amp;book</string>

28
src/rpc.cpp

@ -27,8 +27,7 @@ RPC::RPC(MainWindow* main) {
// Setup transactions table model
transactionsTableModel = new TxTableModel(ui->transactionsTable);
main->ui->transactionsTable->setModel(transactionsTableModel);
main->ui->transactionsTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch);
// Set up timer to refresh Price
priceTimer = new QTimer(main);
QObject::connect(priceTimer, &QTimer::timeout, [=]() {
@ -220,7 +219,7 @@ void RPC::importTPrivKey(QString addr, bool rescan, const std::function<void(jso
}
void RPC::validateAddress(QString address, const std::function<void(json)>& cb) {
QString method = address.startsWith("z") ? "z_validateaddress" : "validateaddress";
QString method = Settings::isZAddress(address) ? "z_validateaddress" : "validateaddress";
json payload = {
{"jsonrpc", "1.0"},
@ -963,6 +962,29 @@ void RPC::addNewTxToWatch(const QString& newOpid, WatchedTx wtx) {
watchTxStatus();
}
/**
* Execute a transaction with the standard UI. i.e., standard status bar message and standard error
* handling
*/
void RPC::executeStandardUITransaction(Tx tx) {
executeTransaction(tx,
[=] (QString opid) {
ui->statusBar->showMessage(QObject::tr("Computing Tx: ") % opid);
},
[=] (QString, QString txid) {
ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid);
},
[=] (QString opid, QString errStr) {
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(main, QObject::tr("Transaction Error"), errStr, QMessageBox::Ok);
}
);
}
// Execute a transaction!
void RPC::executeTransaction(Tx tx,

2
src/rpc.h

@ -58,6 +58,8 @@ public:
void refreshZECPrice();
void getZboardTopics(std::function<void(QMap<QString, QString>)> cb);
void executeStandardUITransaction(Tx tx);
void executeTransaction(Tx tx,
const std::function<void(QString opid)> submitted,
const std::function<void(QString opid, QString txid)> computed,

14
src/sendtab.cpp

@ -512,7 +512,12 @@ Tx MainWindow::createTxFromSendPage() {
// If address is sprout, then we can't send change to sapling, because of turnstile.
sendChangeToSapling = sendChangeToSapling && !Settings::getInstance()->isSproutAddress(addr);
double amt = ui->sendToWidgets->findChild<QLineEdit*>(QString("Amount") % QString::number(i+1))->text().trimmed().toDouble();
QString amtStr = ui->sendToWidgets->findChild<QLineEdit*>(QString("Amount") % QString::number(i+1))->text().trimmed();
if (amtStr.isEmpty()) {
amtStr = "-1";; // The user didn't specify an amount
}
double amt = amtStr.toDouble();
totalAmt += amt;
QString memo = ui->sendToWidgets->findChild<QLabel*>(QString("MemoTxt") % QString::number(i+1))->text().trimmed();
@ -521,8 +526,7 @@ Tx MainWindow::createTxFromSendPage() {
if (Settings::getInstance()->getAllowCustomFees()) {
tx.fee = ui->minerFeeAmt->text().toDouble();
}
else {
} else {
tx.fee = Settings::getMinerFee();
}
@ -731,6 +735,8 @@ bool MainWindow::confirmTx(Tx tx, RecurringPaymentInfo* rpi) {
// Send button clicked
void MainWindow::sendButton() {
// Create a Tx from the values on the send tab. Note that this Tx object
// might not be valid yet.
Tx tx = createTxFromSendPage();
QString error = doSendTxValidations(tx);
@ -810,7 +816,7 @@ QString MainWindow::doSendTxValidations(Tx tx) {
// This technically shouldn't be possible, but issue #62 seems to have discovered a bug
// somewhere, so just add a check to make sure.
if (toAddr.amount < 0) {
return QString(tr("Amount '%1' is invalid!").arg(toAddr.amount));
return QString(tr("Amount for address '%1' is invalid!").arg(toAddr.addr));
}
}

22
src/settings.cpp

@ -178,6 +178,7 @@ void Settings::saveRestore(QDialog* d) {
void Settings::saveRestoreTableHeader(QTableView* table, QDialog* d, QString tablename) {
table->horizontalHeader()->restoreState(QSettings().value(tablename).toByteArray());
table->horizontalHeader()->setStretchLastSection(true);
QObject::connect(d, &QDialog::finished, [=](auto) {
QSettings().setValue(tablename, table->horizontalHeader()->saveState());
@ -250,17 +251,12 @@ QString Settings::getTokenName() {
}
}
QString Settings::getDonationAddr(bool sapling) {
QString Settings::getDonationAddr() {
if (Settings::getInstance()->isTestnet())
if (sapling)
return "ztestsapling1wn6889vznyu42wzmkakl2effhllhpe4azhu696edg2x6me4kfsnmqwpglaxzs7tmqsq7kudemp5";
else
return "ztn6fYKBii4Fp4vbGhkPgrtLU4XjXp4ZBMZgShtopmDGbn1L2JLTYbBp2b7SSkNr9F3rQeNZ9idmoR7s4JCVUZ7iiM5byhF";
else
if (sapling)
return "zs1gv64eu0v2wx7raxqxlmj354y9ycznwaau9kduljzczxztvs4qcl00kn2sjxtejvrxnkucw5xx9u";
else
return "zcEgrceTwvoiFdEvPWcsJHAMrpLsprMF6aRJiQa3fan5ZphyXLPuHghnEPrEPRoEVzUy65GnMVyCTRdkT6BYBepnXh6NBYs";
}
bool Settings::addToZcashConf(QString confLocation, QString line) {
@ -320,13 +316,23 @@ double Settings::getZboardAmount() {
QString Settings::getZboardAddr() {
if (Settings::getInstance()->isTestnet()) {
return getDonationAddr(true);
return getDonationAddr();
}
else {
return "zs10m00rvkhfm4f7n23e4sxsx275r7ptnggx39ygl0vy46j9mdll5c97gl6dxgpk0njuptg2mn9w5s";
}
}
bool Settings::isValidSaplingPrivateKey(QString pk) {
if (isTestnet()) {
QRegExp zspkey("^secret-extended-key-test[0-9a-z]{278}$", Qt::CaseInsensitive);
return zspkey.exactMatch(pk);
} else {
QRegExp zspkey("^secret-extended-key-main[0-9a-z]{278}$", Qt::CaseInsensitive);
return zspkey.exactMatch(pk);
}
}
bool Settings::isValidAddress(QString addr) {
QRegExp zcexp("^z[a-z0-9]{94}$", Qt::CaseInsensitive);
QRegExp zsexp("^z[a-z0-9]{77}$", Qt::CaseInsensitive);

4
src/settings.h

@ -37,6 +37,8 @@ public:
bool isSaplingAddress(QString addr);
bool isSproutAddress(QString addr);
bool isValidSaplingPrivateKey(QString pk);
bool isSyncing();
void setSyncing(bool syncing);
@ -101,7 +103,7 @@ public:
static QString getZECUSDDisplayFormat(double bal);
static QString getTokenName();
static QString getDonationAddr(bool sapling);
static QString getDonationAddr();
static double getMinerFee();
static double getZboardAmount();

26
src/txtablemodel.cpp

@ -104,9 +104,9 @@ void TxTableModel::updateAllData() {
QVariant TxTableModel::data(const QModelIndex &index, int role) const
{
// Align column 4,5 (confirmations, amount) right
// Align numeric columns (confirmations, amount) right
if (role == Qt::TextAlignmentRole &&
(index.column() == 3 || index.column() == 4))
(index.column() == Column::Confirmations || index.column() == Column::Amount))
return QVariant(Qt::AlignRight | Qt::AlignVCenter);
auto dat = modeldata->at(index.row());
@ -125,23 +125,23 @@ void TxTableModel::updateAllData() {
if (role == Qt::DisplayRole) {
switch (index.column()) {
case 0: return dat.type;
case 1: {
case Column::Type: return dat.type;
case Column::Address: {
auto addr = dat.address;
if (addr.trimmed().isEmpty())
return "(Shielded)";
else
return addr;
}
case 2: return QDateTime::fromMSecsSinceEpoch(dat.datetime * (qint64)1000).toLocalTime().toString();
case 3: return QString::number(dat.confirmations);
case 4: return Settings::getZECDisplayFormat(dat.amount);
case Column::Time: return QDateTime::fromMSecsSinceEpoch(dat.datetime * (qint64)1000).toLocalTime().toString();
case Column::Confirmations: return QString::number(dat.confirmations);
case Column::Amount: return Settings::getZECDisplayFormat(dat.amount);
}
}
if (role == Qt::ToolTipRole) {
switch (index.column()) {
case 0: {
case Column::Type: {
if (dat.memo.startsWith("zcash:")) {
return Settings::paymentURIPretty(Settings::parseURI(dat.memo));
} else {
@ -149,16 +149,16 @@ void TxTableModel::updateAllData() {
(dat.memo.isEmpty() ? "" : " tx memo: \"" + dat.memo + "\"");
}
}
case 1: {
case Column::Address: {
auto addr = modeldata->at(index.row()).address;
if (addr.trimmed().isEmpty())
return "(Shielded)";
else
return addr;
}
case 2: return QDateTime::fromMSecsSinceEpoch(modeldata->at(index.row()).datetime * (qint64)1000).toLocalTime().toString();
case 3: return QString("%1 Network Confirmations").arg(QString::number(dat.confirmations));
case 4: return Settings::getInstance()->getUSDFromZecAmount(modeldata->at(index.row()).amount);
case Column::Time: return QDateTime::fromMSecsSinceEpoch(modeldata->at(index.row()).datetime * (qint64)1000).toLocalTime().toString();
case Column::Confirmations: return QString("%1 Network Confirmations").arg(QString::number(dat.confirmations));
case Column::Amount: return Settings::getInstance()->getUSDFromZecAmount(modeldata->at(index.row()).amount);
}
}
@ -187,7 +187,7 @@ void TxTableModel::updateAllData() {
QVariant TxTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::TextAlignmentRole && (section == 3 || section == 4))
if (role == Qt::TextAlignmentRole && (section == Column::Confirmations || section == Column::Amount))
return QVariant(Qt::AlignRight | Qt::AlignVCenter);
if (role == Qt::FontRole) {

9
src/txtablemodel.h

@ -11,6 +11,15 @@ public:
TxTableModel(QObject* parent);
~TxTableModel();
enum Column
{
Type = 0,
Address = 1,
Time = 2,
Confirmations = 3,
Amount = 4
};
void addTData (const QList<TransactionItem>& data);
void addZSentData(const QList<TransactionItem>& data);
void addZRecvData(const QList<TransactionItem>& data);

Loading…
Cancel
Save