Browse Source

Merge pull request 'Allow custom rescan height on privkey import' (#145) from import into dev

Reviewed-on: #145
pull/147/head
duke 4 months ago
parent
commit
203c23dad2
  1. 72
      src/mainwindow.cpp
  2. 34
      src/privkey.ui
  3. 4
      src/rpc.cpp
  4. 31
      src/settings.cpp
  5. 2
      src/settings.h

72
src/mainwindow.cpp

@ -1132,21 +1132,22 @@ void MainWindow::doImport(QList<QString>* keys) {
return;
}
DEBUG(" keys.size= " << keys->size() );
if (keys->isEmpty()) {
delete keys;
ui->statusBar->showMessage(tr("Private key import rescan finished"));
return;
}
// Pop the first key
QString key = keys->first();
keys->pop_front();
bool rescan = keys->isEmpty();
// Get the first key
QString key = keys->takeFirst();
if (key.startsWith("SK") ||
key.startsWith("secret")) { // Z key
bool rescan = false;
if (Settings::getInstance()->isValidSaplingPrivateKey(key) ) {
DEBUG("importing zaddr privkey with rescan=" << rescan);
rpc->importZPrivKey(key, rescan, [=] (auto) { this->doImport(keys); });
} else {
DEBUG("importing taddr privkey with rescan=" << rescan);
rpc->importTPrivKey(key, rescan, [=] (auto) { this->doImport(keys); });
}
}
@ -1247,7 +1248,6 @@ void MainWindow::payHushURI(QString uri, QString myAddr) {
}
}
void MainWindow::importPrivKey() {
QDialog d(this);
Ui_PrivKey pui;
@ -1259,20 +1259,27 @@ void MainWindow::importPrivKey() {
tr("Please paste your private keys here, one per line") % ".\n" %
tr("The keys will be imported into your connected Hush node"));
// if rescan is not checked, disable the rescan height input
QObject::connect(pui.chkrescan, &QCheckBox::stateChanged, [=](auto checked) {
pui.rescanfrom->setEnabled(checked);
});
if (d.exec() == QDialog::Accepted && !pui.privKeyTxt->toPlainText().trimmed().isEmpty()) {
auto rawkeys = pui.privKeyTxt->toPlainText().trimmed().split("\n");
QList<QString> keysTmp;
// Filter out all the empty keys.
// Filter out all the empty keys and comment lines
std::copy_if(rawkeys.begin(), rawkeys.end(), std::back_inserter(keysTmp), [=] (auto key) {
return !key.startsWith("#") && !key.trimmed().isEmpty();
});
auto keys = new QList<QString>();
// ignore anything after the first space of a line, such as if you paste a line from z_exportwallet output
std::transform(keysTmp.begin(), keysTmp.end(), std::back_inserter(*keys), [=](auto key) {
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
@ -1283,13 +1290,52 @@ void MainWindow::importPrivKey() {
delete multiline;
}
// Start the import. The function takes ownership of keys
QTimer::singleShot(1, [=]() {doImport(keys);});
// Finally, validate all keys, removing any which are invalid
auto keysValidated = new QList<QString>();
auto settings = Settings::getInstance();
std::copy_if(keys->begin(), keys->end(), std::back_inserter(*keysValidated), [=] (auto key) {
bool isValid = settings->isValidSaplingPrivateKey(key) || settings->isValidTransparentPrivateKey(key);
if (!isValid) { DEBUG("privkey " << key << " is not valid"); }
return isValid;
});
DEBUG("found " << keysValidated->size() << " valid privkeys");
bool rescan = pui.chkrescan->isChecked();
// avoid giving invalid data to RPCs and a rescan if there were no valid privkeys
if(keysValidated->size() == 0) {
QMessageBox::information(this, "No valid keys",
tr("No valid private keys were found, please make sure you copy and pasted correctly"),
QMessageBox::Ok);
return;
}
// Start the import. The function takes ownership of keysValidated
QTimer::singleShot(1, [=]() {
// we import all keys without rescanning and then finally decide if we will rescan once
doImport(keysValidated);
if (rescan) {
//TODO: verify rescanfrom is a valid integer
rpc->rescan(pui.rescanfrom->text().trimmed().toInt() , [=] (QJsonValue response){
qDebug() << __func__ << ":rescanning from height " << pui.rescanfrom->text().toInt() << " finished" << response;
ui->statusBar->showMessage(tr("Rescanning finished"), 5000);
});
}
});
// Show the dialog that keys will be imported.
QMessageBox::information(this,
"Imported", tr("The keys were imported! It may take several minutes to rescan the blockchain. Until then, functionality may be limited"),
if(rescan) {
QMessageBox::information(this, "Imported",
tr("The keys were imported! It may take several hours to rescan the blockchain. Until then, functionality may be limited"),
QMessageBox::Ok);
} else {
QMessageBox::information(this, "Imported",
tr("The keys were imported! You chose to not rescan, so funds in that address will not show up in your wallet yet."),
QMessageBox::Ok);
}
}
}

34
src/privkey.ui

@ -14,7 +14,7 @@
<string>Private Keys</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<item row="1" column="0" colspan="3">
<widget class="QPlainTextEdit" name="privKeyTxt">
<property name="plainText">
<string/>
@ -22,6 +22,38 @@
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="chkrescan">
<property name="text">
<string>Rescan</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="rescanlabel">
<property name="text">
<string>Rescan Height</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLineEdit" name="rescanfrom">
<property name="text">
<string notr="true">1</string>
</property>
<property name="minimumSize">
<size>
<width>50</width>
</size>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>

4
src/rpc.cpp

@ -374,7 +374,7 @@ void RPC::importTPrivKey(QString privkey, bool rescan, const std::function<void(
{"jsonrpc", "1.0"},
{"id", "42"},
{"method", "importprivkey"},
{"params", QJsonArray { privkey, "", "false", "0", "128" }},
{"params", QJsonArray { privkey, "", rescan , "0", "128" }},
};
} else {
qDebug() << "Detected new-style HUSH WIF";
@ -382,7 +382,7 @@ void RPC::importTPrivKey(QString privkey, bool rescan, const std::function<void(
{"jsonrpc", "1.0"},
{"id", "42"},
{"method", "importprivkey"},
{"params", QJsonArray { privkey, (rescan? "yes" : "no") }},
{"params", QJsonArray { privkey, "", rescan }},
};
}

31
src/settings.cpp

@ -134,13 +134,6 @@ bool Settings::isSaplingAddress(QString addr) {
(!isTestnet() && addr.startsWith("zs1"));
}
bool Settings::isSproutAddress(QString addr) {
if (!isValidAddress(addr))
return false;
return isZAddress(addr) && !isSaplingAddress(addr);
}
bool Settings::isZAddress(QString addr) {
if (!isValidAddress(addr))
return false;
@ -435,6 +428,30 @@ double Settings::getMinerFee() {
return 0.0001;
}
bool Settings::isValidTransparentPrivateKey(QString pk) {
if (pk.length() > 52) {
DEBUG("privkey invalid, too long");
return false;
}
if (pk.length() < 51) {
DEBUG("privkey invalid, too short");
return false;
}
// TODO: can a taddr privkey start with anything else?
if (pk.startsWith("U") || pk.startsWith("5") || pk.startsWith("L") || pk.startsWith("K") || pk.startsWith("7")) {
// verify only contains base58 characters
QRegExp exp("^[U5LK7][123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{51,52}$", Qt::CaseSensitive);
bool valid = exp.exactMatch(pk);
if(!valid) { DEBUG("privkey invalid, not base58"); }
return valid;
} else {
DEBUG("privkey invalid, wrong prefix");
return false;
}
}
bool Settings::isValidSaplingPrivateKey(QString pk) {
if (isTestnet()) {
QRegExp zspkey("^secret-extended-key-test[0-9a-z]{278}$", Qt::CaseInsensitive);

2
src/settings.h

@ -47,9 +47,9 @@ public:
void setTestnet(bool isTestnet);
bool isSaplingAddress(QString addr);
bool isSproutAddress(QString addr);
bool isValidSaplingPrivateKey(QString pk);
bool isValidTransparentPrivateKey(QString pk);
bool isSyncing();
void setSyncing(bool syncing);

Loading…
Cancel
Save