diff --git a/src/connection.cpp b/src/connection.cpp index 6ef11bb..b8700e5 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -66,7 +66,11 @@ void ConnectionLoader::createZcashConf() { QDir().mkdir(fi.dir().absolutePath()); QFile file(confLocation); - file.open(QIODevice::ReadWrite | QIODevice::Truncate); + if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) { + qDebug() << "Could not create zcash.conf, returning"; + return; + } + QTextStream out(&file); out << "server=1\n"; @@ -113,6 +117,53 @@ void ConnectionLoader::createZcashConf() { // stdout: "renamed '/home/adityapk/.zcash-params/sprout-groth16.params.dl' -> '/home/adityapk/.zcash-params/sprout-groth16.params'\n" // finished with code 0 } + + { + startEmbeddedZcashd(); + + auto config = autoDetectZcashConf(); + if (config.get() != nullptr) { + auto connection = makeConnection(config); + refreshZcashdState(connection); + + return; + } else { + qDebug() << "Coulnd't get embedded startup zcashd"; + } + } +} + +bool ConnectionLoader::startEmbeddedZcashd() { + static bool erroredOut = false; + + if (erroredOut) { + qDebug() << "Last zcashd start attempted errored, so not restarting"; + return false; + } + + // Finally, start zcashd + qDebug() << "Starting zcashd"; + QFileInfo fi(Settings::getInstance()->getExecName()); + auto zcashdProgram = fi.dir().filePath("zcashd"); + + QProcess* p = new QProcess(main); + QObject::connect(p, &QProcess::started, [=] () { + Settings::getInstance()->setEmbeddedZcashdRunning(true); + }); + + QObject::connect(p, QOverload::of(&QProcess::finished), + [=](int exitCode, QProcess::ExitStatus exitStatus) { + qDebug() << "zcashd finished with code " << exitCode; + p->deleteLater(); + }); + + QObject::connect(p, &QProcess::errorOccurred, [&] (auto error) mutable { + qDebug() << "Couldn't start zcashd: " << error; + erroredOut = true; + }); + + p->start(zcashdProgram); + return true; } void ConnectionLoader::doManualConnect() { @@ -178,15 +229,26 @@ void ConnectionLoader::refreshZcashdState(Connection* connection) { // Failed, see what it is. //qDebug() << err << ":" << QString::fromStdString(res.dump()); - if (err == QNetworkReply::NetworkError::ConnectionRefusedError) { - auto isZcashConfFound = connection->config.get()->usingZcashConf; - QString explanation = QString() - % (isZcashConfFound ? "A zcash.conf file was found, but a" : "A") - % " connection to zcashd could not be established.\n\n" - % "If you are connecting to a remote/non-standard node " - % "please set the host/port and user/password in the File->Settings menu"; - - this->showError(explanation); + if (err == QNetworkReply::NetworkError::ConnectionRefusedError) { + // Start embedded zcasd + this->showInformation("Starting Embedded zcashd"); + if (this->startEmbeddedZcashd()) { + // Refresh after one second + QTimer::singleShot(1000, [=]() { this->refreshZcashdState(connection); }); + } else { + // Errored out, show error and exit + QString explanation = "Couldn't start zcashd"; + this->showError(explanation); + } + + // auto isZcashConfFound = connection->config.get()->usingZcashConf; + // QString explanation = QString() + // % (isZcashConfFound ? "A zcash.conf file was found, but a" : "A") + // % " connection to zcashd could not be established.\n\n" + // % "If you are connecting to a remote/non-standard node " + // % "please set the host/port and user/password in the File->Settings menu"; + + // this->showError(explanation); } else if (err == QNetworkReply::NetworkError::AuthenticationRequiredError) { QString explanation = QString() % "Authentication failed. The username / password you specified was " @@ -196,10 +258,7 @@ void ConnectionLoader::refreshZcashdState(Connection* connection) { } else if (err == QNetworkReply::NetworkError::InternalServerError && !res.is_discarded()) { // The server is loading, so just poll until it succeeds QString status = QString::fromStdString(res["error"]["message"]); - - QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation); - connD->icon->setPixmap(icon.pixmap(128, 128)); - connD->status->setText("Your zcashd is starting up. Please wait.\n\n" % status); + showInformation("Your zcashd is starting up. Please wait.\n\n" % status); // Refresh after one second QTimer::singleShot(1000, [=]() { this->refreshZcashdState(connection); }); @@ -208,6 +267,12 @@ void ConnectionLoader::refreshZcashdState(Connection* connection) { ); } +void ConnectionLoader::showInformation(QString info) { + QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation); + connD->icon->setPixmap(icon.pixmap(128, 128)); + connD->status->setText(info); +} + void ConnectionLoader::showError(QString explanation) { QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical); connD->icon->setPixmap(icon.pixmap(128, 128)); diff --git a/src/connection.h b/src/connection.h index 2b3c215..21f3f71 100644 --- a/src/connection.h +++ b/src/connection.h @@ -48,10 +48,13 @@ private: QString locateZcashConfFile(); QString zcashConfWritableLocation(); + bool startEmbeddedZcashd(); + void refreshZcashdState(Connection* connection); int getProgressFromStatus(QString status); void showError(QString explanation); + void showInformation(QString info); void doRPCSetConnection(Connection* conn); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index dd7ad6f..7adfe59 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -88,6 +88,10 @@ void MainWindow::closeEvent(QCloseEvent* event) { s.setValue("baltablegeometry", ui->balancesTable->horizontalHeader()->saveState()); s.setValue("tratablegeometry", ui->transactionsTable->horizontalHeader()->saveState()); + // Let the RPC know to shutdown any running service. + rpc->closeEvent(); + + // Bubble up QMainWindow::closeEvent(event); } diff --git a/src/rpc.cpp b/src/rpc.cpp index f3dc59c..bc107c6 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -807,3 +807,19 @@ void RPC::refreshZECPrice() { Settings::getInstance()->setZECPrice(0); }); } + +void RPC::shutdownZcashd() { + json payload = { + {"jsonrpc", "1.0"}, + {"id", "someid"}, + {"method", "stop"} + }; + + conn->doRPCWithDefaultErrorHandling(payload, [=](auto) {}); +} + +void RPC::closeEvent() { + if (Settings::getInstance()->isEmbeddedZcashdRunning()) { + shutdownZcashd(); + } +} \ No newline at end of file diff --git a/src/rpc.h b/src/rpc.h index 6ec7df7..d8dde07 100644 --- a/src/rpc.h +++ b/src/rpc.h @@ -56,11 +56,15 @@ public: void importZPrivKey(QString addr, bool rescan, const std::function& cb); void importTPrivKey(QString addr, bool rescan, const std::function& cb); + void shutdownZcashd(); + void getAllPrivKeys(const std::function>)>); Turnstile* getTurnstile() { return turnstile; } Connection* getConnection() { return conn; } + void closeEvent(); + private: void noConnection(); diff --git a/src/settings.h b/src/settings.h index ecb980a..de7ebb8 100644 --- a/src/settings.h +++ b/src/settings.h @@ -35,6 +35,9 @@ public: QString getExecName() { return _executable; } void setExecName(QString name) { _executable = name; } + void setEmbeddedZcashdRunning(bool r) { _isEmbeddedZcashd = r; } + bool isEmbeddedZcashdRunning() { return _isEmbeddedZcashd; } + int getBlockNumber(); void setBlockNumber(int number); @@ -63,10 +66,11 @@ private: QString _confLocation; QString _executable; - bool _isTestnet = false; - bool _isSyncing = false; - int _blockNumber = 0; - bool _manualConn = false; + bool _isTestnet = false; + bool _isSyncing = false; + int _blockNumber = 0; + bool _manualConn = false; + bool _isEmbeddedZcashd = false; double zecPrice = 0.0; };